Line data Source code
1 : /*
2 : * Copyright (c) 2013-2019 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 Appenders are used to append data to somewhere.
24 : *
25 : * This file declares the base appender class.
26 : */
27 :
28 : // self
29 : //
30 : #include "snaplogger/logger.h"
31 :
32 : #include "snaplogger/console_appender.h"
33 : #include "snaplogger/exception.h"
34 : #include "snaplogger/file_appender.h"
35 : #include "snaplogger/guard.h"
36 : #include "snaplogger/private_logger.h"
37 : #include "snaplogger/syslog_appender.h"
38 :
39 :
40 : // last include
41 : //
42 : #include <snapdev/poison.h>
43 :
44 :
45 :
46 : namespace snaplogger
47 : {
48 :
49 :
50 : namespace
51 : {
52 :
53 :
54 :
55 : bool g_first_instance = true;
56 : logger::pointer_t * g_instance = nullptr;
57 :
58 :
59 : struct auto_delete_logger
60 : {
61 2 : ~auto_delete_logger()
62 : {
63 2 : logger::pointer_t * ptr(nullptr);
64 : {
65 4 : guard g;
66 2 : swap(ptr, g_instance);
67 : }
68 2 : if(ptr != nullptr)
69 : {
70 2 : (*ptr)->shutdown();
71 2 : delete ptr;
72 : }
73 2 : }
74 : };
75 :
76 2 : auto_delete_logger g_logger_deleter = auto_delete_logger();
77 :
78 :
79 :
80 : }
81 : // no name namespace
82 :
83 :
84 :
85 2 : logger::logger()
86 : {
87 2 : }
88 :
89 :
90 2 : logger::~logger()
91 : {
92 2 : }
93 :
94 :
95 289865 : logger::pointer_t logger::get_instance()
96 : {
97 579730 : guard g;
98 :
99 289865 : if(g_instance == nullptr)
100 : {
101 2 : if(!g_first_instance)
102 : {
103 0 : throw duplicate_error("preventing an attempt of re-creating the snap logger.");
104 : }
105 :
106 2 : g_first_instance = false;
107 :
108 : // note that we create a `private_logger` object
109 : //
110 2 : g_instance = new logger::pointer_t();
111 2 : g_instance->reset(new private_logger());
112 : }
113 :
114 579730 : return *g_instance;
115 : }
116 :
117 :
118 : /** \brief Reset the logger to its startup state.
119 : *
120 : * This function resets the logger to non-asynchronous and no appenders.
121 : *
122 : * This is mainly used in our unit tests so we do not have to run the
123 : * tests one at a time. It should nearly never be useful in your environment
124 : * except if you do a fork() and wanted the child to have its own special
125 : * log environment.
126 : */
127 13 : void logger::reset()
128 : {
129 26 : guard g;
130 :
131 13 : set_asynchronous(false);
132 13 : f_appenders.clear();
133 13 : f_lowest_severity = severity_t::SEVERITY_OFF;
134 13 : }
135 :
136 :
137 0 : void logger::shutdown()
138 : {
139 0 : }
140 :
141 :
142 0 : bool logger::is_configured() const
143 : {
144 0 : guard g;
145 :
146 0 : return !f_appenders.empty();
147 : }
148 :
149 :
150 0 : bool logger::has_appender(std::string const & type) const
151 : {
152 0 : return std::find_if(
153 : f_appenders.begin()
154 : , f_appenders.end()
155 0 : , [&type](auto a)
156 0 : {
157 0 : return type == a->get_type();
158 0 : }) != f_appenders.end();
159 : }
160 :
161 :
162 2 : appender::pointer_t logger::get_appender(std::string const & name) const
163 : {
164 : auto it(std::find_if(
165 : f_appenders.begin()
166 : , f_appenders.end()
167 1 : , [&name](auto a)
168 1 : {
169 1 : return name == a->get_name();
170 3 : }));
171 2 : if(it == f_appenders.end())
172 : {
173 1 : return appender::pointer_t();
174 : }
175 :
176 1 : return *it;
177 : }
178 :
179 :
180 0 : void logger::set_config(advgetopt::getopt const & params)
181 : {
182 0 : if(params.is_defined("asynchronous"))
183 : {
184 0 : set_asynchronous(params.get_string("asynchronous") == "true");
185 : }
186 :
187 0 : std::string const name(advgetopt::CONFIGURATION_SECTIONS);
188 0 : auto const & sections(params.get_option(name));
189 0 : if(sections != nullptr)
190 : {
191 0 : size_t const max(sections->size());
192 0 : for(size_t idx(0); idx < max; ++idx)
193 : {
194 0 : std::string const section_name(sections->get_value(idx));
195 0 : std::string const section_type(section_name + "::type");
196 0 : std::string type;
197 0 : if(params.is_defined(section_type))
198 : {
199 0 : type = params.get_string(section_type);
200 : }
201 : else
202 : {
203 : // try with the name of the section if no type is defined
204 : //
205 0 : type = section_name;
206 : }
207 0 : if(!type.empty())
208 : {
209 0 : appender::pointer_t a(create_appender(type, section_name));
210 0 : if(a != nullptr)
211 : {
212 0 : add_appender(a);
213 : }
214 : // else -- this may be a section which does not represent an appender
215 : }
216 : }
217 : }
218 :
219 0 : guard g;
220 :
221 0 : for(auto a : f_appenders)
222 : {
223 0 : a->set_config(params);
224 : }
225 0 : }
226 :
227 :
228 0 : void logger::reopen()
229 : {
230 0 : guard g;
231 :
232 0 : for(auto a : f_appenders)
233 : {
234 0 : a->reopen();
235 : }
236 0 : }
237 :
238 :
239 15 : void logger::add_appender(appender::pointer_t a)
240 : {
241 30 : guard g;
242 :
243 15 : if(a->unique())
244 : {
245 0 : std::string const type(a->get_type());
246 : auto it(std::find_if(
247 : f_appenders.begin()
248 : , f_appenders.end()
249 0 : , [&type](auto app)
250 0 : {
251 0 : return type == app->get_type();
252 0 : }));
253 0 : if(it != f_appenders.end())
254 : {
255 : throw duplicate_error(
256 : "an appender of type \""
257 0 : + type
258 0 : + "\" can only be added once.");
259 : }
260 : }
261 :
262 15 : f_appenders.push_back(a);
263 :
264 15 : severity_changed(a->get_severity());
265 15 : }
266 :
267 :
268 0 : void logger::add_config(std::string const & config_filename)
269 : {
270 0 : advgetopt::options_environment opt_env;
271 :
272 : char const * configuration_files[] =
273 : {
274 0 : config_filename.c_str()
275 : , nullptr
276 0 : };
277 :
278 0 : opt_env.f_project_name = "logger";
279 0 : opt_env.f_environment_variable_name = "SNAPLOGGER";
280 0 : opt_env.f_configuration_files = configuration_files;
281 0 : opt_env.f_environment_flags = advgetopt::GETOPT_ENVIRONMENT_FLAG_DYNAMIC_PARAMETERS;
282 :
283 0 : advgetopt::getopt opts(opt_env);
284 :
285 0 : opts.parse_configuration_files();
286 0 : opts.parse_environment_variable();
287 :
288 0 : set_config(opts);
289 0 : }
290 :
291 :
292 0 : appender::pointer_t logger::add_console_appender()
293 : {
294 0 : appender::pointer_t a(std::make_shared<console_appender>("console"));
295 :
296 0 : advgetopt::options_environment opt_env;
297 0 : opt_env.f_project_name = "logger";
298 0 : advgetopt::getopt opts(opt_env);
299 0 : a->set_config(opts);
300 :
301 0 : guard g;
302 :
303 0 : add_appender(a);
304 :
305 0 : return a;
306 : }
307 :
308 :
309 0 : appender::pointer_t logger::add_syslog_appender(std::string const & identity)
310 : {
311 0 : appender::pointer_t a(std::make_shared<syslog_appender>("syslog"));
312 :
313 : advgetopt::option options[] =
314 : {
315 : advgetopt::define_option(
316 : advgetopt::Name("syslog::identity")
317 : , advgetopt::Flags(advgetopt::command_flags<advgetopt::GETOPT_FLAG_REQUIRED>())
318 : ),
319 : advgetopt::end_options()
320 0 : };
321 :
322 0 : advgetopt::options_environment opt_env;
323 0 : opt_env.f_project_name = "logger";
324 0 : opt_env.f_options = options;
325 0 : advgetopt::getopt opts(opt_env);
326 0 : if(!identity.empty())
327 : {
328 0 : opts.get_option("syslog::identity")->set_value(0, identity);
329 : }
330 0 : a->set_config(opts);
331 :
332 0 : guard g;
333 :
334 0 : add_appender(a);
335 :
336 0 : return a;
337 : }
338 :
339 :
340 0 : appender::pointer_t logger::add_file_appender(std::string const & filename)
341 : {
342 0 : file_appender::pointer_t a(std::make_shared<file_appender>("file"));
343 :
344 : advgetopt::option options[] =
345 : {
346 : advgetopt::define_option(
347 : advgetopt::Name("file::filename")
348 : , advgetopt::Flags(advgetopt::command_flags<advgetopt::GETOPT_FLAG_REQUIRED>())
349 : ),
350 : advgetopt::end_options()
351 0 : };
352 :
353 0 : advgetopt::options_environment opt_env;
354 0 : opt_env.f_project_name = "logger";
355 0 : opt_env.f_options = options;
356 0 : advgetopt::getopt opts(opt_env);
357 0 : if(!filename.empty())
358 : {
359 0 : opts.get_option("file::filename")->set_value(0, filename);
360 : }
361 0 : a->set_config(opts);
362 :
363 0 : guard g;
364 :
365 0 : add_appender(a);
366 :
367 0 : return a;
368 : }
369 :
370 :
371 96407 : severity_t logger::get_lowest_severity() const
372 : {
373 192814 : guard g;
374 :
375 192814 : return f_lowest_severity;
376 : }
377 :
378 :
379 0 : void logger::set_severity(severity_t severity_level)
380 : {
381 0 : guard g;
382 :
383 0 : f_lowest_severity = severity_level;
384 0 : for(auto a : f_appenders)
385 : {
386 0 : a->set_severity(severity_level);
387 : }
388 0 : }
389 :
390 :
391 0 : void logger::reduce_severity(severity_t severity_level)
392 : {
393 0 : for(auto a : f_appenders)
394 : {
395 0 : a->reduce_severity(severity_level);
396 : }
397 0 : }
398 :
399 :
400 304 : void logger::severity_changed(severity_t severity_level)
401 : {
402 608 : guard g;
403 :
404 304 : if(severity_level < f_lowest_severity)
405 : {
406 16 : f_lowest_severity = severity_level;
407 : }
408 288 : else if(severity_level > f_lowest_severity)
409 : {
410 : // if the severity level grew we have to search for the new lowest;
411 : // this happens very rarely while running, it's likely to happen
412 : // up to once per appender on initialization.
413 : //
414 287 : auto minmax(std::minmax_element(f_appenders.begin(), f_appenders.end()));
415 287 : f_lowest_severity = (*minmax.first)->get_severity();
416 : }
417 304 : }
418 :
419 :
420 0 : void logger::add_component_to_include(component::pointer_t comp)
421 : {
422 0 : guard g;
423 :
424 0 : f_components_to_include.insert(comp);
425 0 : }
426 :
427 :
428 0 : void logger::add_component_to_ignore(component::pointer_t comp)
429 : {
430 0 : guard g;
431 :
432 0 : f_components_to_ignore.insert(comp);
433 0 : }
434 :
435 :
436 0 : bool logger::is_asynchronous() const
437 : {
438 0 : guard g;
439 :
440 0 : return f_asynchronous;
441 : }
442 :
443 :
444 15 : void logger::set_asynchronous(bool status)
445 : {
446 15 : status = status != false;
447 :
448 15 : bool do_delete(false);
449 : {
450 30 : guard g;
451 :
452 15 : if(f_asynchronous != status)
453 : {
454 2 : f_asynchronous = status;
455 2 : if(!f_asynchronous)
456 : {
457 1 : do_delete = true;
458 : }
459 : }
460 : }
461 :
462 15 : if(do_delete)
463 : {
464 1 : private_logger * l(dynamic_cast<private_logger *>(this));
465 1 : l->delete_thread();
466 : }
467 15 : }
468 :
469 :
470 96406 : void logger::log_message(message const & msg)
471 : {
472 96406 : if(const_cast<message &>(msg).tellp() != 0)
473 : {
474 96406 : bool asynchronous(false);
475 : {
476 192812 : guard g;
477 :
478 96406 : if(f_asynchronous)
479 : {
480 2 : message::pointer_t m(std::make_shared<message>(msg, msg));
481 1 : private_logger * l(dynamic_cast<private_logger *>(this));
482 1 : l->send_message_to_thread(m);
483 1 : asynchronous = true;
484 : }
485 : }
486 :
487 96406 : if(!asynchronous)
488 : {
489 96405 : process_message(msg);
490 : }
491 : }
492 :
493 192808 : if(f_fatal_severity != severity_t::SEVERITY_OFF
494 96404 : && msg.get_severity() >= f_fatal_severity)
495 : {
496 0 : call_fatal_error_callback();
497 0 : throw fatal_error("A fatal error occurred.");
498 : }
499 96404 : }
500 :
501 :
502 96406 : void logger::process_message(message const & msg)
503 : {
504 192812 : appender::vector_t appenders;
505 :
506 : {
507 192812 : guard g;
508 :
509 96406 : bool include(f_components_to_include.empty());
510 96406 : component::set_t const & components(msg.get_components());
511 96406 : if(components.empty())
512 : {
513 96402 : if(f_components_to_ignore.find(f_normal_component) != f_components_to_ignore.end())
514 : {
515 0 : return;
516 : }
517 96402 : if(!include)
518 : {
519 0 : if(f_components_to_include.find(f_normal_component) != f_components_to_include.end())
520 : {
521 0 : include = true;
522 : }
523 : }
524 : }
525 : else
526 : {
527 8 : for(auto c : components)
528 : {
529 4 : if(f_components_to_ignore.find(c) != f_components_to_ignore.end())
530 : {
531 0 : return;
532 : }
533 4 : if(!include)
534 : {
535 0 : if(f_components_to_include.find(c) != f_components_to_include.end())
536 : {
537 0 : include = true;
538 : }
539 : }
540 : }
541 : }
542 96406 : if(!include)
543 : {
544 0 : return;
545 : }
546 :
547 96406 : if(f_appenders.empty())
548 : {
549 0 : if(isatty(fileno(stdout)))
550 : {
551 0 : add_console_appender();
552 : }
553 : else
554 : {
555 0 : add_syslog_appender(std::string());
556 : }
557 : }
558 :
559 96406 : appenders = f_appenders;
560 : }
561 :
562 192817 : for(auto a : appenders)
563 : {
564 96411 : a->send_message(msg);
565 : }
566 : }
567 :
568 :
569 0 : void logger::set_fatal_error_callback(std::function<void(void)> & f)
570 : {
571 0 : f_fatal_error_callback = f;
572 0 : }
573 :
574 :
575 0 : void logger::call_fatal_error_callback()
576 : {
577 0 : if(f_fatal_error_callback != nullptr)
578 : {
579 0 : f_fatal_error_callback();
580 : }
581 0 : }
582 :
583 :
584 0 : bool is_configured()
585 : {
586 0 : guard g;
587 :
588 0 : if(g_instance == nullptr)
589 : {
590 0 : return false;
591 : }
592 :
593 0 : return (*g_instance)->is_configured();
594 : }
595 :
596 :
597 0 : bool has_appender(std::string const & type)
598 : {
599 0 : guard g;
600 :
601 0 : if(g_instance == nullptr)
602 : {
603 0 : return false;
604 : }
605 :
606 0 : return (*g_instance)->has_appender(type);
607 : }
608 :
609 :
610 0 : void reopen()
611 : {
612 0 : guard g;
613 :
614 0 : if(g_instance == nullptr)
615 : {
616 0 : return;
617 : }
618 :
619 0 : (*g_instance)->reopen();
620 : }
621 :
622 :
623 0 : bool configure_console(bool force)
624 : {
625 0 : bool result(!is_configured() || (force && !has_appender("console")));
626 0 : if(result)
627 : {
628 0 : logger::get_instance()->add_console_appender();
629 : }
630 :
631 0 : return result;
632 : }
633 :
634 :
635 0 : bool configure_syslog(std::string const & identity)
636 : {
637 0 : bool result(!is_configured());
638 0 : if(result)
639 : {
640 0 : logger::get_instance()->add_syslog_appender(identity);
641 : }
642 :
643 0 : return result;
644 : }
645 :
646 :
647 0 : bool configure_file(std::string const & filename)
648 : {
649 0 : bool result(!is_configured());
650 0 : if(result)
651 : {
652 0 : logger::get_instance()->add_file_appender(filename);
653 : }
654 :
655 0 : return result;
656 : }
657 :
658 :
659 0 : bool configure_config(std::string const & config_filename)
660 : {
661 0 : bool result(!is_configured());
662 0 : if(result)
663 : {
664 0 : logger::get_instance()->add_config(config_filename);
665 : }
666 :
667 0 : return result;
668 : }
669 :
670 :
671 :
672 :
673 6 : } // snaplogger namespace
674 : // vim: ts=4 sw=4 et
|