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 Appenders are used to append data to somewhere.
22 : *
23 : * This file declares the base appender class.
24 : */
25 :
26 : // self
27 : //
28 : #include "snaplogger/logger.h"
29 :
30 : #include "snaplogger/console_appender.h"
31 : #include "snaplogger/exception.h"
32 : #include "snaplogger/file_appender.h"
33 : #include "snaplogger/guard.h"
34 : #include "snaplogger/private_logger.h"
35 : #include "snaplogger/syslog_appender.h"
36 :
37 :
38 : // serverplugins
39 : //
40 : #include <serverplugins/paths.h>
41 :
42 :
43 : // last include
44 : //
45 : #include <snapdev/poison.h>
46 :
47 :
48 :
49 : namespace snaplogger
50 : {
51 :
52 :
53 : namespace
54 : {
55 :
56 :
57 :
58 : bool g_first_instance = true;
59 : logger::pointer_t * g_instance = nullptr;
60 2 : std::string g_default_plugin_paths = std::string("/usr/local/lib/snaplogger/plugins:/usr/lib/snaplogger/plugins");
61 :
62 :
63 : struct auto_delete_logger
64 : {
65 2 : ~auto_delete_logger()
66 : {
67 2 : logger::pointer_t * ptr(nullptr);
68 : {
69 4 : guard g;
70 2 : swap(ptr, g_instance);
71 : }
72 2 : if(ptr != nullptr)
73 : {
74 2 : (*ptr)->shutdown();
75 2 : delete ptr;
76 : }
77 2 : }
78 : };
79 :
80 2 : auto_delete_logger g_logger_deleter = auto_delete_logger();
81 :
82 :
83 :
84 : }
85 : // no name namespace
86 :
87 :
88 :
89 2 : logger::logger()
90 : {
91 2 : }
92 :
93 :
94 1 : logger::~logger()
95 : {
96 1 : }
97 :
98 :
99 280987 : logger::pointer_t logger::get_instance()
100 : {
101 561974 : guard g;
102 :
103 280987 : if(g_instance == nullptr)
104 : {
105 2 : if(!g_first_instance)
106 : {
107 0 : throw duplicate_error("preventing an attempt of re-creating the snap logger.");
108 : }
109 :
110 2 : g_first_instance = false;
111 :
112 : // note that we create a `private_logger` object
113 : //
114 2 : g_instance = new logger::pointer_t();
115 2 : g_instance->reset(new private_logger());
116 : }
117 :
118 561974 : return *g_instance;
119 : }
120 :
121 :
122 : /** \brief Reset the logger to its startup state.
123 : *
124 : * This function resets the logger to non-asynchronous and no appenders.
125 : *
126 : * This is mainly used in our unit tests so we do not have to run the
127 : * tests one at a time. It should nearly never be useful in your environment
128 : * except if you do a fork() and wanted the child to have its own special
129 : * log environment.
130 : */
131 16 : void logger::reset()
132 : {
133 32 : guard g;
134 :
135 16 : set_asynchronous(false);
136 16 : f_appenders.clear();
137 16 : f_lowest_severity = severity_t::SEVERITY_OFF;
138 16 : }
139 :
140 :
141 0 : void logger::shutdown()
142 : {
143 0 : }
144 :
145 :
146 1 : std::string const & logger::default_plugin_paths()
147 : {
148 1 : return g_default_plugin_paths;
149 : }
150 :
151 :
152 1 : void logger::load_plugins(std::string const & plugin_paths)
153 : {
154 2 : guard g;
155 :
156 2 : serverplugins::paths paths;
157 1 : paths.add(plugin_paths);
158 :
159 2 : serverplugins::names names(paths);
160 1 : names.find_plugins("snaplogger_");
161 :
162 1 : f_plugins = std::make_shared<serverplugins::collection>(names);
163 1 : f_plugins->load_plugins(shared_from_this());
164 1 : }
165 :
166 :
167 0 : bool logger::is_configured() const
168 : {
169 0 : guard g;
170 :
171 0 : return !f_appenders.empty();
172 : }
173 :
174 :
175 0 : bool logger::has_appender(std::string const & type) const
176 : {
177 0 : return std::find_if(
178 : f_appenders.begin()
179 : , f_appenders.end()
180 0 : , [&type](auto a)
181 0 : {
182 0 : return type == a->get_type();
183 0 : }) != f_appenders.end();
184 : }
185 :
186 :
187 2 : appender::pointer_t logger::get_appender(std::string const & name) const
188 : {
189 2 : auto it(std::find_if(
190 : f_appenders.begin()
191 : , f_appenders.end()
192 1 : , [&name](auto a)
193 1 : {
194 1 : return name == a->get_name();
195 3 : }));
196 2 : if(it == f_appenders.end())
197 : {
198 1 : return appender::pointer_t();
199 : }
200 :
201 1 : return *it;
202 : }
203 :
204 :
205 0 : appender::vector_t logger::get_appenders() const
206 : {
207 0 : return f_appenders;
208 : }
209 :
210 :
211 2 : void logger::set_config(advgetopt::getopt const & params)
212 : {
213 : // The asynchronous flag can cause problems unless the programmer
214 : // specifically planned for it so we do not allow it in configuration
215 : // file at the moment. Later we may have two flags. If both are true
216 : // then we will allow asynchronous logging.
217 : //
218 : //if(params.is_defined("asynchronous"))
219 : //{
220 : // set_asynchronous(params.get_string("asynchronous") == "true");
221 : //}
222 :
223 4 : auto const & sections(params.get_option(advgetopt::CONFIGURATION_SECTIONS));
224 2 : if(sections != nullptr)
225 : {
226 0 : size_t const max(sections->size());
227 0 : for(size_t idx(0); idx < max; ++idx)
228 : {
229 0 : std::string const section_name(sections->get_value(idx));
230 0 : std::string const section_type(section_name + "::type");
231 0 : std::string type;
232 0 : if(params.is_defined(section_type))
233 : {
234 0 : type = params.get_string(section_type);
235 : }
236 : else
237 : {
238 : // try with the name of the section if no type is defined
239 : //
240 0 : type = section_name;
241 : }
242 0 : if(!type.empty())
243 : {
244 0 : appender::pointer_t a(create_appender(type, section_name));
245 0 : if(a != nullptr)
246 : {
247 0 : add_appender(a);
248 : }
249 : // else -- this may be a section which does not represent an appender
250 : }
251 : }
252 : }
253 :
254 4 : guard g;
255 :
256 6 : for(auto a : f_appenders)
257 : {
258 4 : a->set_config(params);
259 : }
260 2 : }
261 :
262 :
263 0 : void logger::reopen()
264 : {
265 0 : guard g;
266 :
267 0 : for(auto a : f_appenders)
268 : {
269 0 : a->reopen();
270 : }
271 0 : }
272 :
273 :
274 23 : void logger::add_appender(appender::pointer_t a)
275 : {
276 46 : guard g;
277 :
278 23 : if(a->unique())
279 : {
280 0 : std::string const type(a->get_type());
281 0 : auto it(std::find_if(
282 : f_appenders.begin()
283 : , f_appenders.end()
284 0 : , [&type](auto app)
285 0 : {
286 0 : return type == app->get_type();
287 0 : }));
288 0 : if(it != f_appenders.end())
289 : {
290 : // the console is a pretty special type because it can't be
291 : // added twice but it may get added early because an error
292 : // occurs and forces initialization of the logger "too soon"
293 : //
294 0 : if(type == "console")
295 : {
296 0 : if(a->get_name() != "console"
297 0 : && (*it)->get_name() == "console")
298 : {
299 0 : (*it)->set_name(a->get_name());
300 : }
301 0 : return;
302 : }
303 0 : if(type == "syslog")
304 : {
305 0 : if(a->get_name() != "syslog"
306 0 : && (*it)->get_name() == "syslog")
307 : {
308 0 : (*it)->set_name(a->get_name());
309 : }
310 0 : return;
311 : }
312 : throw duplicate_error(
313 : "an appender of type \""
314 0 : + type
315 0 : + "\" can only be added once.");
316 : }
317 : }
318 :
319 23 : f_appenders.push_back(a);
320 :
321 23 : severity_changed(a->get_severity());
322 : }
323 :
324 :
325 0 : void logger::add_config(std::string const & config_filename)
326 : {
327 0 : advgetopt::options_environment opt_env;
328 :
329 0 : char const * configuration_files[] =
330 : {
331 0 : config_filename.c_str()
332 : , nullptr
333 0 : };
334 :
335 0 : opt_env.f_project_name = "snaplogger";
336 0 : opt_env.f_environment_variable_name = "SNAPLOGGER";
337 0 : opt_env.f_configuration_files = configuration_files;
338 0 : opt_env.f_environment_flags = advgetopt::GETOPT_ENVIRONMENT_FLAG_DYNAMIC_PARAMETERS;
339 :
340 0 : advgetopt::getopt opts(opt_env);
341 :
342 0 : opts.parse_configuration_files();
343 0 : opts.parse_environment_variable();
344 :
345 0 : set_config(opts);
346 0 : }
347 :
348 :
349 0 : appender::pointer_t logger::add_console_appender()
350 : {
351 0 : appender::pointer_t a(std::make_shared<console_appender>("console"));
352 :
353 0 : advgetopt::options_environment opt_env;
354 0 : opt_env.f_project_name = "snaplogger";
355 0 : opt_env.f_environment_flags = advgetopt::GETOPT_ENVIRONMENT_FLAG_AUTO_DONE;
356 0 : advgetopt::getopt opts(opt_env);
357 0 : a->set_config(opts);
358 :
359 0 : guard g;
360 :
361 0 : add_appender(a);
362 :
363 0 : return a;
364 : }
365 :
366 :
367 0 : appender::pointer_t logger::add_syslog_appender(std::string const & identity)
368 : {
369 0 : appender::pointer_t a(std::make_shared<syslog_appender>("syslog"));
370 :
371 0 : advgetopt::option options[] =
372 : {
373 : advgetopt::define_option(
374 : advgetopt::Name("syslog::identity")
375 : , advgetopt::Flags(advgetopt::command_flags<advgetopt::GETOPT_FLAG_REQUIRED>())
376 : ),
377 : advgetopt::end_options()
378 : };
379 :
380 0 : advgetopt::options_environment opt_env;
381 0 : opt_env.f_project_name = "snaplogger";
382 0 : opt_env.f_environment_flags = advgetopt::GETOPT_ENVIRONMENT_FLAG_AUTO_DONE;
383 0 : opt_env.f_options = options;
384 0 : advgetopt::getopt opts(opt_env);
385 0 : if(!identity.empty())
386 : {
387 0 : opts.get_option("syslog::identity")->set_value(0, identity);
388 : }
389 0 : a->set_config(opts);
390 :
391 0 : guard g;
392 :
393 0 : add_appender(a);
394 :
395 0 : return a;
396 : }
397 :
398 :
399 0 : appender::pointer_t logger::add_file_appender(std::string const & filename)
400 : {
401 0 : file_appender::pointer_t a(std::make_shared<file_appender>("file"));
402 :
403 0 : advgetopt::option options[] =
404 : {
405 : advgetopt::define_option(
406 : advgetopt::Name("file::filename")
407 : , advgetopt::Flags(advgetopt::command_flags<advgetopt::GETOPT_FLAG_REQUIRED>())
408 : ),
409 : advgetopt::end_options()
410 : };
411 :
412 0 : advgetopt::options_environment opt_env;
413 0 : opt_env.f_project_name = "snaplogger";
414 0 : opt_env.f_environment_flags = advgetopt::GETOPT_ENVIRONMENT_FLAG_AUTO_DONE;
415 0 : opt_env.f_options = options;
416 0 : advgetopt::getopt opts(opt_env);
417 0 : if(!filename.empty())
418 : {
419 0 : opts.get_option("file::filename")->set_value(0, filename);
420 : }
421 0 : a->set_config(opts);
422 :
423 0 : guard g;
424 :
425 0 : add_appender(a);
426 :
427 0 : return a;
428 : }
429 :
430 :
431 93388 : severity_t logger::get_lowest_severity() const
432 : {
433 186776 : guard g;
434 :
435 93388 : if(f_appenders.empty())
436 : {
437 : // we do not know the level yet, we do not have the appenders
438 : // yet... so accept anything at this point
439 : //
440 1 : return severity_t::SEVERITY_ALL;
441 : }
442 :
443 93387 : if(f_lowest_replacements.empty())
444 : {
445 93387 : return f_lowest_severity;
446 : }
447 :
448 : // there is not need to build messages that no appenders is going
449 : // to handle, so return the max. between the lowest of all appenders
450 : // and the lowest from the replacements
451 : //
452 0 : return std::max(f_lowest_severity, f_lowest_replacements.back());
453 : }
454 :
455 :
456 : /** \brief Override the lowest severity.
457 : *
458 : * After this call, and until you call the restore_lowest_severity() function,
459 : * the get_lowest_severity() function will return \p severity_level. This
460 : * new security level may be lower or higher than the expected level.
461 : *
462 : * Setting this level to a level lower than the current lowest level is
463 : * not useful. The get_lowest_severity() will still return the
464 : * f_lowest_severiry value in that case. Since it can change dynamically,
465 : * this lowest replacement is still saved as is.
466 : *
467 : * \param[in] severity_level The severity level to use in
468 : * get_lowest_severity() until restore_lower_severity().
469 : */
470 0 : void logger::override_lowest_severity(severity_t severity_level)
471 : {
472 0 : f_lowest_replacements.push_back(severity_level);
473 0 : }
474 :
475 :
476 : /** \brief Cancel one call to the override_lowest_severity().
477 : *
478 : * Each time you call the override_lowest_severity() function, you are
479 : * expected to call restore_lowest_severity() once to cancel the effect.
480 : *
481 : * Call the restore_lowest_severity() too many times is safe. However,
482 : * to make it safe, you are expected to use the override_lowest_severity_level
483 : * class. Create an object of that type. When the object is detroyed, the
484 : * lowest level added gets removed automatically.
485 : */
486 0 : void logger::restore_lowest_severity()
487 : {
488 0 : if(!f_lowest_replacements.empty())
489 : {
490 0 : f_lowest_replacements.pop_back();
491 : }
492 0 : }
493 :
494 :
495 1 : void logger::set_severity(severity_t severity_level)
496 : {
497 2 : guard g;
498 :
499 1 : f_lowest_severity = severity_level;
500 6 : for(auto a : f_appenders)
501 : {
502 5 : a->set_severity(severity_level);
503 : }
504 1 : }
505 :
506 :
507 1 : void logger::reduce_severity(severity_t severity_level)
508 : {
509 3 : for(auto a : f_appenders)
510 : {
511 2 : a->reduce_severity(severity_level);
512 : }
513 1 : }
514 :
515 :
516 315 : void logger::severity_changed(severity_t severity_level)
517 : {
518 630 : guard g;
519 :
520 315 : if(severity_level < f_lowest_severity)
521 : {
522 20 : f_lowest_severity = severity_level;
523 : }
524 295 : else if(severity_level > f_lowest_severity)
525 : {
526 : // if the severity level grew we have to search for the new lowest;
527 : // this happens very rarely while running, it's likely to happen
528 : // up to once per appender on initialization.
529 : //
530 284 : auto const min(std::min_element(f_appenders.begin(), f_appenders.end()));
531 284 : if(min == f_appenders.end())
532 : {
533 : // I don't think this is possible because if there are no appenders
534 : // then we should not even get called; also the new level should
535 : // not be higher if the list is empty
536 : //
537 0 : f_lowest_severity = severity_t::SEVERITY_ALL;
538 : }
539 : else
540 : {
541 284 : f_lowest_severity = (*min)->get_severity();
542 : }
543 : }
544 315 : }
545 :
546 :
547 0 : severity_t logger::get_default_severity() const
548 : {
549 0 : private_logger const * l(dynamic_cast<private_logger const *>(this));
550 0 : severity::pointer_t sev(l->get_default_severity());
551 0 : return sev == nullptr ? severity_t::SEVERITY_DEFAULT : sev->get_severity();
552 : }
553 :
554 :
555 0 : bool logger::set_default_severity(severity_t severity_level)
556 : {
557 0 : private_logger * l(dynamic_cast<private_logger *>(this));
558 0 : if(severity_level == severity_t::SEVERITY_ALL)
559 : {
560 : // reset to default
561 0 : l->set_default_severity(severity::pointer_t());
562 : }
563 : else
564 : {
565 0 : severity::pointer_t sev(l->get_severity(severity_level));
566 0 : if(sev == nullptr)
567 : {
568 0 : return false;
569 : }
570 0 : l->set_default_severity(sev);
571 : }
572 0 : return true;
573 : }
574 :
575 :
576 0 : void logger::add_component_to_include(component::pointer_t comp)
577 : {
578 0 : guard g;
579 :
580 0 : f_components_to_include.insert(comp);
581 0 : }
582 :
583 :
584 0 : void logger::remove_component_to_include(component::pointer_t comp)
585 : {
586 0 : guard g;
587 :
588 0 : f_components_to_include.erase(comp);
589 0 : }
590 :
591 :
592 1 : void logger::add_component_to_ignore(component::pointer_t comp)
593 : {
594 2 : guard g;
595 :
596 1 : f_components_to_ignore.insert(comp);
597 1 : }
598 :
599 :
600 0 : void logger::remove_component_to_ignore(component::pointer_t comp)
601 : {
602 0 : guard g;
603 :
604 0 : f_components_to_ignore.erase(comp);
605 0 : }
606 :
607 :
608 0 : void logger::add_default_field(std::string const & name, std::string const & value)
609 : {
610 0 : if(!name.empty())
611 : {
612 0 : if(name[0] == '_')
613 : {
614 : throw invalid_parameter(
615 : "field name \""
616 0 : + name
617 0 : + "\" is a system name (whether reserved or already defined) and as such is read-only."
618 0 : " Do not start your field names with an underscore (_).");
619 : }
620 0 : if(name == "id")
621 : {
622 : throw invalid_parameter(
623 : "field name \"id\" is automatically set by the message class,"
624 0 : " it cannot be set as a default field.");
625 : }
626 :
627 0 : guard g;
628 :
629 0 : f_default_fields[name] = value;
630 : }
631 0 : }
632 :
633 :
634 0 : std::string logger::get_default_field(std::string const & name) const
635 : {
636 0 : guard g;
637 :
638 0 : auto it(f_default_fields.find(name));
639 0 : if(it != f_default_fields.end())
640 : {
641 0 : return it->second;
642 : }
643 0 : return std::string();
644 : }
645 :
646 :
647 93388 : field_map_t logger::get_default_fields() const
648 : {
649 186776 : guard g;
650 :
651 186776 : return f_default_fields;
652 : }
653 :
654 :
655 0 : void logger::remove_default_field(std::string const & name)
656 : {
657 0 : guard g;
658 :
659 0 : auto it(f_default_fields.find(name));
660 0 : if(it != f_default_fields.end())
661 : {
662 0 : f_default_fields.erase(it);
663 : }
664 0 : }
665 :
666 :
667 0 : bool logger::is_asynchronous() const
668 : {
669 0 : guard g;
670 :
671 0 : return f_asynchronous;
672 : }
673 :
674 :
675 18 : void logger::set_asynchronous(bool status)
676 : {
677 18 : status = status != false;
678 :
679 18 : bool do_delete(false);
680 : {
681 36 : guard g;
682 :
683 18 : if(f_asynchronous != status)
684 : {
685 2 : f_asynchronous = status;
686 2 : if(!f_asynchronous)
687 : {
688 1 : do_delete = true;
689 : }
690 : }
691 : }
692 :
693 18 : if(do_delete)
694 : {
695 1 : private_logger * l(dynamic_cast<private_logger *>(this));
696 1 : l->delete_thread();
697 : }
698 18 : }
699 :
700 :
701 93387 : void logger::log_message(message const & msg)
702 : {
703 93387 : if(const_cast<message &>(msg).tellp() != 0)
704 : {
705 93387 : bool asynchronous(false);
706 : {
707 186774 : guard g;
708 :
709 93387 : if(f_asynchronous)
710 : {
711 2 : message::pointer_t m(std::make_shared<message>(msg, msg));
712 1 : private_logger * l(dynamic_cast<private_logger *>(this));
713 1 : l->send_message_to_thread(m);
714 1 : asynchronous = true;
715 : }
716 : }
717 :
718 93387 : if(!asynchronous)
719 : {
720 93386 : process_message(msg);
721 : }
722 : }
723 :
724 186762 : if(f_fatal_severity != severity_t::SEVERITY_OFF
725 93381 : && msg.get_severity() >= f_fatal_severity)
726 : {
727 0 : call_fatal_error_callback();
728 0 : throw fatal_error("A fatal error occurred.");
729 : }
730 93381 : }
731 :
732 :
733 93387 : void logger::process_message(message const & msg)
734 : {
735 186772 : appender::vector_t appenders;
736 :
737 : {
738 186772 : guard g;
739 :
740 93387 : bool include(f_components_to_include.empty());
741 93387 : component::set_t const & components(msg.get_components());
742 93387 : if(components.empty())
743 : {
744 93378 : if(f_components_to_ignore.find(f_normal_component) != f_components_to_ignore.end())
745 : {
746 0 : return;
747 : }
748 93378 : if(!include)
749 : {
750 0 : if(f_components_to_include.find(f_normal_component) != f_components_to_include.end())
751 : {
752 0 : include = true;
753 : }
754 : }
755 : }
756 : else
757 : {
758 22 : for(auto c : components)
759 : {
760 15 : if(f_components_to_ignore.find(c) != f_components_to_ignore.end())
761 : {
762 2 : return;
763 : }
764 13 : if(!include)
765 : {
766 0 : if(f_components_to_include.find(c) != f_components_to_include.end())
767 : {
768 0 : include = true;
769 : }
770 : }
771 : }
772 : }
773 93385 : if(!include)
774 : {
775 0 : return;
776 : }
777 :
778 93385 : if(f_appenders.empty())
779 : {
780 0 : if(isatty(fileno(stdout)))
781 : {
782 0 : add_console_appender();
783 : }
784 : else
785 : {
786 0 : add_syslog_appender(std::string());
787 : }
788 : }
789 :
790 93385 : ++f_severity_stats[static_cast<std::size_t>(msg.get_severity())];
791 :
792 93385 : appenders = f_appenders;
793 : }
794 :
795 186822 : for(auto a : appenders)
796 : {
797 93437 : a->send_message(msg);
798 : }
799 : }
800 :
801 :
802 0 : void logger::set_fatal_error_callback(std::function<void(void)> & f)
803 : {
804 0 : f_fatal_error_callback = f;
805 0 : }
806 :
807 :
808 0 : void logger::call_fatal_error_callback()
809 : {
810 0 : if(f_fatal_error_callback != nullptr)
811 : {
812 0 : f_fatal_error_callback();
813 : }
814 0 : }
815 :
816 :
817 : /** \brief Return statistics about log severities.
818 : *
819 : * This function returns the statistics counting each message sent per
820 : * severity.
821 : *
822 : * If you enabled the asynchronous functionality of the snaplogger,
823 : * then this statistics may not reflect the current state as the
824 : * logger thread may still not have processed all the messages.
825 : *
826 : * \note
827 : * The severity_stats_t type is a vector that includes all possible
828 : * severity levels (0 to 255), including severity levels that are not
829 : * currently declared. It is done that way so the access is as fast
830 : * as possible when we want to increment one of the stats. Using a
831 : * map would have a much greater impact on the process_message()
832 : * function.
833 : *
834 : * \return a copy of the severity statistics at the time of the call.
835 : */
836 0 : severity_stats_t logger::get_severity_stats() const
837 : {
838 0 : guard g;
839 :
840 0 : return f_severity_stats;
841 : }
842 :
843 :
844 0 : bool is_configured()
845 : {
846 0 : guard g;
847 :
848 0 : if(g_instance == nullptr)
849 : {
850 0 : return false;
851 : }
852 :
853 0 : return (*g_instance)->is_configured();
854 : }
855 :
856 :
857 0 : bool has_appender(std::string const & type)
858 : {
859 0 : guard g;
860 :
861 0 : if(g_instance == nullptr)
862 : {
863 0 : return false;
864 : }
865 :
866 0 : return (*g_instance)->has_appender(type);
867 : }
868 :
869 :
870 0 : void reopen()
871 : {
872 0 : guard g;
873 :
874 0 : if(g_instance == nullptr)
875 : {
876 0 : return;
877 : }
878 :
879 0 : (*g_instance)->reopen();
880 : }
881 :
882 :
883 0 : bool configure_console(bool force)
884 : {
885 0 : bool result(!is_configured() || (force && !has_appender("console")));
886 0 : if(result)
887 : {
888 0 : logger::get_instance()->add_console_appender();
889 : }
890 :
891 0 : return result;
892 : }
893 :
894 :
895 0 : bool configure_syslog(std::string const & identity)
896 : {
897 0 : bool result(!is_configured());
898 0 : if(result)
899 : {
900 0 : logger::get_instance()->add_syslog_appender(identity);
901 : }
902 :
903 0 : return result;
904 : }
905 :
906 :
907 0 : bool configure_file(std::string const & filename)
908 : {
909 0 : bool result(!is_configured());
910 0 : if(result)
911 : {
912 0 : logger::get_instance()->add_file_appender(filename);
913 : }
914 :
915 0 : return result;
916 : }
917 :
918 :
919 0 : bool configure_config(std::string const & config_filename)
920 : {
921 0 : bool result(!is_configured());
922 0 : if(result)
923 : {
924 0 : logger::get_instance()->add_config(config_filename);
925 : }
926 :
927 0 : return result;
928 : }
929 :
930 :
931 :
932 :
933 6 : } // snaplogger namespace
934 : // vim: ts=4 sw=4 et
|