Skip to content

Commit f4ae9ae

Browse files
authored
Merge pull request #60 from sy-c/master
option string to constructor
2 parents 72f327c + 04f76dd commit f4ae9ae

File tree

9 files changed

+229
-36
lines changed

9 files changed

+229
-36
lines changed

CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ add_library (objInfoLoggerClient OBJECT
105105
src/InfoLoggerClient.cxx
106106
src/infoLoggerMessageDecode.c
107107
src/InfoLoggerMessageHelper.cxx
108+
src/infoLoggerUtils.cxx
108109
src/utility.c
109110
src/simplelog.cxx
110111
)
@@ -249,6 +250,7 @@ add_executable(
249250
src/infoLoggerMessageDecode.c
250251
src/InfoLoggerMessageHelper.cxx
251252
src/InfoLoggerMessageList.cxx
253+
src/infoLoggerUtils.cxx
252254
$<$<BOOL:${MYSQL_FOUND}>:src/InfoLoggerDispatchSQL.cxx>
253255
)
254256
target_include_directories(

include/InfoLogger/InfoLogger.hxx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,11 @@ class InfoLogger
139139
/// May throw exceptions on failure.
140140
InfoLogger();
141141

142+
// Constructor
143+
// with optional options
144+
// provided as a comma-separated list of key=value pairs
145+
InfoLogger(const std::string& options);
146+
142147
/// Destructor
143148
virtual ~InfoLogger();
144149

src/InfoLogger.cxx

Lines changed: 144 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
#include "infoLoggerMessage.h"
2121
#include "InfoLoggerClient.h"
22+
#include "infoLoggerUtils.h"
2223

2324
#include <stdio.h>
2425
#include <stdlib.h>
@@ -171,60 +172,105 @@ namespace AliceO2
171172
namespace InfoLogger
172173
{
173174

174-
int infoLoggerDWarningDone = 0;
175-
176175
// private class to isolate internal data from external interface
177176
class InfoLogger::Impl
178177
{
179178
public:
180-
Impl()
179+
Impl(const std::string& options)
181180
{
182181
// initiate internal members
183182
magicTag = InfoLoggerMagicNumber;
184183
numberOfMessages = 0;
185184
currentStreamMessage.clear();
186185
currentStreamOptions = undefinedMessageOption;
186+
client = nullptr;
187187

188188
floodReset();
189189

190190
if (infoLog_proto_init()) {
191191
throw __LINE__;
192192
}
193193
refreshDefaultMsg();
194-
currentMode = OutputMode::infoLoggerD;
195-
196-
const char* confEnv = getenv("INFOLOGGER_MODE");
197-
if (confEnv != NULL) {
198-
if (!strcmp(confEnv, "stdout")) {
199-
currentMode = OutputMode::stdout;
200-
} else if (!strcmp(confEnv, "infoLoggerD")) {
201-
currentMode = OutputMode::infoLoggerD;
202-
} else if (!strncmp(confEnv, "file", 4)) {
203-
currentMode = OutputMode::file;
204-
const char* logFile = "./log.txt";
205-
if (confEnv[4] == ':') {
206-
logFile = &confEnv[5];
194+
195+
// option-processing routine
196+
auto processOptions = [&](std::string opt) {
197+
std::map<std::string, std::string> kv;
198+
if (getKeyValuePairsFromString(opt, kv)) {
199+
throw __LINE__;
200+
}
201+
for (auto& it : kv) {
202+
if (it.first == "outputMode") {
203+
getOutputStreamFromString(it.second.c_str(), mainMode);
204+
} else if (it.first == "outputModeFallback") {
205+
getOutputStreamFromString(it.second.c_str(), fallbackMode);
206+
} else if (it.first == "verbose") {
207+
verbose = atoi(it.second.c_str());
208+
} else {
209+
// unknown option
210+
// printf("Unknown option %s\n",it.second.c_str());
211+
throw __LINE__;
207212
}
208-
printf("Logging to file %s\n", logFile);
209-
stdLog.setLogFile(logFile);
210-
} else if (!strcmp(confEnv, "raw")) {
211-
currentMode = OutputMode::raw;
212-
} else if (!strcmp(confEnv, "none")) {
213-
currentMode = OutputMode::none; // useful for benchmarks
214213
}
214+
return;
215+
};
216+
217+
// parse options from constructor arg
218+
processOptions(options);
219+
220+
// parse options from environment
221+
const char* confEnvOptions = getenv("INFOLOGGER_OPTIONS");
222+
if (confEnvOptions != NULL) {
223+
processOptions(confEnvOptions);
215224
}
216-
client = nullptr;
217-
if (currentMode == OutputMode::infoLoggerD) {
218-
client = new InfoLoggerClient;
219-
if ((client == nullptr) || (!client->isOk())) {
220-
// fallback to stdout if infoLoggerD not available
221-
if (!infoLoggerDWarningDone) {
222-
infoLoggerDWarningDone = 1;
223-
//fprintf(stderr,"infoLoggerD not available, falling back to stdout logging\n");
225+
226+
const char* confEnvMode = getenv("INFOLOGGER_MODE");
227+
if (confEnvMode != NULL) {
228+
getOutputStreamFromString(confEnvMode, mainMode);
229+
}
230+
231+
// init main output and fallbacks if needed
232+
currentMode = mainMode;
233+
for (int it = 0; it < 3; it++) {
234+
if (it == 0) {
235+
currentMode = mainMode;
236+
} else if (it == 1) {
237+
currentMode = fallbackMode;
238+
} else {
239+
currentMode.mode = OutputMode::none;
240+
currentMode.path = "/dev/null";
241+
}
242+
if (verbose) {
243+
printf("Using output mode %s\n", getStringFromMode(currentMode.mode));
244+
}
245+
246+
if (currentMode.mode == OutputMode::file) {
247+
if (verbose) {
248+
printf("Logging to file %s\n", currentMode.path.c_str());
249+
}
250+
if (stdLog.setLogFile(currentMode.path.c_str(), 0, 4, 0) == 0) {
251+
break;
252+
}
253+
} else if (currentMode.mode == OutputMode::stdout) {
254+
if (stdLog.setLogFile(nullptr) == 0) {
255+
break;
256+
}
257+
} else if (currentMode.mode == OutputMode::none) {
258+
if (stdLog.setLogFile(currentMode.path.c_str()) == 0) {
259+
break;
260+
}
261+
} else if (currentMode.mode == OutputMode::infoLoggerD) {
262+
client = new InfoLoggerClient;
263+
if (client != nullptr) {
264+
if (client->isOk()) {
265+
break;
266+
}
224267
}
225-
currentMode = OutputMode::stdout;
268+
}
269+
if (verbose) {
270+
printf("Output to %s failed\n", getStringFromMode(currentMode.mode));
226271
}
227272
}
273+
228274
// todo
229275
// switch mode based on configuration / environment
230276
// connect to client only on first message (or try again after timeout)
@@ -258,7 +304,61 @@ class InfoLogger::Impl
258304
raw,
259305
none };
260306

261-
OutputMode currentMode; // current option for output
307+
struct OutputStream {
308+
OutputMode mode; // selected mode
309+
std::string path; // optional path (eg for 'file' mode)
310+
};
311+
312+
// convert a string to a member of the OutputMode enum
313+
// and sets filePath in case of the "file" mode
314+
// throw an integer error code on error
315+
void getOutputStreamFromString(const char* s, OutputStream& out)
316+
{
317+
if (s == nullptr) {
318+
throw __LINE__;
319+
}
320+
out.mode = OutputMode::none;
321+
out.path = "";
322+
if (!strcmp(s, "stdout")) {
323+
out.mode = OutputMode::stdout;
324+
} else if (!strncmp(s, "file", 4)) {
325+
out.mode = OutputMode::file;
326+
if (s[4] == ':') {
327+
out.path = std::string(&s[5]);
328+
} else {
329+
out.path = "./log.txt";
330+
}
331+
} else if (!strcmp(s, "infoLoggerD")) {
332+
out.mode = OutputMode::infoLoggerD;
333+
} else if (!strcmp(s, "raw")) {
334+
out.mode = OutputMode::raw;
335+
} else if (!strcmp(s, "none")) {
336+
out.mode = OutputMode::none;
337+
} else {
338+
throw __LINE__;
339+
}
340+
return;
341+
};
342+
343+
const char* getStringFromMode(OutputMode m)
344+
{
345+
if (m == OutputMode::stdout) {
346+
return "stdout";
347+
} else if (m == OutputMode::file) {
348+
return "file";
349+
} else if (m == OutputMode::infoLoggerD) {
350+
return "infoLoggerD";
351+
} else if (m == OutputMode::none) {
352+
return "none";
353+
}
354+
return "unknown";
355+
}
356+
357+
OutputStream currentMode; // current option for output
358+
OutputStream mainMode = { OutputMode::infoLoggerD, "" }; // main output mode
359+
OutputStream fallbackMode = { OutputMode::stdout, "" }; // in case main mode is not available
360+
361+
bool verbose = 0; // in verbose mode, more info printed on stdout
262362

263363
/// Log a message, with a list of arguments of type va_list.
264364
/// \param message NUL-terminated string message to push to the log system. It uses the same format as specified for printf(), and the function accepts additionnal formatting parameters.
@@ -285,7 +385,7 @@ class InfoLogger::Impl
285385
void refreshDefaultMsg();
286386
infoLog_msg_t defaultMsg; //< default log message (in particular, to complete optionnal fields)
287387

288-
InfoLoggerClient* client; //< entity to communicate with local infoLoggerD
388+
InfoLoggerClient* client = nullptr; //< entity to communicate with local infoLoggerD
289389
SimpleLog stdLog; //< object to output messages to stdout/file
290390

291391
bool isRedirecting = false; // state of stdout/stderr redirection
@@ -561,7 +661,7 @@ int InfoLogger::Impl::pushMessage(const InfoLoggerMessageOption& options, const
561661
// on error, close connection / use stdout / buffer messages in memory ?
562662
}
563663

564-
if ((currentMode == OutputMode::stdout) || (currentMode == OutputMode::file)) {
664+
if ((currentMode.mode == OutputMode::stdout) || (currentMode.mode == OutputMode::file)) {
565665
char buffer[LOG_MAX_SIZE];
566666
msgHelper.MessageToText(&msg, buffer, sizeof(buffer), InfoLoggerMessageHelper::Format::Simple);
567667

@@ -582,7 +682,7 @@ int InfoLogger::Impl::pushMessage(const InfoLoggerMessageOption& options, const
582682
}
583683

584684
// raw output: infoLogger protocol to stdout
585-
if (currentMode == OutputMode::raw) {
685+
if (currentMode.mode == OutputMode::raw) {
586686
char buffer[LOG_MAX_SIZE];
587687
msgHelper.MessageToText(&msg, buffer, sizeof(buffer), InfoLoggerMessageHelper::Format::Encoded);
588688
puts(buffer);
@@ -625,7 +725,15 @@ int InfoLogger::Impl::logV(const InfoLoggerMessageOption& options, const InfoLog
625725

626726
InfoLogger::InfoLogger()
627727
{
628-
mPimpl = std::make_unique<InfoLogger::Impl>();
728+
mPimpl = std::make_unique<InfoLogger::Impl>("");
729+
if (mPimpl == NULL) {
730+
throw __LINE__;
731+
}
732+
}
733+
734+
InfoLogger::InfoLogger(const std::string& options)
735+
{
736+
mPimpl = std::make_unique<InfoLogger::Impl>(options);
629737
if (mPimpl == NULL) {
630738
throw __LINE__;
631739
}

src/InfoLoggerScripting.cxx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ InfoLogger::InfoLogger()
2222
{
2323
logHandle = std::make_unique<baseInfoClass>();
2424
}
25+
InfoLogger::InfoLogger(const std::string& options)
26+
{
27+
logHandle = std::make_unique<baseInfoClass>(options);
28+
}
2529

2630
InfoLogger::~InfoLogger()
2731
{

src/InfoLoggerScripting.hxx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ class InfoLogger
7676
{
7777
public:
7878
InfoLogger();
79+
InfoLogger(const std::string& options);
7980
~InfoLogger();
8081

8182
// log with default metadata fields and specified severity

src/infoLogger.i

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ class InfoLoggerMetadata {
4545
class InfoLogger {
4646
public:
4747
InfoLogger();
48+
InfoLogger(const std::string &message);
4849
~InfoLogger();
4950

5051
int logInfo(const std::string &message);

src/infoLoggerUtils.cxx

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// Copyright CERN and copyright holders of ALICE O2. This software is
2+
// distributed under the terms of the GNU General Public License v3 (GPL
3+
// Version 3), copied verbatim in the file "COPYING".
4+
//
5+
// See http://alice-o2.web.cern.ch/license for full licensing information.
6+
//
7+
// In applying this license CERN does not waive the privileges and immunities
8+
// granted to it by virtue of its status as an Intergovernmental Organization
9+
// or submit itself to any jurisdiction.
10+
11+
#include "infoLoggerUtils.h"
12+
13+
int getKeyValuePairsFromString(const std::string& input, std::map<std::string, std::string>& output)
14+
{
15+
output.clear();
16+
std::size_t ix0 = 0; // begin of pair in string
17+
std::size_t ix1 = std::string::npos; // end of pair in string
18+
std::size_t ix2 = std::string::npos; // position of '='
19+
for (;;) {
20+
ix1 = input.find(",", ix0);
21+
ix2 = input.find("=", ix0);
22+
if (ix2 >= ix1) {
23+
break;
24+
} // end of string
25+
26+
const std::string& trimchars = "\t\n\v\f\r ";
27+
28+
// trim key
29+
std::string key = input.substr(ix0, ix2 - ix0);
30+
key.erase(key.find_last_not_of(trimchars) + 1);
31+
key.erase(0, key.find_first_not_of(trimchars));
32+
33+
// trim value
34+
std::string value;
35+
if (ix1 == std::string::npos) {
36+
value = input.substr(ix2 + 1);
37+
} else {
38+
value = input.substr(ix2 + 1, ix1 - (ix2 + 1));
39+
}
40+
value.erase(value.find_last_not_of(trimchars) + 1);
41+
value.erase(0, value.find_first_not_of(trimchars));
42+
43+
// insert in map
44+
output.insert(std::pair<std::string, std::string>(key, value));
45+
46+
// iterate next pair unless end of string
47+
if (ix1 == std::string::npos) {
48+
break;
49+
}
50+
ix0 = ix1 + 1;
51+
}
52+
return 0;
53+
}

src/infoLoggerUtils.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright CERN and copyright holders of ALICE O2. This software is
2+
// distributed under the terms of the GNU General Public License v3 (GPL
3+
// Version 3), copied verbatim in the file "COPYING".
4+
//
5+
// See http://alice-o2.web.cern.ch/license for full licensing information.
6+
//
7+
// In applying this license CERN does not waive the privileges and immunities
8+
// granted to it by virtue of its status as an Intergovernmental Organization
9+
// or submit itself to any jurisdiction.
10+
11+
#include <string>
12+
#include <map>
13+
14+
// convert a string comma-separated list of key=value pairs to a map
15+
int getKeyValuePairsFromString(const std::string& input, std::map<std::string, std::string>& output);

test/testInfoLoggerDB.cxx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@
55
#include <sys/types.h>
66
#include <unistd.h>
77

8+
#if LIBMYSQL_VERSION_ID >= 80000
9+
typedef bool my_bool;
10+
#endif
11+
812
int main(int argc, char** argv)
913
{
1014

0 commit comments

Comments
 (0)