13 #include <mx/ioutils/fileUtils.hpp>
15 #include "../../libMagAOX/libMagAOX.hpp"
39 class logdump :
public mx::app::application
48 bool m_jsonMode {
false};
50 unsigned long m_pauseTime {250};
51 int m_fileCheckInterval {20};
57 bool m_follow {
false};
63 void printLogBuff(
const logPrioT & lvl,
68 void printLogJson(
const msgLenT & len,
73 virtual void setupConfig();
75 virtual void loadConfig();
77 virtual int execute();
79 virtual int gettimes(std::vector<std::string> & logs);
85 config.add(
"pauseTime",
"p",
"pauseTime" , argType::Required,
"",
"pauseTime",
false,
"int",
"When following, time in milliseconds to pause before checking for new entries.");
86 config.add(
"fileCheckInterval",
"",
"fileCheckInterval" , argType::Required,
"",
"fileCheckInterval",
false,
"int",
"When following, number of pause intervals between checks for new files.");
88 config.add(
"dir",
"d",
"dir" , argType::Required,
"",
"dir",
false,
"string",
"Directory to search for logs. MagAO-X default is normally used.");
89 config.add(
"ext",
"e",
"ext" , argType::Required,
"",
"ext",
false,
"string",
"The file extension of log files. MagAO-X default is normally used.");
90 config.add(
"nfiles",
"n",
"nfiles" , argType::Required,
"",
"nfiles",
false,
"int",
"Number of log files to dump. If 0, then all matching files dumped. Default: 0, 1 if following.");
91 config.add(
"follow",
"f",
"follow" , argType::True,
"",
"follow",
false,
"bool",
"Follow the log, printing new entries as they appear.");
92 config.add(
"level",
"L",
"level" , argType::Required,
"",
"level",
false,
"int/string",
"Minimum log level to dump, either an integer or a string. -1/TELEMETRY [the default], 0/DEFAULT, 1/D1/DBG1/DEBUG2, 2/D2/DBG2/DEBUG1,3/INFO,4/WARNING,5/ERROR,6/CRITICAL,7/FATAL. Note that only the mininum unique string is required.");
93 config.add(
"code",
"C",
"code" , argType::Required,
"",
"code",
false,
"int",
"The event code, or vector of codes, to dump. If not specified, all codes are dumped. See logCodes.hpp for a complete list of codes.");
94 config.add(
"file",
"F",
"file" , argType::Required,
"",
"file",
false,
"string",
"A single file to process. If no / are found in name it will look in the specified directory (or MagAO-X default).");
95 config.add(
"time",
"T",
"time" , argType::True,
"",
"time",
false,
"bool",
"time span mode: prints the ISO 8601 UTC timestamps of the first and last entry, the elapsed time in seconds, and the number of records in the file as a space-delimited string");
96 config.add(
"json",
"J",
"json" , argType::True,
"",
"json",
false,
"bool",
"JSON mode: emits one JSON document per line for each record in the log");
103 config(m_pauseTime,
"pauseTime");
104 config(m_fileCheckInterval,
"fileCheckInterval");
115 config(m_dir,
"dir");
119 config(m_ext,
"ext");
122 config(m_file,
"file");
124 if(m_file ==
"" && config.nonOptions.size() < 1)
126 std::cerr <<
"logdump: need application name. Try logdump -h for help.\n";
129 if(m_file ==
"" && config.nonOptions.size() > 1)
131 std::cerr <<
"logdump: only one application at a time supported. Try logdump -h for help.\n";
134 m_prefixes.resize(config.nonOptions.size());
135 for(
size_t i=0;i<config.nonOptions.size(); ++i)
137 m_prefixes[i] = config.nonOptions[i];
140 if(config.isSet(
"time")) m_time =
true;
141 if(config.isSet(
"json")) m_jsonMode =
true;
143 config(m_follow,
"follow");
145 if(m_follow) m_nfiles = 1;
146 config(m_nfiles,
"nfiles");
149 config(tmpstr,
"level");
155 config(m_codes,
"code");
163 if(m_file ==
"" && m_prefixes.size() !=1 )
return -1;
165 std::vector<std::string> logs;
169 if(m_file.find(
'/') == std::string::npos)
171 m_file = m_dir +
'/' + m_file;
173 std::cerr <<
"m_file: " << m_file <<
"\n";
175 logs.push_back(m_file);
179 logs = mx::ioutils::getFileNames( m_dir, m_prefixes[0],
"", m_ext);
185 m_nfiles = logs.size();
188 if(m_nfiles > logs.size()) m_nfiles = logs.size();
192 return gettimes(logs);
195 bool firstRun =
true;
197 for(
size_t i=logs.size() - m_nfiles; i < logs.size(); ++i)
199 std::string fname = logs[i];
202 bufferPtrT head(
new char[logHeader::maxHeadSize]);
206 fin = fopen(fname.c_str(),
"rb");
209 off_t finSize = mx::ioutils::fileSize( fileno(fin) );
223 nrd = fread( head.get(),
sizeof(
char), logHeader::minHeadSize, fin);
227 if( m_follow ==
true && i == logs.size()-1)
233 std::this_thread::sleep_for( std::chrono::duration<unsigned long, std::milli>(m_pauseTime));
235 nrd = fread( head.get(),
sizeof(
char), logHeader::minHeadSize, fin);
239 if(check >= m_fileCheckInterval)
242 size_t oldsz = logs.size();
243 logs = mx::ioutils::getFileNames( m_dir, m_prefixes[0],
"", m_ext);
244 if(logs.size() > oldsz)
264 if( logHeader::msgLen0(head) == logHeader::MAX_LEN0-1)
267 nrd = fread( head.get() + logHeader::minHeadSize,
sizeof(
char),
sizeof(
msgLen1T), fin);
269 else if( logHeader::msgLen0(head) == logHeader::MAX_LEN0)
272 nrd = fread( head.get() + logHeader::minHeadSize,
sizeof(
char),
sizeof(
msgLen2T), fin);
275 logPrioT lvl = logHeader::logLevel(head);
277 msgLenT len = logHeader::msgLen(head);
283 fseek(fin, len, SEEK_CUR);
288 if(m_codes.size() > 0)
291 for(
size_t c = 0; c< m_codes.size(); ++c)
293 if( m_codes[c] == ec )
302 fseek(fin, len, SEEK_CUR);
307 size_t hSz = logHeader::headerSize(head);
309 if( (
size_t) hSz + (
size_t) len > buffSz )
313 memcpy( logBuff.get(), head.get(), hSz);
316 nrd = fread( logBuff.get() + hSz,
sizeof(
char), len, fin);
322 if(m_follow && firstRun && finSize > 512 && totNrd < finSize-512)
328 if (!logVerify(ec, logBuff, len))
330 std::cerr <<
"Log " << fname <<
" failed verification on code=" << ec <<
" at byte=" << totNrd-len-hSz <<
". File possibly corrupt. Exiting." << std::endl;
336 printLogJson(len, logBuff);
340 printLogBuff(lvl, ec, len, logBuff);
358 static_cast<void>(len);
360 if(ec == eventCodes::GIT_STATE)
365 std::cout <<
"\n\t\t\t\t SOFTWARE RESTART\n";
376 std::cout <<
"\033[104m\033[91m\033[5m\033[1m";
418 static_cast<void>(len);
427 for(
size_t i=logs.size() - m_nfiles; i < logs.size(); ++i)
429 std::string fname = logs[i];
432 bufferPtrT head(
new char[logHeader::maxHeadSize]);
434 fin = fopen(fname.c_str(),
"rb");
451 nrd = fread( head.get(),
sizeof(
char), logHeader::minHeadSize, fin);
458 if( logHeader::msgLen0(head) == logHeader::MAX_LEN0-1)
461 nrd = fread( head.get() + logHeader::minHeadSize,
sizeof(
char),
sizeof(
msgLen1T), fin);
463 else if( logHeader::msgLen0(head) == logHeader::MAX_LEN0)
466 nrd = fread( head.get() + logHeader::minHeadSize,
sizeof(
char),
sizeof(
msgLen2T), fin);
471 msgLenT len = logHeader::msgLen(head);
472 timespecX ts0 = logHeader::timespec(head);
475 uint32_t nRecords = 1;
476 fseek(fin, len, SEEK_CUR);
485 nrd = fread( head.get(),
sizeof(
char), logHeader::minHeadSize, fin);
497 if( logHeader::msgLen0(head) == logHeader::MAX_LEN0-1)
500 nrd = fread( head.get() + logHeader::minHeadSize,
sizeof(
char),
sizeof(
msgLen1T), fin);
502 else if( logHeader::msgLen0(head) == logHeader::MAX_LEN0)
505 nrd = fread( head.get() + logHeader::minHeadSize,
sizeof(
char),
sizeof(
msgLen2T), fin);
510 len = logHeader::msgLen(head);
511 ts = logHeader::timespec(head);
514 fseek(fin, len, SEEK_CUR);
An application to dump MagAo-X binary logs to the terminal.
virtual int gettimes(std::vector< std::string > &logs)
std::vector< eventCodeT > m_codes
void printLogJson(const msgLenT &len, bufferPtrT &logBuff)
virtual void setupConfig()
virtual void loadConfig()
void printLogBuff(const logPrioT &lvl, const eventCodeT &ec, const msgLenT &len, bufferPtrT &logBuff)
std::vector< std::string > m_prefixes
When following, number of loops to wait before checking for a new file. Default is 4.
#define MAGAOX_default_logExt
The extension for MagAO-X binary log files.
#define MAGAOX_logRelPath
The relative path to the log directory.
#define MAGAOX_path
The path to the MagAO-X system files.
#define MAGAOX_env_path
Environment variable setting the MagAO-X path.
uint16_t msgLen1T
The type used for intermediate message length.
uint16_t eventCodeT
The type of an event code (16-bit unsigned int).
uint64_t msgLen2T
The type used for long message length.
msgLen2T msgLenT
The type used to refer to the message length, regardless of length.
int8_t logPrioT
The type of the log priority code.
std::shared_ptr< char > bufferPtrT
The log entry buffer smart pointer.
constexpr static logPrioT LOG_CRITICAL
The process can not continue and will shut down (fatal)
constexpr static logPrioT LOG_ERROR
An error has occured which the software will attempt to correct.
constexpr static logPrioT LOG_INFO
Informational. The info log level is the lowest level recorded during normal operations.
constexpr static logPrioT LOG_ALERT
This should only be used if some action is required by operators to keep the system safe.
constexpr static logPrioT LOG_EMERGENCY
Normal operations of the entire system should be shut down immediately.
constexpr static logPrioT LOG_WARNING
A condition has occurred which may become an error, but the process continues.
constexpr static logPrioT LOG_DEFAULT
Used to denote "use the default level for this log type".
constexpr static logPrioT LOG_NOTICE
A normal but significant condition.
logPrioT logLevelFromString(const std::string &str)
Get the log priority from a string, which might have the number or the name.
static std::string repoName(void *msgBuffer)
Access the repo name field.
A fixed-width timespec structure.
nanosecT time_ns
Nanoseconds.
secT time_s
Time since the Unix epoch.
std::string ISO8601DateTimeStrX()
Get a date-time string in ISO 8601 format for timespecX.