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