Skip to content

Commit

Permalink
Merge pull request #74 from sy-c/master
Browse files Browse the repository at this point in the history
v2.2.0
  • Loading branch information
sy-c authored Oct 6, 2021
2 parents ad8f7d1 + 900c6e3 commit 3b6e155
Show file tree
Hide file tree
Showing 6 changed files with 174 additions and 20 deletions.
5 changes: 5 additions & 0 deletions doc/releaseNotes.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,8 @@ This file describes the main feature changes for each InfoLogger released versio

## v2.1.1 - 28/06/2021
- minor cosmetics for auto-mute feature.

## v2.2.0 - 06/10/2021
- Added option for o2-infologger-log to collect logs from a named pipe (multiple clients possible). Pipe can be created and listened to continuously. e.g. `o2-infologger-log -f /tmp/log-pipe -c -l`.
- API:
- Added InfoLoggerContext light copy constructor, with fields overwrite. See example use in <../test/testInfoLogger.cxx>.
6 changes: 6 additions & 0 deletions include/InfoLogger/InfoLogger.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,12 @@ class InfoLoggerContext final
/// \param listOfKeyValuePairs A list of fieldName / value pairs to be set. Fields which were set automatically from environment are overwritten.
InfoLoggerContext(const std::list<std::pair<FieldName, const std::string&>>& listOfKeyValuePairs);

/// Create new context.
/// The context is created as a copy of an existing context.
/// Additionnaly, listed fields are set with provided value.
/// \param listOfKeyValuePairs A list of fieldName / value pairs to be set. Fields which were set from copy source are overwritten.
InfoLoggerContext(const InfoLoggerContext &sourceContext, const std::list<std::pair<FieldName, const std::string&>>& listOfKeyValuePairs);

/// Update context with default values
/// All fields are cleared with default values.
void reset();
Expand Down
17 changes: 17 additions & 0 deletions src/InfoLoggerContext.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,23 @@ InfoLoggerContext::InfoLoggerContext(const std::list<std::pair<FieldName, const
setField(listOfKeyValuePairs);
}

InfoLoggerContext::InfoLoggerContext(const InfoLoggerContext &sourceContext, const std::list<std::pair<FieldName, const std::string&>>& listOfKeyValuePairs)
{
// members initialization from source context
facility = sourceContext.facility;
role = sourceContext.role;
system = sourceContext.system;
detector = sourceContext.detector;
partition = sourceContext.partition;
run = sourceContext.run;
processId = sourceContext.processId;
hostName = sourceContext.hostName;
userName = sourceContext.userName;

// now set fields provided as arguments
setField(listOfKeyValuePairs);
}

