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