Line data Source code
1 : /*
2 : * License:
3 : * Copyright (c) 2013-2019 Made to Order Software Corp. All Rights Reserved
4 : *
5 : * https://snapwebsites.org/
6 : * contact@m2osw.com
7 : *
8 : * This program is free software; you can redistribute it and/or modify
9 : * it under the terms of the GNU General Public License as published by
10 : * the Free Software Foundation; either version 2 of the License, or
11 : * (at your option) any later version.
12 : *
13 : * This program is distributed in the hope that it will be useful,
14 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 : * GNU General Public License for more details.
17 : *
18 : * You should have received a copy of the GNU General Public License along
19 : * with this program; if not, write to the Free Software Foundation, Inc.,
20 : * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 : *
22 : * Authors:
23 : * Alexis Wilke alexis@m2osw.com
24 : */
25 :
26 : /** \file
27 : * \brief Appenders are used to append data to somewhere.
28 : *
29 : * This file declares the base appender class.
30 : */
31 :
32 : // self
33 : //
34 : #include "snaplogger/logger.h"
35 :
36 : #include "snaplogger/console_appender.h"
37 : #include "snaplogger/exception.h"
38 : #include "snaplogger/file_appender.h"
39 : #include "snaplogger/guard.h"
40 : #include "snaplogger/private_logger.h"
41 : #include "snaplogger/syslog_appender.h"
42 :
43 :
44 : // last include
45 : //
46 : #include <snapdev/poison.h>
47 :
48 :
49 :
50 : namespace snaplogger
51 : {
52 :
53 :
54 : namespace
55 : {
56 :
57 :
58 :
59 : bool g_first_instance = true;
60 : logger::pointer_t * g_instance = nullptr;
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 2 : logger::~logger()
95 : {
96 2 : }
97 :
98 :
99 131466 : logger::pointer_t logger::get_instance()
100 : {
101 262932 : guard g;
102 :
103 131466 : 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 262932 : 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 3 : void logger::reset()
132 : {
133 6 : guard g;
134 :
135 3 : set_asynchronous(false);
136 3 : f_appenders.clear();
137 3 : f_lowest_severity = severity_t::SEVERITY_OFF;
138 3 : }
139 :
140 :
141 0 : void logger::shutdown()
142 : {
143 0 : }
144 :
145 :
146 0 : bool logger::is_configured() const
147 : {
148 0 : guard g;
149 :
150 0 : return !f_appenders.empty();
151 : }
152 :
153 :
154 0 : bool logger::has_appender(std::string const & type) const
155 : {
156 0 : return std::find_if(
157 : f_appenders.begin()
158 : , f_appenders.end()
159 0 : , [&type](auto a)
160 0 : {
161 0 : return type == a->get_type();
162 0 : }) != f_appenders.end();
163 : }
164 :
165 :
166 0 : void logger::set_config(advgetopt::getopt const & params)
167 : {
168 0 : if(params.is_defined("asynchronous"))
169 : {
170 0 : set_asynchronous(params.get_string("asynchronous") == "true");
171 : }
172 :
173 0 : std::string const name(advgetopt::CONFIGURATION_SECTIONS);
174 0 : auto const & sections(params.get_option(name));
175 0 : if(sections != nullptr)
176 : {
177 0 : size_t const max(sections->size());
178 0 : for(size_t idx(0); idx < max; ++idx)
179 : {
180 0 : std::string const section_name(sections->get_value(idx));
181 0 : std::string const section_type(section_name + "::type");
182 0 : std::string type;
183 0 : if(params.is_defined(section_type))
184 : {
185 0 : type = params.get_string(section_type);
186 : }
187 : else
188 : {
189 : // try with the name of the section if no type is defined
190 : //
191 0 : type = section_name;
192 : }
193 0 : if(!type.empty())
194 : {
195 0 : appender::pointer_t a(create_appender(type, section_name));
196 0 : if(a != nullptr)
197 : {
198 0 : add_appender(a);
199 : }
200 : // else -- this may be a section which does not represent an appender
201 : }
202 : }
203 : }
204 :
205 0 : guard g;
206 :
207 0 : for(auto a : f_appenders)
208 : {
209 0 : a->set_config(params);
210 : }
211 0 : }
212 :
213 :
214 0 : void logger::reopen()
215 : {
216 0 : guard g;
217 :
218 0 : for(auto a : f_appenders)
219 : {
220 0 : a->reopen();
221 : }
222 0 : }
223 :
224 :
225 3 : void logger::add_appender(appender::pointer_t a)
226 : {
227 6 : guard g;
228 :
229 3 : if(a->unique())
230 : {
231 0 : std::string const type(a->get_type());
232 : auto it(std::find_if(
233 : f_appenders.begin()
234 : , f_appenders.end()
235 0 : , [&type](auto app)
236 0 : {
237 0 : return type == app->get_type();
238 0 : }));
239 0 : if(it != f_appenders.end())
240 : {
241 : throw duplicate_error(
242 : "an appender of type \""
243 0 : + type
244 0 : + "\" can only be added once.");
245 : }
246 : }
247 :
248 3 : f_appenders.push_back(a);
249 :
250 3 : severity_changed(a->get_severity());
251 3 : }
252 :
253 :
254 0 : void logger::add_config(std::string const & config_filename)
255 : {
256 0 : advgetopt::options_environment opt_env;
257 :
258 : char const * configuration_files[] =
259 : {
260 0 : config_filename.c_str()
261 : , nullptr
262 0 : };
263 :
264 0 : opt_env.f_project_name = "logger";
265 0 : opt_env.f_environment_variable_name = "SNAPLOGGER";
266 0 : opt_env.f_configuration_files = configuration_files;
267 0 : opt_env.f_environment_flags = advgetopt::GETOPT_ENVIRONMENT_FLAG_DYNAMIC_PARAMETERS;
268 :
269 0 : advgetopt::getopt opts(opt_env);
270 :
271 0 : opts.parse_configuration_files();
272 0 : opts.parse_environment_variable();
273 :
274 0 : set_config(opts);
275 0 : }
276 :
277 :
278 0 : appender::pointer_t logger::add_console_appender()
279 : {
280 0 : appender::pointer_t a(std::make_shared<console_appender>("console"));
281 :
282 0 : advgetopt::options_environment opt_env;
283 0 : opt_env.f_project_name = "logger";
284 0 : advgetopt::getopt opts(opt_env);
285 0 : a->set_config(opts);
286 :
287 0 : guard g;
288 :
289 0 : add_appender(a);
290 :
291 0 : return a;
292 : }
293 :
294 :
295 0 : appender::pointer_t logger::add_syslog_appender(std::string const & identity)
296 : {
297 0 : appender::pointer_t a(std::make_shared<syslog_appender>("syslog"));
298 :
299 : advgetopt::option options[] =
300 : {
301 : advgetopt::define_option(
302 : advgetopt::Name("syslog::identity")
303 : , advgetopt::Flags(advgetopt::command_flags<advgetopt::GETOPT_FLAG_REQUIRED>())
304 : ),
305 : advgetopt::end_options()
306 0 : };
307 :
308 0 : advgetopt::options_environment opt_env;
309 0 : opt_env.f_project_name = "logger";
310 0 : opt_env.f_options = options;
311 0 : advgetopt::getopt opts(opt_env);
312 0 : if(!identity.empty())
313 : {
314 0 : opts.get_option("syslog::identity")->set_value(0, identity);
315 : }
316 0 : a->set_config(opts);
317 :
318 0 : guard g;
319 :
320 0 : add_appender(a);
321 :
322 0 : return a;
323 : }
324 :
325 :
326 0 : appender::pointer_t logger::add_file_appender(std::string const & filename)
327 : {
328 0 : file_appender::pointer_t a(std::make_shared<file_appender>("file"));
329 :
330 : advgetopt::option options[] =
331 : {
332 : advgetopt::define_option(
333 : advgetopt::Name("file::filename")
334 : , advgetopt::Flags(advgetopt::command_flags<advgetopt::GETOPT_FLAG_REQUIRED>())
335 : ),
336 : advgetopt::end_options()
337 0 : };
338 :
339 0 : advgetopt::options_environment opt_env;
340 0 : opt_env.f_project_name = "logger";
341 0 : opt_env.f_options = options;
342 0 : advgetopt::getopt opts(opt_env);
343 0 : if(!filename.empty())
344 : {
345 0 : opts.get_option("file::filename")->set_value(0, filename);
346 : }
347 0 : a->set_config(opts);
348 :
349 0 : guard g;
350 :
351 0 : add_appender(a);
352 :
353 0 : return a;
354 : }
355 :
356 :
357 65541 : severity_t logger::get_lowest_severity() const
358 : {
359 131082 : guard g;
360 :
361 131082 : return f_lowest_severity;
362 : }
363 :
364 :
365 0 : void logger::set_severity(severity_t severity_level)
366 : {
367 0 : guard g;
368 :
369 0 : f_lowest_severity = severity_level;
370 0 : for(auto a : f_appenders)
371 : {
372 0 : a->set_severity(severity_level);
373 : }
374 0 : }
375 :
376 :
377 0 : void logger::reduce_severity(severity_t severity_level)
378 : {
379 0 : for(auto a : f_appenders)
380 : {
381 0 : a->reduce_severity(severity_level);
382 : }
383 0 : }
384 :
385 :
386 259 : void logger::severity_changed(severity_t severity_level)
387 : {
388 518 : guard g;
389 :
390 259 : if(severity_level < f_lowest_severity)
391 : {
392 4 : f_lowest_severity = severity_level;
393 : }
394 255 : else if(severity_level > f_lowest_severity)
395 : {
396 : // if the severity level grew we have to search for the new lowest;
397 : // this happens very rarely while running, it's likely to happen
398 : // up to once per appender on initialization.
399 : //
400 255 : auto minmax(std::minmax_element(f_appenders.begin(), f_appenders.end()));
401 255 : f_lowest_severity = (*minmax.first)->get_severity();
402 : }
403 259 : }
404 :
405 :
406 0 : void logger::add_component_to_include(component::pointer_t comp)
407 : {
408 0 : guard g;
409 :
410 0 : f_components_to_include.insert(comp);
411 0 : }
412 :
413 :
414 0 : void logger::add_component_to_ignore(component::pointer_t comp)
415 : {
416 0 : guard g;
417 :
418 0 : f_components_to_ignore.insert(comp);
419 0 : }
420 :
421 :
422 0 : bool logger::is_asynchronous() const
423 : {
424 0 : guard g;
425 :
426 0 : return f_asynchronous;
427 : }
428 :
429 :
430 3 : void logger::set_asynchronous(bool status)
431 : {
432 3 : status = status != false;
433 :
434 3 : bool do_delete(false);
435 : {
436 6 : guard g;
437 :
438 3 : if(f_asynchronous != status)
439 : {
440 0 : f_asynchronous = status;
441 0 : if(!f_asynchronous)
442 : {
443 0 : do_delete = true;
444 : }
445 : }
446 : }
447 :
448 3 : if(do_delete)
449 : {
450 0 : private_logger * l(dynamic_cast<private_logger *>(this));
451 0 : l->delete_thread();
452 : }
453 3 : }
454 :
455 :
456 65541 : void logger::log_message(message const & msg)
457 : {
458 65541 : if(const_cast<message &>(msg).tellp() != 0)
459 : {
460 65541 : bool asynchronous(false);
461 : {
462 131082 : guard g;
463 :
464 65541 : if(f_asynchronous)
465 : {
466 0 : message::pointer_t m(std::make_shared<message>(msg, msg));
467 0 : private_logger * l(dynamic_cast<private_logger *>(this));
468 0 : l->send_message_to_thread(m);
469 0 : asynchronous = true;
470 : }
471 : }
472 :
473 65541 : if(!asynchronous)
474 : {
475 65541 : process_message(msg);
476 : }
477 : }
478 :
479 131082 : if(f_fatal_severity != severity_t::SEVERITY_OFF
480 65541 : && msg.get_severity() >= f_fatal_severity)
481 : {
482 0 : throw fatal_error("A fatal error occurred.");
483 : }
484 65541 : }
485 :
486 :
487 65541 : void logger::process_message(message const & msg)
488 : {
489 :
490 131082 : appender::vector_t appenders;
491 :
492 : {
493 131082 : guard g;
494 :
495 65541 : bool include(f_components_to_include.empty());
496 65541 : component::set_t const & components(msg.get_components());
497 65541 : if(components.empty())
498 : {
499 65541 : if(f_components_to_ignore.find(f_normal_component) != f_components_to_ignore.end())
500 : {
501 0 : return;
502 : }
503 65541 : if(!include)
504 : {
505 0 : if(f_components_to_include.find(f_normal_component) != f_components_to_include.end())
506 : {
507 0 : include = true;
508 : }
509 : }
510 : }
511 : else
512 : {
513 0 : for(auto c : components)
514 : {
515 0 : if(f_components_to_ignore.find(c) != f_components_to_ignore.end())
516 : {
517 0 : return;
518 : }
519 0 : if(!include)
520 : {
521 0 : if(f_components_to_include.find(c) != f_components_to_include.end())
522 : {
523 0 : include = true;
524 : }
525 : }
526 : }
527 : }
528 65541 : if(!include)
529 : {
530 0 : return;
531 : }
532 :
533 65541 : if(f_appenders.empty())
534 : {
535 0 : if(isatty(fileno(stdout)))
536 : {
537 0 : add_console_appender();
538 : }
539 : else
540 : {
541 0 : add_syslog_appender(std::string());
542 : }
543 : }
544 :
545 65541 : appenders = f_appenders;
546 : }
547 :
548 131082 : for(auto a : appenders)
549 : {
550 65541 : a->send_message(msg);
551 : }
552 : }
553 :
554 :
555 0 : bool is_configured()
556 : {
557 0 : guard g;
558 :
559 0 : if(g_instance == nullptr)
560 : {
561 0 : return false;
562 : }
563 :
564 0 : return (*g_instance)->is_configured();
565 : }
566 :
567 :
568 0 : bool has_appender(std::string const & type)
569 : {
570 0 : guard g;
571 :
572 0 : if(g_instance == nullptr)
573 : {
574 0 : return false;
575 : }
576 :
577 0 : return (*g_instance)->has_appender(type);
578 : }
579 :
580 :
581 0 : void reopen()
582 : {
583 0 : guard g;
584 :
585 0 : if(g_instance == nullptr)
586 : {
587 0 : return;
588 : }
589 :
590 0 : (*g_instance)->reopen();
591 : }
592 :
593 :
594 0 : bool configure_console(bool force)
595 : {
596 0 : bool result(!is_configured() || (force && !has_appender("console")));
597 0 : if(result)
598 : {
599 0 : logger::get_instance()->add_console_appender();
600 : }
601 :
602 0 : return result;
603 : }
604 :
605 :
606 0 : bool configure_syslog(std::string const & identity)
607 : {
608 0 : bool result(!is_configured());
609 0 : if(result)
610 : {
611 0 : logger::get_instance()->add_syslog_appender(identity);
612 : }
613 :
614 0 : return result;
615 : }
616 :
617 :
618 0 : bool configure_file(std::string const & filename)
619 : {
620 0 : bool result(!is_configured());
621 0 : if(result)
622 : {
623 0 : logger::get_instance()->add_file_appender(filename);
624 : }
625 :
626 0 : return result;
627 : }
628 :
629 :
630 0 : bool configure_config(std::string const & config_filename)
631 : {
632 0 : bool result(!is_configured());
633 0 : if(result)
634 : {
635 0 : logger::get_instance()->add_config(config_filename);
636 : }
637 :
638 0 : return result;
639 : }
640 :
641 :
642 :
643 :
644 6 : } // snaplogger namespace
645 : // vim: ts=4 sw=4 et
|