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