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