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