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/private_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/logger.h"
37 : #include "snaplogger/syslog_appender.h"
38 :
39 :
40 : // cppthread lib
41 : //
42 : #include <cppthread/log.h>
43 : #include <cppthread/runner.h>
44 :
45 :
46 : // last include
47 : //
48 : #include <snapdev/poison.h>
49 :
50 :
51 :
52 : namespace snaplogger
53 : {
54 :
55 :
56 : namespace
57 : {
58 :
59 :
60 :
61 0 : void getopt_logs(cppthread::log_level_t l, std::string const & m)
62 : {
63 0 : severity_t sev(severity_t::SEVERITY_ERROR);
64 0 : switch(l)
65 : {
66 0 : case cppthread::log_level_t::debug:
67 0 : sev = severity_t::SEVERITY_DEBUG;
68 0 : break;
69 :
70 0 : case cppthread::log_level_t::info:
71 0 : sev = severity_t::SEVERITY_INFORMATION;
72 0 : break;
73 :
74 0 : case cppthread::log_level_t::warning:
75 0 : sev = severity_t::SEVERITY_WARNING;
76 0 : break;
77 :
78 0 : case cppthread::log_level_t::fatal:
79 0 : sev = severity_t::SEVERITY_FATAL;
80 0 : break;
81 :
82 : //case cppthread::log_level_t::error:
83 0 : default:
84 : // anything else, keep SEVERITY_ERROR
85 0 : break;
86 :
87 : }
88 :
89 0 : message msg(sev, __FILE__, __func__, __LINE__);
90 0 : msg << m;
91 :
92 : // this call cannot create a loop, if the creation of the logger
93 : // generates an cppthread log, then the second call will generate
94 : // an exception (see get_instance() in snaplogger/logger.cpp)
95 : //
96 0 : logger::pointer_t lg(logger::get_instance());
97 :
98 0 : lg->log_message(msg);
99 0 : }
100 :
101 :
102 :
103 : }
104 : // no name namespace
105 :
106 :
107 : namespace detail
108 : {
109 :
110 :
111 :
112 :
113 1 : class asynchronous_logger
114 : : public cppthread::runner
115 : {
116 : public:
117 1 : asynchronous_logger(message_fifo_t::pointer_t fifo)
118 1 : : runner("logger asynchronous thread")
119 2 : , f_logger(logger::get_instance())
120 3 : , f_fifo(fifo)
121 : {
122 1 : }
123 :
124 2 : virtual void run()
125 : {
126 : // loop until the FIFO is marked as being done
127 : //
128 : for(;;)
129 : {
130 3 : message::pointer_t msg;
131 2 : if(!f_fifo->pop_front(msg, -1))
132 : {
133 1 : break;
134 : }
135 2 : logger::pointer_t l(f_logger.lock());
136 1 : if(l != nullptr)
137 : {
138 1 : l->process_message(*msg);
139 : }
140 1 : }
141 1 : }
142 :
143 : private:
144 : logger::weak_pointer_t f_logger = logger::pointer_t();
145 : message_fifo_t::pointer_t f_fifo = message_fifo_t::pointer_t();
146 : };
147 :
148 :
149 :
150 : }
151 : // detail namespace
152 :
153 :
154 :
155 2 : private_logger::private_logger()
156 : {
157 2 : f_normal_component = get_component(COMPONENT_NORMAL);
158 :
159 2 : cppthread::set_log_callback(getopt_logs);
160 2 : }
161 :
162 :
163 6 : private_logger::~private_logger()
164 : {
165 2 : delete_thread();
166 4 : }
167 :
168 :
169 2 : void private_logger::shutdown()
170 : {
171 2 : delete_thread();
172 2 : }
173 :
174 :
175 8 : void private_logger::register_appender_factory(appender_factory::pointer_t factory)
176 : {
177 8 : if(factory == nullptr)
178 : {
179 : throw logger_logic_error( // LCOV_EXCL_LINE
180 : "register_appender_factory() called with a nullptr."); // LCOV_EXCL_LINE
181 : }
182 :
183 16 : guard g;
184 :
185 8 : if(f_appender_factories.find(factory->get_type()) != f_appender_factories.end())
186 : {
187 : throw duplicate_error( // LCOV_EXCL_LINE
188 : "trying to register appender type \"" // LCOV_EXCL_LINE
189 : + factory->get_type() // LCOV_EXCL_LINE
190 : + "\" twice won't work."); // LCOV_EXCL_LINE
191 : }
192 :
193 8 : f_appender_factories[factory->get_type()] = factory;
194 8 : }
195 :
196 :
197 2 : appender::pointer_t private_logger::create_appender(std::string const & type, std::string const & name)
198 : {
199 4 : guard g;
200 :
201 2 : auto it(f_appender_factories.find(type));
202 2 : if(it != f_appender_factories.end())
203 : {
204 1 : return it->second->create(name);
205 : }
206 :
207 1 : return appender::pointer_t();
208 : }
209 :
210 :
211 26 : component::pointer_t private_logger::get_component(std::string const & name)
212 : {
213 52 : guard g;
214 :
215 26 : auto it(f_components.find(name));
216 26 : if(it != f_components.end())
217 : {
218 20 : return it->second;
219 : }
220 :
221 12 : auto comp(std::make_shared<component>(name));
222 6 : f_components[name] = comp;
223 :
224 6 : return comp;
225 : }
226 :
227 :
228 18 : format::pointer_t private_logger::get_default_format()
229 : {
230 36 : guard g;
231 :
232 18 : if(f_default_format == nullptr)
233 : {
234 2 : f_default_format = std::make_shared<format>(
235 : //"${env:name=HOME:padding='-':align=center:exact_width=6} "
236 : "${date} ${time} ${hostname}"
237 : " ${progname}[${pid}]: ${severity}:"
238 : " ${message:escape:max_width=1000}"
239 : " (in function \"${function}()\")"
240 : " (${basename}:${line})"
241 1 : );
242 : }
243 :
244 36 : return f_default_format;
245 : }
246 :
247 :
248 94615 : environment::pointer_t private_logger::create_environment()
249 : {
250 94615 : pid_t const tid(cppthread::gettid());
251 :
252 189230 : guard g;
253 :
254 94615 : auto it(f_environment.find(tid));
255 94615 : if(it == f_environment.end())
256 : {
257 2 : auto result(std::make_shared<environment>(tid));
258 1 : f_environment[tid] = result;
259 1 : return result;
260 : }
261 :
262 94614 : return it->second;
263 : }
264 :
265 :
266 : /** \brief Add a severity.
267 : *
268 : * This function adds a severity to the private logger object.
269 : *
270 : * Remember that a severity can be given aliases so this function may
271 : * add quite a few entries, not just one.
272 : *
273 : * \warning
274 : * You should not be calling this function directly. Please see the
275 : * direct snaplogger::add_severity() function instead.
276 : *
277 : * \exception duplicate_error
278 : * The function verifies that the new severity is not a duplicate of
279 : * an existing system severity.
280 : *
281 : * \param[in] sev The severity object to be added.
282 : */
283 25 : void private_logger::add_severity(severity::pointer_t sev)
284 : {
285 50 : guard g;
286 :
287 25 : auto it(f_severity_by_severity.find(sev->get_severity()));
288 25 : if(it != f_severity_by_severity.end())
289 : {
290 3 : if(it->second->is_system())
291 : {
292 2 : throw duplicate_error("a system severity cannot be replaced.");
293 : }
294 : }
295 :
296 59 : for(auto n : sev->get_all_names())
297 : {
298 36 : auto s(f_severity_by_name.find(n));
299 36 : if(s != f_severity_by_name.end())
300 : {
301 3 : if(s->second->is_system())
302 : {
303 2 : throw duplicate_error("a system severity cannot be replaced.");
304 : }
305 : }
306 : }
307 :
308 21 : f_severity_by_severity[sev->get_severity()] = sev;
309 :
310 55 : for(auto n : sev->get_all_names())
311 : {
312 34 : f_severity_by_name[n] = sev;
313 : }
314 21 : }
315 :
316 :
317 24 : severity::pointer_t private_logger::get_severity(std::string const & name) const
318 : {
319 48 : guard g;
320 :
321 24 : auto it(f_severity_by_name.find(name));
322 24 : if(it == f_severity_by_name.end())
323 : {
324 17 : return severity::pointer_t();
325 : }
326 :
327 7 : return it->second;
328 : }
329 :
330 :
331 16 : severity::pointer_t private_logger::get_severity(severity_t sev) const
332 : {
333 32 : guard g;
334 :
335 16 : auto it(f_severity_by_severity.find(sev));
336 16 : if(it == f_severity_by_severity.end())
337 : {
338 2 : return severity::pointer_t();
339 : }
340 :
341 14 : return it->second;
342 : }
343 :
344 :
345 26 : void private_logger::set_diagnostic(std::string const & key, std::string const & diagnostic)
346 : {
347 52 : guard g;
348 :
349 26 : f_map_diagnostics[key] = diagnostic;
350 26 : }
351 :
352 :
353 1 : void private_logger::unset_diagnostic(std::string const & key)
354 : {
355 2 : guard g;
356 :
357 1 : auto it(f_map_diagnostics.find(key));
358 1 : if(it != f_map_diagnostics.end())
359 : {
360 1 : f_map_diagnostics.erase(it);
361 : }
362 1 : }
363 :
364 :
365 45 : map_diagnostics_t private_logger::get_map_diagnostics()
366 : {
367 90 : guard g;
368 :
369 90 : return f_map_diagnostics;
370 : }
371 :
372 :
373 0 : void private_logger::set_maximum_trace_diagnostics(size_t max)
374 : {
375 0 : f_maximum_trace_diagnostics = max;
376 0 : }
377 :
378 :
379 0 : size_t private_logger::get_maximum_trace_diagnostics() const
380 : {
381 0 : return f_maximum_trace_diagnostics;
382 : }
383 :
384 :
385 0 : void private_logger::add_trace_diagnostic(std::string const & diagnostic)
386 : {
387 0 : guard g;
388 :
389 0 : f_trace_diagnostics.push_back(diagnostic);
390 0 : if(f_trace_diagnostics.size() > f_maximum_trace_diagnostics)
391 : {
392 0 : f_trace_diagnostics.pop_front();
393 : }
394 0 : }
395 :
396 :
397 0 : void private_logger::clear_trace_diagnostics()
398 : {
399 0 : guard g;
400 :
401 0 : f_trace_diagnostics.clear();
402 0 : }
403 :
404 :
405 0 : trace_diagnostics_t private_logger::get_trace_diagnostics()
406 : {
407 0 : guard g;
408 :
409 0 : return f_trace_diagnostics;
410 : }
411 :
412 :
413 3 : void private_logger::push_nested_diagnostic(std::string const & diagnostic)
414 : {
415 6 : guard g;
416 :
417 3 : f_nested_diagnostics.push_back(diagnostic);
418 3 : }
419 :
420 :
421 3 : void private_logger::pop_nested_diagnostic()
422 : {
423 6 : guard g;
424 :
425 3 : f_nested_diagnostics.pop_back();
426 3 : }
427 :
428 :
429 8 : string_vector_t private_logger::get_nested_diagnostics() const
430 : {
431 16 : guard g;
432 :
433 16 : return f_nested_diagnostics;
434 : }
435 :
436 :
437 57 : void private_logger::register_variable_factory(variable_factory::pointer_t factory)
438 : {
439 114 : guard g;
440 :
441 57 : auto it(f_variable_factories.find(factory->get_type()));
442 57 : if(it != f_variable_factories.end())
443 : {
444 : throw duplicate_error(
445 : "trying to add two variable factories of type \""
446 2 : + factory->get_type()
447 3 : + "\".");
448 : }
449 :
450 56 : f_variable_factories[factory->get_type()] = factory;
451 56 : }
452 :
453 :
454 212 : variable::pointer_t private_logger::get_variable(std::string const & type)
455 : {
456 424 : guard g;
457 :
458 212 : if(f_variable_factories.empty())
459 : {
460 : throw invalid_variable("No variable factories were registered yet; you can't create a variable with type \"" // LCOV_EXCL_LINE
461 : + type // LCOV_EXCL_LINE
462 : + "\" at this point."); // LCOV_EXCL_LINE
463 : }
464 :
465 212 : auto it(f_variable_factories.find(type));
466 212 : if(it == f_variable_factories.end())
467 : {
468 : // TBD: should we instead return a null var.?
469 : throw invalid_variable("You can't create variable with type \""
470 2 : + type
471 3 : + "\", no such variable type was registered.");
472 : }
473 :
474 422 : return it->second->create_variable();
475 : }
476 :
477 :
478 48247 : bool private_logger::has_functions() const
479 : {
480 96494 : guard g;
481 :
482 96494 : return !f_functions.empty();
483 : }
484 :
485 :
486 23 : void private_logger::register_function(function::pointer_t func)
487 : {
488 46 : guard g;
489 :
490 23 : auto it(f_functions.find(func->get_name()));
491 23 : if(it != f_functions.end())
492 : {
493 : throw duplicate_error(
494 : "trying to add two functions named \""
495 2 : + func->get_name()
496 3 : + "\".");
497 : }
498 22 : f_functions[func->get_name()] = func;
499 22 : }
500 :
501 :
502 95 : function::pointer_t private_logger::get_function(std::string const & name) const
503 : {
504 190 : guard g;
505 :
506 95 : auto it(f_functions.find(name));
507 95 : if(it != f_functions.end())
508 : {
509 73 : return it->second;
510 : }
511 :
512 22 : return function::pointer_t();
513 : }
514 :
515 :
516 1 : void private_logger::create_thread()
517 : {
518 2 : guard g;
519 :
520 : try
521 : {
522 1 : f_fifo = std::make_shared<message_fifo_t>();
523 1 : f_asynchronous_logger = std::make_shared<detail::asynchronous_logger>(f_fifo);
524 1 : f_thread = std::make_shared<cppthread::thread>("asynchronous logger thread", f_asynchronous_logger.get());
525 1 : f_thread->start();
526 : }
527 : catch(...) // LCOV_EXCL_LINE
528 : {
529 : if(f_fifo != nullptr) // LCOV_EXCL_LINE
530 : {
531 : f_fifo->done(false); // LCOV_EXCL_LINE
532 : }
533 :
534 : f_thread.reset(); // LCOV_EXCL_LINE
535 : f_asynchronous_logger.reset(); // LCOV_EXCL_LINE
536 : f_fifo.reset(); // LCOV_EXCL_LINE
537 : throw; // LCOV_EXCL_LINE
538 : }
539 1 : }
540 :
541 :
542 5 : void private_logger::delete_thread()
543 : {
544 : // WARNING: we can't call fifo::done() while our guard is locked
545 : // we also have to make sure it's not a null pointer
546 : //
547 10 : message_fifo_t::pointer_t fifo = message_fifo_t::pointer_t();
548 10 : asynchronous_logger_pointer_t asynchronous_logger = asynchronous_logger_pointer_t();
549 10 : cppthread::thread::pointer_t thread = cppthread::thread::pointer_t();
550 :
551 : {
552 10 : guard g;
553 :
554 5 : swap(thread, f_thread);
555 5 : swap(asynchronous_logger, f_asynchronous_logger);
556 5 : swap(fifo, f_fifo);
557 : }
558 :
559 5 : if(fifo != nullptr)
560 : {
561 1 : fifo->done(false);
562 : }
563 :
564 : try
565 : {
566 5 : thread.reset();
567 : }
568 : catch(std::exception const & e)
569 : {
570 : // in most cases this one happens when quitting when one of your
571 : // functions attempts to get an instance of the logger, which is
572 : // forbidden once you return from your main() function
573 : //
574 : std::cerr << "got exception \""
575 : << e.what()
576 : << "\" while deleting the asynchronous thread."
577 : << std::endl;
578 : }
579 5 : }
580 :
581 :
582 1 : void private_logger::send_message_to_thread(message::pointer_t msg)
583 : {
584 : {
585 2 : guard g;
586 :
587 1 : if(f_fifo == nullptr)
588 : {
589 1 : create_thread();
590 : }
591 : }
592 :
593 1 : f_fifo->push_back(msg);
594 1 : }
595 :
596 :
597 :
598 :
599 95026 : private_logger::pointer_t get_private_logger()
600 : {
601 95026 : return std::dynamic_pointer_cast<private_logger>(logger::get_instance());
602 : }
603 :
604 :
605 48313 : private_logger::pointer_t get_private_logger(message const & msg)
606 : {
607 48313 : return std::dynamic_pointer_cast<private_logger>(msg.get_logger());
608 : }
609 :
610 :
611 :
612 6 : } // snaplogger namespace
613 : // vim: ts=4 sw=4 et
|