InfoLoggerContext::~InfoLoggerContext()
{
}
Expand Down
37 changes: 34 additions & 3 deletions src/InfoLoggerDispatchSQL.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -265,11 +265,42 @@ int InfoLoggerDispatchSQLImpl::customLoop()
int InfoLoggerDispatchSQLImpl::customMessageProcess(std::shared_ptr<InfoLoggerMessageList> lmsg)
{
// procedure for dropped messages and keep count of them
auto returnDroppedMessage = [&](const char* message) {
auto returnDroppedMessage = [&](const char* message, infoLog_msg_t* m) {
// log bad message content (truncated)
const int maxLen = 200;

// verbose logging of the other fields
std::string logDetails;
for (int i = 0; i < nFields - 1; i++) {
logDetails += protocols[0].fields[i].name;
logDetails += "=";
if (!m->values[i].isUndefined) {
switch (protocols[0].fields[i].type) {
case infoLog_msgField_def_t::ILOG_TYPE_STRING:
if (m->values[i].value.vString != nullptr) {
std::string ss = m->values[i].value.vString;
logDetails += ss.substr(0,maxLen);
if (ss.length() > maxLen) {
logDetails += "...";
}
}
break;
case infoLog_msgField_def_t::ILOG_TYPE_INT:
logDetails += std::to_string(m->values[i].value.vInt);
break;
case infoLog_msgField_def_t::ILOG_TYPE_DOUBLE:
logDetails += std::to_string(m->values[i].value.vDouble);
break;
default:
break;
}
}
logDetails += " ";
}

int msgLen = (int)strlen(message);
theLog->error("Dropping message (%d bytes): %.*s%s", msgLen, maxLen, message, (msgLen > maxLen) ? "..." : "");
theLog->error(" %s", logDetails.c_str());
msgDroppedCount++;
return 0; // remove message from queue
};
Expand Down Expand Up @@ -347,15 +378,15 @@ int InfoLoggerDispatchSQLImpl::customMessageProcess(std::shared_ptr<InfoLoggerMe
theLog->error("mysql_stmt_bind() failed: %s", mysql_error(db));
theLog->error("message: %s", msg);
// if can not bind, message malformed, drop it
return returnDroppedMessage(msg);
return returnDroppedMessage(msg, m);
}

// Do the insertion
if (mysql_stmt_execute(stmt)) {
theLog->error("mysql_stmt_exec() failed: (%d) %s", mysql_errno(db), mysql_error(db));
// column too long
if (mysql_errno(db) == ER_DATA_TOO_LONG) {
return returnDroppedMessage(msg);
return returnDroppedMessage(msg, m);
}
// retry with new connection - usually it means server was down
disconnectDB();
Expand Down
121 changes: 104 additions & 17 deletions src/log.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>

using namespace AliceO2::InfoLogger;

Expand All @@ -34,8 +36,12 @@ void print_usage()
printf("Options: \n");
printf(" -s [severity] Possible values: Info (default), Error, Fatal, Warning, Debug.\n");
printf(" -o[key]=[value] Set a message field. Valid keys: context (Facility, Role, System, Detector, Partition, Run) and message options (Severity, Level, ErrorCode, SourceFile, SourceLine).\n");
printf(" -x If set, reads data coming on stdin line by line.\n");
printf(" -x If set, read data coming on stdin line by line.\n");
printf(" and transmit them as messages (1 line = 1 message).\n");
printf(" -f [file] Same as -x, but from a file.\n");
printf(" -c When -f selected, create a FIFO (mkfifo) to read from. It is removed after use.\n");
printf(" -l When -f selected, loop over file continuously.\n");
printf(" -v Verbose mode (file operations, etc).\n");
printf(" -h This help.\n");
}

Expand Down Expand Up @@ -145,7 +151,12 @@ pid_t findPipeProcess()
int main(int argc, char** argv)
{

int optFromStdin = 0; // 1 if logging from stdin, 0 when taking input from command line
int optFromFile = 0; // 1 if logging from a file (stdin or pipe), 0 when taking input from command line
int inputFd = -1; // file descriptor used for input, if any
std::string inputPath = ""; // path to file to read from, if any
bool createFifo = 0; // if set, create a FIFO
bool loopInput = 0; // if set, loop on file (useful for fifo mode)
bool verbose = 0; // if set, prints messages about file operations

InfoLogger::InfoLoggerMessageOption msgOptions = InfoLogger::undefinedMessageOption;
msgOptions.severity = InfoLogger::Severity::Info;
Expand All @@ -157,7 +168,7 @@ int main(int argc, char** argv)
char option;

// read options
while ((option = getopt(argc, argv, "s:xo:h")) != -1) {
while ((option = getopt(argc, argv, "s:xo:hf:clv")) != -1) {
switch (option) {

case 's': {
Expand All @@ -171,7 +182,8 @@ int main(int argc, char** argv)
} break;

case 'x': {
optFromStdin = 1;
optFromFile = 1;
inputFd = fileno(stdin);
// try to find process sending messages to stdin
pid_t pid = findPipeProcess();
if (pid != 0) {
Expand All @@ -193,6 +205,23 @@ int main(int argc, char** argv)
break;
*/

case 'c': {
createFifo = 1;
} break;

case 'l': {
loopInput = 1;
} break;

case 'v': {
verbose = 1;
} break;

case 'f': {
inputPath = optarg;
optFromFile = 1;
} break;

case 'h':
print_usage();
return 0;
Expand Down Expand Up @@ -221,25 +250,83 @@ int main(int argc, char** argv)

// todo: catch exceptions

// also read from stdin if option set */
if (optFromStdin) {
LineBuffer lb; // buffer for input lines
std::string msg;
int eof;
// also read from file if option set */
if (optFromFile) {

if (createFifo) {
bool isOk = 0;
int err = 0;
if (mkfifo(inputPath.c_str(), 0666)) {
err = errno;
if (err == EEXIST) {
// is the existing file a fifo ?
struct stat statbuf;
if (stat(inputPath.c_str(), &statbuf) == 0) {
if (S_ISFIFO(statbuf.st_mode)) {
// the FIFO already exists
isOk = 1;
}
}
}
} else {
if (verbose) printf("FIFO %s created\n", inputPath.c_str());
isOk = 1;
}
if (!isOk) {
printf("Failed to create FIFO %s : %s\n", inputPath.c_str(), strerror(err));
return -1;
}
}

// read lines from stdin until EOF, and send them as log messages
for (;;) {
eof = lb.appendFromFileDescriptor(fileno(stdin), -1);

if (inputFd < 0) {
inputFd = open(inputPath.c_str(), O_RDONLY);
if (inputFd < 0) {
printf("Failed to open %s : %s\n", inputPath.c_str(), strerror(errno));
return -1;
}
if (verbose) printf("Input file opened = %d\n",inputFd);
}

LineBuffer lb; // buffer for input lines
std::string msg;
int eof;

// read lines from stdin until EOF, and send them as log messages
for (;;) {
if (lb.getNextLine(msg)) {
// no line left
eof = lb.appendFromFileDescriptor(inputFd, -1);
for (;;) {
if (lb.getNextLine(msg)) {
// no line left
break;
}
//infoLogger_msg_xt(UNDEFINED_STRING,UNDEFINED_INT,UNDEFINED_INT,facility,severity,level,msg);
theLog->log(msgOptions, msgContext, "%s", msg.c_str());
}
if (eof)
break;
}
//infoLogger_msg_xt(UNDEFINED_STRING,UNDEFINED_INT,UNDEFINED_INT,facility,severity,level,msg);
theLog->log(msgOptions, msgContext, "%s", msg.c_str());
}
if (eof)

if (inputFd>0) {
if (verbose) printf("Input file closed\n");
close(inputFd);
inputFd = -1;
}

if (!loopInput) {
break;
}
}

}

// remove fifo after use
if (createFifo) {
if (unlink(inputPath.c_str())) {
printf("Failed to remove %s\n", inputPath.c_str());
} else {
if (verbose) printf("FIFO %s removed\n", inputPath.c_str());
}
}

Expand Down
8 changes: 8 additions & 0 deletions test/testInfoLogger.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,14 @@ int main()
theLog.log({ InfoLogger::Severity::Info, 123, 456, __FILE__, __LINE__ }, "infoLogger message test with source code info");
theLog.log(LOGINFO(123), "infoLogger message test with source code info");

// example use of context
InfoLoggerContext ctxt({{InfoLoggerContext::FieldName::Facility, std::string("test1")}});
theLog.log({{}},ctxt,"infoLogger message - facility test1");

// reuse a context and overwrite some fields
theLog.log({{}},InfoLoggerContext(ctxt,{{InfoLoggerContext::FieldName::Facility, std::string("test2")}}),"infoLogger message - facility test2");

// c++ style
theLog << "another test message " << InfoLogger::endm;
theLog << InfoLogger::Severity::Error << "another (stream error) message " << InfoLogger::endm;
theLog << InfoLogger::Severity::Warning << "another (stream warning) message " << InfoLogger::endm;
Expand Down

0 comments on commit 3b6e155

Please sign in to comment.