Skip to content

Commit 3b6e155

Browse files
authored
Merge pull request #74 from sy-c/master
v2.2.0
2 parents ad8f7d1 + 900c6e3 commit 3b6e155

File tree

6 files changed

+174
-20
lines changed

6 files changed

+174
-20
lines changed

doc/releaseNotes.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,3 +100,8 @@ This file describes the main feature changes for each InfoLogger released versio
100100

101101
## v2.1.1 - 28/06/2021
102102
- minor cosmetics for auto-mute feature.
103+
104+
## v2.2.0 - 06/10/2021
105+
- 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`.
106+
- API:
107+
- Added InfoLoggerContext light copy constructor, with fields overwrite. See example use in <../test/testInfoLogger.cxx>.

include/InfoLogger/InfoLogger.hxx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,12 @@ class InfoLoggerContext final
8080
/// \param listOfKeyValuePairs A list of fieldName / value pairs to be set. Fields which were set automatically from environment are overwritten.
8181
InfoLoggerContext(const std::list<std::pair<FieldName, const std::string&>>& listOfKeyValuePairs);
8282

83+
/// Create new context.
84+
/// The context is created as a copy of an existing context.
85+
/// Additionnaly, listed fields are set with provided value.
86+
/// \param listOfKeyValuePairs A list of fieldName / value pairs to be set. Fields which were set from copy source are overwritten.
87+
InfoLoggerContext(const InfoLoggerContext &sourceContext, const std::list<std::pair<FieldName, const std::string&>>& listOfKeyValuePairs);
88+
8389
/// Update context with default values
8490
/// All fields are cleared with default values.
8591
void reset();

src/InfoLoggerContext.cxx

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,23 @@ InfoLoggerContext::InfoLoggerContext(const std::list<std::pair<FieldName, const
3030
setField(listOfKeyValuePairs);
3131
}
3232

33+
InfoLoggerContext::InfoLoggerContext(const InfoLoggerContext &sourceContext, const std::list<std::pair<FieldName, const std::string&>>& listOfKeyValuePairs)
34+
{
35+
// members initialization from source context
36+
facility = sourceContext.facility;
37+
role = sourceContext.role;
38+
system = sourceContext.system;
39+
detector = sourceContext.detector;
40+
partition = sourceContext.partition;
41+
run = sourceContext.run;
42+
processId = sourceContext.processId;
43+
hostName = sourceContext.hostName;
44+
userName = sourceContext.userName;
45+
46+
// now set fields provided as arguments
47+
setField(listOfKeyValuePairs);
48+
}
49+
3350
InfoLoggerContext::~InfoLoggerContext()
3451
{
3552
}

src/InfoLoggerDispatchSQL.cxx

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -265,11 +265,42 @@ int InfoLoggerDispatchSQLImpl::customLoop()
265265
int InfoLoggerDispatchSQLImpl::customMessageProcess(std::shared_ptr<InfoLoggerMessageList> lmsg)
266266
{
267267
// procedure for dropped messages and keep count of them
268-
auto returnDroppedMessage = [&](const char* message) {
268+
auto returnDroppedMessage = [&](const char* message, infoLog_msg_t* m) {
269269
// log bad message content (truncated)
270270
const int maxLen = 200;
271+
272+
// verbose logging of the other fields
273+
std::string logDetails;
274+
for (int i = 0; i < nFields - 1; i++) {
275+
logDetails += protocols[0].fields[i].name;
276+
logDetails += "=";
277+
if (!m->values[i].isUndefined) {
278+
switch (protocols[0].fields[i].type) {
279+
case infoLog_msgField_def_t::ILOG_TYPE_STRING:
280+
if (m->values[i].value.vString != nullptr) {
281+
std::string ss = m->values[i].value.vString;
282+
logDetails += ss.substr(0,maxLen);
283+
if (ss.length() > maxLen) {
284+
logDetails += "...";
285+
}
286+
}
287+
break;
288+
case infoLog_msgField_def_t::ILOG_TYPE_INT:
289+
logDetails += std::to_string(m->values[i].value.vInt);
290+
break;
291+
case infoLog_msgField_def_t::ILOG_TYPE_DOUBLE:
292+
logDetails += std::to_string(m->values[i].value.vDouble);
293+
break;
294+
default:
295+
break;
296+
}
297+
}
298+
logDetails += " ";
299+
}
300+
271301
int msgLen = (int)strlen(message);
272302
theLog->error("Dropping message (%d bytes): %.*s%s", msgLen, maxLen, message, (msgLen > maxLen) ? "..." : "");
303+
theLog->error(" %s", logDetails.c_str());
273304
msgDroppedCount++;
274305
return 0; // remove message from queue
275306
};
@@ -347,15 +378,15 @@ int InfoLoggerDispatchSQLImpl::customMessageProcess(std::shared_ptr<InfoLoggerMe
347378
theLog->error("mysql_stmt_bind() failed: %s", mysql_error(db));
348379
theLog->error("message: %s", msg);
349380
// if can not bind, message malformed, drop it
350-
return returnDroppedMessage(msg);
381+
return returnDroppedMessage(msg, m);
351382
}
352383

353384
// Do the insertion
354385
if (mysql_stmt_execute(stmt)) {
355386
theLog->error("mysql_stmt_exec() failed: (%d) %s", mysql_errno(db), mysql_error(db));
356387
// column too long
357388
if (mysql_errno(db) == ER_DATA_TOO_LONG) {
358-
return returnDroppedMessage(msg);
389+
return returnDroppedMessage(msg, m);
359390
}
360391
// retry with new connection - usually it means server was down
361392
disconnectDB();

src/log.cxx

Lines changed: 104 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
#include <sys/types.h>
2525
#include <dirent.h>
2626
#include <sys/stat.h>
27+
#include <fcntl.h>
28+
#include <errno.h>
2729

2830
using namespace AliceO2::InfoLogger;
2931

@@ -34,8 +36,12 @@ void print_usage()
3436
printf("Options: \n");
3537
printf(" -s [severity] Possible values: Info (default), Error, Fatal, Warning, Debug.\n");
3638
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");
37-
printf(" -x If set, reads data coming on stdin line by line.\n");
39+
printf(" -x If set, read data coming on stdin line by line.\n");
3840
printf(" and transmit them as messages (1 line = 1 message).\n");
41+
printf(" -f [file] Same as -x, but from a file.\n");
42+
printf(" -c When -f selected, create a FIFO (mkfifo) to read from. It is removed after use.\n");
43+
printf(" -l When -f selected, loop over file continuously.\n");
44+
printf(" -v Verbose mode (file operations, etc).\n");
3945
printf(" -h This help.\n");
4046
}
4147

@@ -145,7 +151,12 @@ pid_t findPipeProcess()
145151
int main(int argc, char** argv)
146152
{
147153

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

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

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

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

173184
case 'x': {
174-
optFromStdin = 1;
185+
optFromFile = 1;
186+
inputFd = fileno(stdin);
175187
// try to find process sending messages to stdin
176188
pid_t pid = findPipeProcess();
177189
if (pid != 0) {
@@ -193,6 +205,23 @@ int main(int argc, char** argv)
193205
break;
194206
*/
195207

208+
case 'c': {
209+
createFifo = 1;
210+
} break;
211+
212+
case 'l': {
213+
loopInput = 1;
214+
} break;
215+
216+
case 'v': {
217+
verbose = 1;
218+
} break;
219+
220+
case 'f': {
221+
inputPath = optarg;
222+
optFromFile = 1;
223+
} break;
224+
196225
case 'h':
197226
print_usage();
198227
return 0;
@@ -221,25 +250,83 @@ int main(int argc, char** argv)
221250

222251
// todo: catch exceptions
223252

224-
// also read from stdin if option set */
225-
if (optFromStdin) {
226-
LineBuffer lb; // buffer for input lines
227-
std::string msg;
228-
int eof;
253+
// also read from file if option set */
254+
if (optFromFile) {
255+
256+
if (createFifo) {
257+
bool isOk = 0;
258+
int err = 0;
259+
if (mkfifo(inputPath.c_str(), 0666)) {
260+
err = errno;
261+
if (err == EEXIST) {
262+
// is the existing file a fifo ?
263+
struct stat statbuf;
264+
if (stat(inputPath.c_str(), &statbuf) == 0) {
265+
if (S_ISFIFO(statbuf.st_mode)) {
266+
// the FIFO already exists
267+
isOk = 1;
268+
}
269+
}
270+
}
271+
} else {
272+
if (verbose) printf("FIFO %s created\n", inputPath.c_str());
273+
isOk = 1;
274+
}
275+
if (!isOk) {
276+
printf("Failed to create FIFO %s : %s\n", inputPath.c_str(), strerror(err));
277+
return -1;
278+
}
279+
}
229280

230-
// read lines from stdin until EOF, and send them as log messages
231281
for (;;) {
232-
eof = lb.appendFromFileDescriptor(fileno(stdin), -1);
282+
283+
if (inputFd < 0) {
284+
inputFd = open(inputPath.c_str(), O_RDONLY);
285+
if (inputFd < 0) {
286+
printf("Failed to open %s : %s\n", inputPath.c_str(), strerror(errno));
287+
return -1;
288+
}
289+
if (verbose) printf("Input file opened = %d\n",inputFd);
290+
}
291+
292+
LineBuffer lb; // buffer for input lines
293+
std::string msg;
294+
int eof;
295+
296+
// read lines from stdin until EOF, and send them as log messages
233297
for (;;) {
234-
if (lb.getNextLine(msg)) {
235-
// no line left
298+
eof = lb.appendFromFileDescriptor(inputFd, -1);
299+
for (;;) {
300+
if (lb.getNextLine(msg)) {
301+
// no line left
302+
break;
303+
}
304+
//infoLogger_msg_xt(UNDEFINED_STRING,UNDEFINED_INT,UNDEFINED_INT,facility,severity,level,msg);
305+
theLog->log(msgOptions, msgContext, "%s", msg.c_str());
306+
}
307+
if (eof)
236308
break;
237-
}
238-
//infoLogger_msg_xt(UNDEFINED_STRING,UNDEFINED_INT,UNDEFINED_INT,facility,severity,level,msg);
239-
theLog->log(msgOptions, msgContext, "%s", msg.c_str());
240309
}
241-
if (eof)
310+
311+
if (inputFd>0) {
312+
if (verbose) printf("Input file closed\n");
313+
close(inputFd);
314+
inputFd = -1;
315+
}
316+
317+
if (!loopInput) {
242318
break;
319+
}
320+
}
321+
322+
}
323+
324+
// remove fifo after use
325+
if (createFifo) {
326+
if (unlink(inputPath.c_str())) {
327+
printf("Failed to remove %s\n", inputPath.c_str());
328+
} else {
329+
if (verbose) printf("FIFO %s removed\n", inputPath.c_str());
243330
}
244331
}
245332

test/testInfoLogger.cxx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,14 @@ int main()
4848
theLog.log({ InfoLogger::Severity::Info, 123, 456, __FILE__, __LINE__ }, "infoLogger message test with source code info");
4949
theLog.log(LOGINFO(123), "infoLogger message test with source code info");
5050

51+
// example use of context
52+
InfoLoggerContext ctxt({{InfoLoggerContext::FieldName::Facility, std::string("test1")}});
53+
theLog.log({{}},ctxt,"infoLogger message - facility test1");
54+
55+
// reuse a context and overwrite some fields
56+
theLog.log({{}},InfoLoggerContext(ctxt,{{InfoLoggerContext::FieldName::Facility, std::string("test2")}}),"infoLogger message - facility test2");
57+
58+
// c++ style
5159
theLog << "another test message " << InfoLogger::endm;
5260
theLog << InfoLogger::Severity::Error << "another (stream error) message " << InfoLogger::endm;
5361
theLog << InfoLogger::Severity::Warning << "another (stream warning) message " << InfoLogger::endm;

0 commit comments

Comments
 (0)