Line data Source code
1 : // Copyright (c) 2013-2021 Made to Order Software Corp. All Rights Reserved
2 : //
3 : // https://snapwebsites.org/project/snaplogger
4 : // contact@m2osw.com
5 : //
6 : // This program is free software; you can redistribute it and/or modify
7 : // it under the terms of the GNU General Public License as published by
8 : // the Free Software Foundation; either version 2 of the License, or
9 : // (at your option) any later version.
10 : //
11 : // This program is distributed in the hope that it will be useful,
12 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : // GNU General Public License for more details.
15 : //
16 : // You should have received a copy of the GNU General Public License along
17 : // with this program; if not, write to the Free Software Foundation, Inc.,
18 : // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 :
20 : /** \file
21 : * \brief Implementation of the various logger variable support.
22 : *
23 : * This file implements the few logger variables which retrieves their
24 : * value from the message variable.
25 : */
26 :
27 :
28 : // self
29 : //
30 : #include "snaplogger/exception.h"
31 : #include "snaplogger/format.h"
32 : #include "snaplogger/map_diagnostic.h"
33 : #include "snaplogger/nested_diagnostic.h"
34 : #include "snaplogger/trace_diagnostic.h"
35 : #include "snaplogger/variable.h"
36 :
37 :
38 : // C++ lib
39 : //
40 : #include <iostream>
41 :
42 :
43 : // last include
44 : //
45 : #include <snapdev/poison.h>
46 :
47 :
48 :
49 : namespace snaplogger
50 : {
51 :
52 :
53 : namespace
54 : {
55 :
56 :
57 :
58 35 : DEFINE_LOGGER_VARIABLE(severity)
59 : {
60 12 : severity_t sev(msg.get_severity());
61 24 : severity::pointer_t severity(get_severity(msg, sev));
62 12 : if(severity == nullptr)
63 : {
64 : // not found, write value directly
65 : //
66 0 : value += std::to_string(static_cast<int>(sev));
67 0 : variable::process_value(msg, value);
68 0 : return;
69 : }
70 :
71 24 : auto params(get_params());
72 12 : if(!params.empty())
73 : {
74 0 : if(params[0]->get_name() == "format")
75 : {
76 0 : auto v(params[0]->get_value());
77 0 : if(v != "alpha")
78 : {
79 0 : if(v == "number")
80 : {
81 0 : value += std::to_string(static_cast<int>(sev));
82 0 : variable::process_value(msg, value);
83 0 : return;
84 : }
85 : throw invalid_variable(
86 : "the ${severity:format=alpha|number} variable cannot be set to \""
87 0 : + v
88 0 : + "\".");
89 : }
90 : }
91 : }
92 :
93 12 : value += severity->get_description();
94 :
95 12 : variable::process_value(msg, value);
96 : }
97 :
98 :
99 :
100 45506 : DEFINE_LOGGER_VARIABLE(message)
101 : {
102 45369 : if(msg.get_recursive_message())
103 : {
104 : // do nothing if we find a ${message} inside the message itself
105 2 : return;
106 : }
107 :
108 90734 : std::string const m(msg.get_message());
109 45367 : if(m.find("${") == std::string::npos)
110 : {
111 45350 : value += m;
112 : }
113 : else
114 : {
115 17 : msg.set_recursive_message(true);
116 34 : format f(m);
117 17 : value += f.process_message(msg);
118 17 : msg.set_recursive_message(false);
119 : }
120 :
121 45367 : variable::process_value(msg, value);
122 : }
123 :
124 :
125 :
126 12 : DEFINE_LOGGER_VARIABLE_IGNORED_ON_NO_REPEAT(field)
127 : {
128 2 : auto params(get_params());
129 1 : if(!params.empty())
130 : {
131 1 : if(params[0]->get_name() == "name")
132 : {
133 2 : auto name(params[0]->get_value());
134 1 : value += msg.get_field(name);
135 : }
136 : }
137 :
138 1 : variable::process_value(msg, value);
139 1 : }
140 :
141 :
142 :
143 12 : DEFINE_LOGGER_VARIABLE_IGNORED_ON_NO_REPEAT(fields)
144 : {
145 : enum class format_t
146 : {
147 : FORMAT_JSON,
148 : FORMAT_SHELL
149 : };
150 : enum class json_t
151 : {
152 : START_COMMA,
153 : END_COMMA,
154 : OBJECT
155 : };
156 :
157 1 : format_t format(format_t::FORMAT_JSON);
158 1 : json_t json(json_t::OBJECT);
159 :
160 2 : auto params(get_params());
161 1 : for(auto p : params)
162 : {
163 0 : auto const v(p->get_value());
164 0 : if(p->get_name() == "format")
165 : {
166 0 : if(v == "json")
167 : {
168 0 : format = format_t::FORMAT_JSON;
169 : }
170 0 : else if(v == "shell")
171 : {
172 0 : format = format_t::FORMAT_SHELL;
173 : }
174 : else
175 : {
176 : throw invalid_variable(
177 : "the ${fields:format=json|shell} variable cannot be set to \""
178 0 : + v
179 0 : + "\".");
180 : }
181 : }
182 0 : else if(p->get_name() == "json")
183 : {
184 0 : if(v == "start-comma")
185 : {
186 0 : json = json_t::START_COMMA;
187 : }
188 0 : else if(v == "end-comma")
189 : {
190 0 : json = json_t::END_COMMA;
191 : }
192 0 : else if(v == "object")
193 : {
194 0 : json = json_t::OBJECT;
195 : }
196 : else
197 : {
198 : throw invalid_variable(
199 : "the ${fields:json=start-comma|end-comma|object} variable cannot be set to \""
200 0 : + v
201 0 : + "\".");
202 : }
203 : }
204 : }
205 :
206 1 : int start_comma(0);
207 1 : switch(format)
208 : {
209 1 : case format_t::FORMAT_JSON:
210 1 : if(json == json_t::OBJECT)
211 : {
212 1 : value += "{";
213 1 : start_comma = 1;
214 : }
215 1 : break;
216 :
217 0 : case format_t::FORMAT_SHELL:
218 : // nothing for this one
219 0 : break;
220 :
221 : }
222 :
223 4 : for(auto f : msg.get_fields())
224 : {
225 3 : switch(format)
226 : {
227 3 : case format_t::FORMAT_JSON:
228 3 : if(json == json_t::START_COMMA || start_comma == 2)
229 : {
230 2 : value += ",";
231 : }
232 : // TODO: need to test for quotes inside "first" or "second"
233 3 : value += "\"" + f.first + "\":" + "\"" + f.second + "\"";
234 3 : if(json == json_t::END_COMMA)
235 : {
236 0 : value += ",";
237 : }
238 3 : else if(json == json_t::OBJECT)
239 : {
240 : // if more than one field, make sure to add commas
241 : // between each field
242 : //
243 3 : start_comma = 2;
244 : }
245 3 : break;
246 :
247 0 : case format_t::FORMAT_SHELL:
248 0 : value += f.first + "=" + f.second + "\n";
249 0 : break;
250 :
251 : }
252 : }
253 1 : switch(format)
254 : {
255 1 : case format_t::FORMAT_JSON:
256 1 : if(json == json_t::OBJECT)
257 : {
258 1 : value += "}";
259 : }
260 1 : break;
261 :
262 0 : case format_t::FORMAT_SHELL:
263 : // nothing for this one
264 0 : break;
265 :
266 : }
267 :
268 1 : variable::process_value(msg, value);
269 1 : }
270 :
271 :
272 :
273 37 : DEFINE_LOGGER_VARIABLE(project_name)
274 : {
275 : // when the advgetopt is properly connected to the logger, then the
276 : // logger will save the project name in its diagnostic map
277 : //
278 22 : map_diagnostics_t map(get_map_diagnostics(msg));
279 11 : auto it(map.find(DIAG_KEY_PROJECT_NAME));
280 11 : if(it != map.end())
281 : {
282 11 : value += it->second;
283 : }
284 :
285 11 : variable::process_value(msg, value);
286 11 : }
287 :
288 :
289 :
290 20 : DEFINE_LOGGER_VARIABLE(progname)
291 : {
292 : // when the advgetopt is properly connected to the logger, then the
293 : // logger will save the program name in its diagnostic map
294 : //
295 12 : map_diagnostics_t map(get_map_diagnostics(msg));
296 6 : auto it(map.find(DIAG_KEY_PROGNAME));
297 6 : if(it != map.end())
298 : {
299 6 : value += it->second;
300 : }
301 :
302 6 : variable::process_value(msg, value);
303 6 : }
304 :
305 :
306 :
307 46 : DEFINE_LOGGER_VARIABLE(version)
308 : {
309 : // when the advgetopt is properly connected to the logger, then the
310 : // logger will save the program version in its diagnostic map
311 : //
312 34 : map_diagnostics_t map(get_map_diagnostics(msg));
313 17 : auto it(map.find(DIAG_KEY_VERSION));
314 17 : if(it != map.end())
315 : {
316 15 : value += it->second;
317 : }
318 :
319 17 : variable::process_value(msg, value);
320 17 : }
321 :
322 :
323 :
324 8 : DEFINE_LOGGER_VARIABLE(build_date)
325 : {
326 0 : map_diagnostics_t map(get_map_diagnostics(msg));
327 0 : auto it(map.find(DIAG_KEY_BUILD_DATE));
328 0 : if(it != map.end())
329 : {
330 0 : value += it->second;
331 : }
332 :
333 0 : variable::process_value(msg, value);
334 0 : }
335 :
336 :
337 :
338 8 : DEFINE_LOGGER_VARIABLE(build_time)
339 : {
340 0 : map_diagnostics_t map(get_map_diagnostics(msg));
341 0 : auto it(map.find(DIAG_KEY_BUILD_TIME));
342 0 : if(it != map.end())
343 : {
344 0 : value += it->second;
345 : }
346 :
347 0 : variable::process_value(msg, value);
348 0 : }
349 :
350 :
351 :
352 8 : DEFINE_LOGGER_VARIABLE(filename)
353 : {
354 0 : value += msg.get_filename();
355 :
356 0 : variable::process_value(msg, value);
357 0 : }
358 :
359 :
360 :
361 11 : DEFINE_LOGGER_VARIABLE(basename)
362 : {
363 0 : std::string const filename(msg.get_filename());
364 0 : std::string::size_type const pos(filename.rfind('/'));
365 0 : if(pos == std::string::npos)
366 : {
367 0 : value += filename;
368 : }
369 : else
370 : {
371 0 : value += filename.substr(pos + 1);
372 : }
373 :
374 0 : variable::process_value(msg, value);
375 0 : }
376 :
377 :
378 :
379 8 : DEFINE_LOGGER_VARIABLE(path)
380 : {
381 0 : std::string const filename(msg.get_filename());
382 0 : std::string::size_type const pos(filename.rfind('/'));
383 0 : if(pos != std::string::npos)
384 : {
385 0 : value += filename.substr(0, pos);
386 : }
387 :
388 0 : variable::process_value(msg, value);
389 0 : }
390 :
391 :
392 :
393 11 : DEFINE_LOGGER_VARIABLE(function)
394 : {
395 0 : value += msg.get_function();
396 :
397 0 : variable::process_value(msg, value);
398 0 : }
399 :
400 :
401 :
402 11 : DEFINE_LOGGER_VARIABLE(line)
403 : {
404 0 : value += std::to_string(msg.get_line());
405 :
406 0 : variable::process_value(msg, value);
407 0 : }
408 :
409 :
410 :
411 80 : DEFINE_LOGGER_VARIABLE(diagnostic)
412 : {
413 18 : constexpr int FLAG_NESTED = 0x01;
414 18 : constexpr int FLAG_MAP = 0x02;
415 18 : constexpr int FLAG_TRACE = 0x04;
416 :
417 18 : std::int64_t nested_depth(-1);
418 18 : std::int64_t trace_depth(-1);
419 36 : std::string key;
420 18 : int flags(0);
421 :
422 36 : for(auto p : get_params())
423 : {
424 18 : if(p->get_name() == "nested")
425 : {
426 8 : nested_depth = p->get_integer();
427 8 : flags |= FLAG_NESTED;
428 : }
429 10 : else if(p->get_name() == "map")
430 : {
431 10 : key = p->get_value();
432 10 : flags |= FLAG_MAP;
433 : }
434 0 : else if(p->get_name() == "trace")
435 : {
436 0 : trace_depth = p->get_integer();
437 0 : flags |= FLAG_TRACE;
438 : }
439 : }
440 :
441 18 : if(flags == 0
442 18 : || (flags & FLAG_NESTED) != 0)
443 : {
444 8 : char sep('{');
445 16 : string_vector_t nested(get_nested_diagnostics(msg));
446 8 : size_t idx(0);
447 16 : if((flags & FLAG_NESTED) != 0
448 8 : && static_cast<ssize_t>(nested.size()) > nested_depth)
449 : {
450 2 : idx = nested.size() - nested_depth;
451 2 : value += sep;
452 2 : value += "...";
453 2 : sep = '/';
454 : }
455 :
456 40 : for(; idx < nested.size(); ++idx)
457 : {
458 16 : value += sep;
459 16 : sep = '/';
460 16 : value += nested[idx];
461 : }
462 8 : value += "}";
463 : }
464 :
465 18 : if(flags == 0
466 18 : || (flags & FLAG_TRACE) != 0)
467 : {
468 0 : trace_diagnostics_t trace(get_trace_diagnostics());
469 0 : if(trace.empty())
470 : {
471 0 : value += "[<no trace>]";
472 : }
473 : else
474 : {
475 0 : auto it(trace.begin());
476 0 : size_t sz(trace.size());
477 0 : if((flags & FLAG_TRACE) != 0)
478 : {
479 0 : for(; static_cast<int64_t>(sz) > trace_depth && it != trace.end(); ++it, --sz);
480 : }
481 :
482 0 : char sep('[');
483 0 : for(; it != trace.end(); ++it)
484 : {
485 0 : value += sep;
486 0 : sep = '/';
487 0 : value += *it;
488 : }
489 0 : value += "]";
490 : }
491 : }
492 :
493 18 : if(flags == 0
494 18 : || (flags & FLAG_MAP) != 0)
495 : {
496 20 : auto const diagnostics(get_map_diagnostics(msg));
497 10 : if(!diagnostics.empty())
498 : {
499 10 : if(key.empty())
500 : {
501 0 : char sep('<');
502 0 : for(auto d : diagnostics)
503 : {
504 0 : value += sep;
505 0 : sep = ':';
506 0 : value += d.first;
507 0 : value += '=';
508 0 : value += d.second;
509 : }
510 0 : value += '>';
511 : }
512 : else
513 : {
514 10 : auto it(diagnostics.find(key));
515 10 : if(it != diagnostics.end())
516 : {
517 10 : value += '<';
518 10 : value += key;
519 10 : value += '=';
520 10 : value += it->second;
521 10 : value += '>';
522 : }
523 : }
524 : }
525 : }
526 :
527 18 : variable::process_value(msg, value);
528 18 : }
529 :
530 :
531 : }
532 : // no name namespace
533 :
534 :
535 6 : } // snaplogger namespace
536 : // vim: ts=4 sw=4 et
|