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 Severity levels for your log messages.
24 : *
25 : * The severity implementation loads the severity configuration file
26 : * and generates a set of severity levels that one can attach to
27 : * log messages.
28 : */
29 :
30 :
31 : // self
32 : //
33 : #include "snaplogger/severity.h"
34 :
35 : #include "snaplogger/exception.h"
36 : #include "snaplogger/guard.h"
37 : #include "snaplogger/private_logger.h"
38 :
39 :
40 : // advgetopt lib
41 : //
42 : #include <advgetopt/advgetopt.h>
43 : #include <advgetopt/options.h>
44 :
45 :
46 : // boost lib
47 : //
48 : #include <boost/preprocessor/stringize.hpp>
49 :
50 :
51 :
52 : // C++ lib
53 : //
54 : #include <iostream>
55 : #include <map>
56 :
57 :
58 : // C lib
59 : //
60 : #include <sys/time.h>
61 :
62 :
63 : // last include
64 : //
65 : #include <snapdev/poison.h>
66 :
67 :
68 :
69 : namespace snaplogger
70 : {
71 :
72 :
73 :
74 : namespace
75 : {
76 :
77 :
78 : bool g_severity_auto_added = false;
79 :
80 :
81 : struct system_severity
82 : {
83 : severity_t f_severity = severity_t::SEVERITY_ALL;
84 : char const * f_name = nullptr;
85 : char const * f_alias = nullptr; // at most 1 alias for system severities
86 : char const * f_description = nullptr;
87 : char const * f_styles = nullptr;
88 : };
89 :
90 : #pragma GCC diagnostic push
91 : #pragma GCC diagnostic ignored "-Wpedantic"
92 : constexpr system_severity g_system_severity[] =
93 : {
94 : {
95 : .f_severity = severity_t::SEVERITY_ALL,
96 : .f_name = "all",
97 : .f_alias = "everything",
98 : .f_description = "all",
99 : .f_styles = nullptr
100 : },
101 : {
102 : .f_severity = severity_t::SEVERITY_TRACE,
103 : .f_name = "trace",
104 : .f_alias = nullptr,
105 : .f_description = "trace",
106 : .f_styles = nullptr
107 : },
108 : {
109 : .f_severity = severity_t::SEVERITY_DEBUG,
110 : .f_name = "debug",
111 : .f_alias = "dbg",
112 : .f_description = "debug",
113 : .f_styles = nullptr
114 : },
115 : {
116 : .f_severity = severity_t::SEVERITY_NOTICE,
117 : .f_name = "notice",
118 : .f_alias = "note",
119 : .f_description = "notice",
120 : .f_styles = nullptr
121 : },
122 : {
123 : .f_severity = severity_t::SEVERITY_UNIMPORTANT,
124 : .f_name = "unimportant",
125 : .f_alias = nullptr,
126 : .f_description = "unimportant",
127 : .f_styles = nullptr
128 : },
129 : {
130 : .f_severity = severity_t::SEVERITY_VERBOSE,
131 : .f_name = "verbose",
132 : .f_alias = "verb",
133 : .f_description = "verbose",
134 : .f_styles = nullptr
135 : },
136 : {
137 : .f_severity = severity_t::SEVERITY_INFORMATION,
138 : .f_name = "information",
139 : .f_alias = "info",
140 : .f_description = "info",
141 : .f_styles = nullptr
142 : },
143 : {
144 : .f_severity = severity_t::SEVERITY_IMPORTANT,
145 : .f_name = "important",
146 : .f_alias = "significant",
147 : .f_description = "important",
148 : .f_styles = "green"
149 : },
150 : {
151 : .f_severity = severity_t::SEVERITY_MINOR,
152 : .f_name = "minor",
153 : .f_alias = nullptr,
154 : .f_description = "minor",
155 : .f_styles = "green"
156 : },
157 : {
158 : .f_severity = severity_t::SEVERITY_DEPRECATED,
159 : .f_name = "deprecated",
160 : .f_alias = nullptr,
161 : .f_description = "deprecated",
162 : .f_styles = "orange"
163 : },
164 : {
165 : .f_severity = severity_t::SEVERITY_WARNING,
166 : .f_name = "warning",
167 : .f_alias = "warn",
168 : .f_description = "warning",
169 : .f_styles = "orange"
170 : },
171 : {
172 : .f_severity = severity_t::SEVERITY_MAJOR,
173 : .f_name = "major",
174 : .f_alias = "paramount",
175 : .f_description = "major",
176 : .f_styles = "orange"
177 : },
178 : {
179 : .f_severity = severity_t::SEVERITY_RECOVERABLE_ERROR,
180 : .f_name = "recoverable-error",
181 : .f_alias = "recoverable",
182 : .f_description = "recoverable error",
183 : .f_styles = "red"
184 : },
185 : {
186 : .f_severity = severity_t::SEVERITY_ERROR,
187 : .f_name = "error",
188 : .f_alias = "err",
189 : .f_description = "error",
190 : .f_styles = "red"
191 : },
192 : {
193 : .f_severity = severity_t::SEVERITY_CRITICAL,
194 : .f_name = "critical",
195 : .f_alias = "crit",
196 : .f_description = "critical",
197 : .f_styles = "red"
198 : },
199 : {
200 : .f_severity = severity_t::SEVERITY_ALERT,
201 : .f_name = "alert",
202 : .f_alias = nullptr,
203 : .f_description = "alert",
204 : .f_styles = "red"
205 : },
206 : {
207 : .f_severity = severity_t::SEVERITY_EMERGENCY,
208 : .f_name = "emergency",
209 : .f_alias = "emerg",
210 : .f_description = "emergency",
211 : .f_styles = "red"
212 : },
213 : {
214 : .f_severity = severity_t::SEVERITY_FATAL,
215 : .f_name = "fatal",
216 : .f_alias = "fatal-error",
217 : .f_description = "fatal",
218 : .f_styles = "red"
219 : },
220 : {
221 : .f_severity = severity_t::SEVERITY_OFF,
222 : .f_name = "off",
223 : .f_alias = "nothing",
224 : .f_description = "off",
225 : .f_styles = nullptr
226 : }
227 : };
228 :
229 :
230 : constexpr char const * const g_configuration_directories[] = {
231 : "/usr/share/snaplogger",
232 : "/etc/snaplogger",
233 : nullptr
234 : };
235 :
236 :
237 : advgetopt::options_environment g_config_option =
238 : {
239 : .f_project_name = "logger",
240 : .f_group_name = nullptr,
241 : .f_options = nullptr,
242 : .f_options_files_directory = nullptr,
243 : .f_environment_variable_name = nullptr,
244 : .f_configuration_files = nullptr,
245 : .f_configuration_filename = "severity.ini",
246 : .f_configuration_directories = g_configuration_directories,
247 : .f_environment_flags = advgetopt::GETOPT_ENVIRONMENT_FLAG_DYNAMIC_PARAMETERS
248 :
249 : //.f_help_header = nullptr,
250 : //.f_help_footer = nullptr,
251 : //.f_version = nullptr,
252 : //.f_license = nullptr,
253 : //.f_copyright = nullptr,
254 : //.f_build_date = UTC_BUILD_DATE,
255 : //.f_build_time = UTC_BUILD_TIME,
256 : //.f_groups = nullptr
257 : };
258 : #pragma GCC diagnostic pop
259 :
260 :
261 :
262 : /** \brief Add system severities.
263 : *
264 : * This function goes through the list of system securities and add them.
265 : * Then it loads the severity.ini files it files and updates the system
266 : * security levels and adds the user defined security levels.
267 : *
268 : * The severity.ini file format is as follow:
269 : *
270 : * * Section name, this is the name of the severity such as `[debug]`
271 : * * Section field: `severity`, the severity level of this severity
272 : * * Section field: `alias`, a list of other names for this severity (i.e.
273 : * the `information` severity is also named `info`)
274 : * * Section field: `description`, the description of the severity
275 : * * Section field: `styles`, the styles such as the color (red, orange...)
276 : *
277 : * For example:
278 : *
279 : * \code
280 : * [information]
281 : * level=50
282 : * aliases=info
283 : * description=info
284 : * default=true
285 : * styles=green
286 : * \endcode
287 : */
288 71 : void auto_add_severities()
289 : {
290 72 : guard g;
291 :
292 71 : if(g_severity_auto_added)
293 : {
294 70 : return;
295 : }
296 1 : g_severity_auto_added = true;
297 :
298 2 : private_logger::pointer_t l(get_private_logger());
299 :
300 20 : for(auto ss : g_system_severity)
301 : {
302 38 : severity::pointer_t sev(std::make_shared<severity>(ss.f_severity, ss.f_name, true));
303 19 : if(ss.f_alias != nullptr)
304 : {
305 : // at this time we have one at the most here
306 : //
307 14 : sev->add_alias(ss.f_alias);
308 : }
309 19 : sev->set_description(ss.f_description);
310 19 : if(ss.f_styles != nullptr)
311 : {
312 11 : sev->set_styles(ss.f_styles);
313 : }
314 :
315 19 : add_severity(sev);
316 : }
317 :
318 : // load user editable parameters
319 : //
320 2 : advgetopt::getopt::pointer_t config(std::make_shared<advgetopt::getopt>(g_config_option));
321 1 : config->parse_configuration_files();
322 2 : advgetopt::option_info::pointer_t const sections(config->get_option(advgetopt::CONFIGURATION_SECTIONS));
323 1 : if(sections != nullptr)
324 : {
325 1 : size_t const max(sections->size());
326 20 : for(size_t idx(0); idx < max; ++idx)
327 : {
328 38 : std::string const section_name(sections->get_value(idx));
329 :
330 : // we found a section name, this is the name of a severity,
331 : // gather the info and create the new severity
332 : //
333 38 : severity::pointer_t sev(get_severity(section_name));
334 19 : if(sev != nullptr)
335 : {
336 : // it already exists...
337 : //
338 : // we allow system severities to get updated in various ways
339 : // but user defined severities must be defined just once
340 : //
341 19 : if(!sev->is_system())
342 : {
343 : throw duplicate_error("we found two severity levels named \""
344 0 : + section_name
345 0 : + "\" in your severity.ini file.");
346 : }
347 :
348 : // although we allow most parameters to be changed,
349 : // we do not want the severity level to be changed
350 : //
351 38 : std::string const severity_field(section_name + "::severity");
352 19 : if(config->is_defined(severity_field))
353 : {
354 19 : long const level(config->get_long(severity_field));
355 19 : if(level < 0 || level > 255)
356 : {
357 0 : throw invalid_severity("severity level must be between 0 and 255.");
358 : }
359 19 : if(static_cast<severity_t>(level) != sev->get_severity())
360 : {
361 : throw invalid_severity("severity level of a system entry cannot be changed. \""
362 0 : + section_name
363 0 : + "\" is trying to do so with "
364 0 : + std::to_string(level)
365 0 : + " instead of "
366 0 : + std::to_string(static_cast<long>(sev->get_severity()))
367 0 : + " (but it's best not to define the severity level at all for system severities).");
368 : }
369 : }
370 :
371 38 : std::string const aliases_field(section_name + "::aliases");
372 19 : if(config->is_defined(aliases_field))
373 : {
374 18 : std::string const aliases(config->get_string(aliases_field));
375 18 : advgetopt::string_list_t names;
376 9 : advgetopt::split_string(aliases, names, {","});
377 18 : auto const & existing_names(sev->get_all_names());
378 19 : for(auto n : names)
379 : {
380 10 : auto const existing_alias(std::find(existing_names.begin(), existing_names.end(), n));
381 10 : if(existing_alias == existing_names.end())
382 : {
383 1 : sev->add_alias(n);
384 : }
385 : }
386 : }
387 : }
388 : else
389 : {
390 0 : std::string const severity_field(section_name + "::severity");
391 0 : if(!config->is_defined(severity_field))
392 : {
393 : throw invalid_severity("severity level must be defined for non-system severity entries. \""
394 0 : + section_name
395 0 : + "\" was not found.");
396 : }
397 0 : long const level(config->get_long(severity_field));
398 0 : if(level < 0 || level > 255)
399 : {
400 : throw invalid_severity("severity level must be between 0 and 255, "
401 0 : + std::to_string(level)
402 0 : + " is not valid.");
403 : }
404 :
405 0 : sev = get_severity(static_cast<severity_t>(level));
406 0 : if(sev != nullptr)
407 : {
408 : throw duplicate_error("there is another severity with level "
409 0 : + std::to_string(level)
410 0 : + ", try using aliases=... instead.");
411 : }
412 :
413 0 : sev = std::make_shared<severity>(static_cast<severity_t>(level), section_name);
414 :
415 0 : std::string const aliases_field(section_name + "::aliases");
416 0 : if(config->is_defined(aliases_field))
417 : {
418 0 : std::string const aliases(config->get_string(aliases_field));
419 0 : advgetopt::string_list_t names;
420 0 : advgetopt::split_string(aliases, names, {","});
421 0 : for(auto n : names)
422 : {
423 0 : sev->add_alias(n);
424 : }
425 : }
426 :
427 0 : add_severity(sev);
428 : }
429 :
430 38 : std::string const description_field(section_name + "::description");
431 19 : if(config->is_defined(description_field))
432 : {
433 18 : sev->set_description(config->get_string(description_field));
434 : }
435 :
436 38 : std::string const styles_field(section_name + "::styles");
437 19 : if(config->is_defined(styles_field))
438 : {
439 11 : sev->set_styles(config->get_string(styles_field));
440 : }
441 :
442 38 : std::string const default_field(section_name + "::default");
443 19 : if(config->is_defined(default_field))
444 : {
445 1 : if(advgetopt::is_true(config->get_string(default_field)))
446 : {
447 1 : l->set_default_severity(sev);
448 : }
449 : }
450 : }
451 : }
452 : }
453 :
454 :
455 :
456 : }
457 : // no name namespace
458 :
459 :
460 :
461 :
462 25 : severity::severity(severity_t sev, std::string const & name, bool system)
463 : : f_severity(sev)
464 25 : , f_names(string_vector_t({name}))
465 50 : , f_system(system)
466 : {
467 25 : if(sev < severity_t::SEVERITY_MIN || sev > severity_t::SEVERITY_MAX)
468 : {
469 : throw invalid_severity("the severity level can't be "
470 0 : + std::to_string(static_cast<int>(sev))
471 0 : + ". The possible range is ["
472 : BOOST_PP_STRINGIZE(severity_t::SEVERITY_MIN)
473 : ".."
474 : BOOST_PP_STRINGIZE(severity_t::SEVERITY_MAX)
475 0 : "]");
476 : }
477 25 : }
478 :
479 :
480 72 : severity_t severity::get_severity() const
481 : {
482 72 : return f_severity;
483 : }
484 :
485 :
486 23 : bool severity::is_system() const
487 : {
488 23 : return f_system;
489 : }
490 :
491 :
492 : /** \brief Mark the severity as registered with the logger.
493 : *
494 : * \internal
495 : *
496 : * This function is considered internal. It is called by the private logger
497 : * implementation at the time the severity is added to the system.
498 : *
499 : * You should never have to call this function directly. You can use the
500 : * is_registered() function to check whether the severity was already
501 : * added or not.
502 : *
503 : * \sa private_logger::add_severity()
504 : */
505 21 : void severity::mark_as_registered()
506 : {
507 21 : f_registered = true;
508 21 : }
509 :
510 :
511 16 : bool severity::is_registered() const
512 : {
513 16 : return f_registered;
514 : }
515 :
516 :
517 6 : std::string severity::get_name() const
518 : {
519 6 : return f_names[0];
520 : }
521 :
522 :
523 : /** \brief All the aliases must be added before registering a severity.
524 : *
525 : * This function adds another alias to the severity.
526 : *
527 : * \exception duplicate_error
528 : * The function raises the duplicate error whenever the alias being added
529 : * is one that already exists in the list of this severity aliases.
530 : * It will not warn about adding the same alias to another severity as
531 : * long as that other severity is not a system defined severity.
532 : */
533 16 : void severity::add_alias(std::string const & name)
534 : {
535 16 : auto it(std::find(f_names.begin(), f_names.end(), name));
536 16 : if(it != f_names.end())
537 : {
538 : throw duplicate_error("severity \""
539 0 : + f_names[0]
540 0 : + "\" already has an alias \""
541 0 : + name
542 0 : + "\".");
543 : }
544 :
545 16 : f_names.push_back(name);
546 :
547 : // TODO: we may be able to add a test here, but we allow
548 : // adding new aliases to system severity
549 :
550 16 : if(is_registered())
551 : {
552 4 : private_logger::pointer_t l(get_private_logger());
553 2 : l->add_alias(l->get_severity(f_severity), name);
554 : }
555 16 : }
556 :
557 :
558 58 : string_vector_t severity::get_all_names() const
559 : {
560 58 : return f_names;
561 : }
562 :
563 :
564 39 : void severity::set_description(std::string const & description)
565 : {
566 39 : f_description = description;
567 39 : }
568 :
569 :
570 15 : std::string severity::get_description() const
571 : {
572 15 : if(f_description.empty())
573 : {
574 2 : return get_name();
575 : }
576 13 : return f_description;
577 : }
578 :
579 :
580 23 : void severity::set_styles(std::string const & styles)
581 : {
582 23 : f_styles = styles;
583 23 : }
584 :
585 :
586 1 : std::string severity::get_styles() const
587 : {
588 1 : return f_styles;
589 : }
590 :
591 :
592 :
593 :
594 :
595 25 : void add_severity(severity::pointer_t sev)
596 : {
597 25 : auto_add_severities();
598 29 : get_private_logger()->add_severity(sev);
599 21 : }
600 :
601 :
602 28 : severity::pointer_t get_severity(std::string const & name)
603 : {
604 28 : auto_add_severities();
605 28 : return get_private_logger()->get_severity(name);
606 : }
607 :
608 :
609 2 : severity::pointer_t get_severity(message const & msg, std::string const & name)
610 : {
611 2 : auto_add_severities();
612 2 : return get_private_logger(msg)->get_severity(name);
613 : }
614 :
615 :
616 4 : severity::pointer_t get_severity(severity_t sev)
617 : {
618 4 : auto_add_severities();
619 4 : return get_private_logger()->get_severity(sev);
620 : }
621 :
622 :
623 12 : severity::pointer_t get_severity(message const & msg, severity_t sev)
624 : {
625 12 : auto_add_severities();
626 12 : return get_private_logger(msg)->get_severity(sev);
627 : }
628 :
629 :
630 :
631 : } // snaplogger namespace
632 :
633 :
634 : #if defined(__GNUC__) && __GNUC__ >= 7 && __GNUC_MINOR__ >= 5 && __GNUC_PATCHLEVEL__ >= 0
635 1 : snaplogger::severity::pointer_t operator ""_sev (char const * name, unsigned long size)
636 : {
637 1 : return snaplogger::get_severity(std::string(name, size));
638 6 : }
639 : #endif
640 :
641 :
642 : // vim: ts=4 sw=4 et
|