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