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