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