Line data Source code
1 : // Copyright (c) 2013-2022 Made to Order Software Corp. All Rights Reserved
2 : //
3 : // https://snapwebsites.org/project/snaplogger
4 : // contact@m2osw.com
5 : //
6 : // This program is free software; you can redistribute it and/or modify
7 : // it under the terms of the GNU General Public License as published by
8 : // the Free Software Foundation; either version 2 of the License, or
9 : // (at your option) any later version.
10 : //
11 : // This program is distributed in the hope that it will be useful,
12 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : // GNU General Public License for more details.
15 : //
16 : // You should have received a copy of the GNU General Public License along
17 : // with this program; if not, write to the Free Software Foundation, Inc.,
18 : // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 :
20 : /** \file
21 : * \brief Appenders are used to append data to somewhere.
22 : *
23 : * This file declares the base appender class.
24 : */
25 :
26 : // self
27 : //
28 : #include "snaplogger/message.h"
29 :
30 : #include "snaplogger/exception.h"
31 : #include "snaplogger/guard.h"
32 : #include "snaplogger/logger.h"
33 :
34 :
35 : // C++ lib
36 : //
37 : #include <iostream>
38 :
39 :
40 : // C lib
41 : //
42 : #include <sys/time.h>
43 :
44 :
45 : // last include
46 : //
47 : #include <snapdev/poison.h>
48 :
49 :
50 :
51 : namespace snaplogger
52 : {
53 :
54 :
55 : namespace
56 : {
57 :
58 : #pragma GCC diagnostic push
59 : #pragma GCC diagnostic ignored "-Wpedantic"
60 : constexpr char const * const g_system_field_names[] =
61 : {
62 : [static_cast<std::size_t>(system_field_t::SYSTEM_FIELD_MESSAGE )] = "_message",
63 : [static_cast<std::size_t>(system_field_t::SYSTEM_FIELD_TIMESTAMP )] = "_timestamp",
64 : [static_cast<std::size_t>(system_field_t::SYSTEM_FIELD_SEVERITY )] = "_severity",
65 : [static_cast<std::size_t>(system_field_t::SYSTEM_FIELD_FILENAME )] = "_filename",
66 : [static_cast<std::size_t>(system_field_t::SYSTEM_FIELD_FUNCTION_NAME)] = "_function_name",
67 : [static_cast<std::size_t>(system_field_t::SYSTEM_FIELD_LINE )] = "_line",
68 : };
69 : #pragma GCC diagnostic pop
70 :
71 :
72 : int g_message_id = 0;
73 :
74 :
75 99165 : int get_next_id()
76 : {
77 198330 : guard g;
78 :
79 99165 : ++g_message_id;
80 99165 : if(g_message_id == 0)
81 : {
82 : // never use 0 as the id; of course, it's very unlikely that this happens
83 0 : g_message_id = 1;
84 : }
85 198330 : return g_message_id;
86 : }
87 :
88 :
89 :
90 : }
91 : // no name namespace
92 :
93 :
94 :
95 :
96 1204931 : int null_buffer::overflow(int c)
97 : {
98 1204931 : return c;
99 : }
100 :
101 :
102 :
103 :
104 :
105 99165 : message::message(
106 : severity_t sev
107 : , char const * file
108 : , char const * func
109 99165 : , int line)
110 : : f_logger(logger::get_instance())
111 : , f_severity(sev)
112 : , f_filename(file == nullptr ? std::string() : std::string(file))
113 : , f_funcname(func == nullptr ? std::string() : std::string(func))
114 : , f_line(line)
115 : , f_environment(create_environment())
116 99165 : , f_fields(f_logger->get_default_fields())
117 : {
118 99165 : clock_gettime(CLOCK_REALTIME_COARSE, &f_timestamp);
119 :
120 99165 : add_field("id", std::to_string(get_next_id()));
121 :
122 198330 : if(f_severity < f_logger->get_lowest_severity()
123 99165 : || f_severity == severity_t::SEVERITY_OFF)
124 : {
125 50919 : f_null.reset(new null_buffer);
126 50919 : std::ostream & ref = *this;
127 50919 : f_saved_buffer = ref.rdbuf(f_null.get());
128 : }
129 99165 : }
130 :
131 :
132 2 : message::message(std::basic_stringstream<char> const & m, message const & msg)
133 : : f_logger(msg.f_logger)
134 : , f_timestamp(msg.f_timestamp)
135 2 : , f_severity(msg.f_severity)
136 : , f_filename(msg.f_filename)
137 : , f_funcname(msg.f_funcname)
138 2 : , f_line(msg.f_line)
139 2 : , f_recursive_message(msg.f_recursive_message)
140 : , f_environment(msg.f_environment)
141 : , f_components(msg.f_components)
142 : , f_fields(msg.f_fields)
143 : , f_null(null_buffer::pointer_t())
144 : , f_saved_buffer(nullptr)
145 8 : , f_copy(true)
146 : {
147 2 : *this << m.rdbuf();
148 2 : }
149 :
150 :
151 198334 : message::~message()
152 : {
153 99167 : if(f_saved_buffer != nullptr)
154 : {
155 50919 : std::ostream & ref = *this;
156 50919 : ref.rdbuf(f_saved_buffer);
157 : }
158 99167 : }
159 :
160 :
161 0 : severity_t message::default_severity()
162 : {
163 0 : return logger::get_instance()->get_default_severity();
164 : }
165 :
166 :
167 33561 : void message::set_severity(severity_t severity)
168 : {
169 33561 : f_severity = severity;
170 33561 : }
171 :
172 :
173 1 : void message::set_filename(std::string const & filename)
174 : {
175 1 : f_filename = filename;
176 1 : }
177 :
178 :
179 1 : void message::set_function(std::string const & funcname)
180 : {
181 1 : f_funcname = funcname;
182 1 : }
183 :
184 :
185 1 : void message::set_line(int line)
186 : {
187 1 : f_line = line;
188 1 : }
189 :
190 :
191 24 : void message::set_recursive_message(bool state) const
192 : {
193 24 : f_recursive_message = state;
194 24 : }
195 :
196 :
197 1 : void message::set_precise_time()
198 : {
199 1 : clock_gettime(CLOCK_REALTIME, &f_timestamp);
200 1 : }
201 :
202 :
203 0 : void message::set_timestamp(timespec const & timestamp)
204 : {
205 0 : f_timestamp = timestamp;
206 0 : }
207 :
208 :
209 15 : bool message::can_add_component(component::pointer_t c) const
210 : {
211 15 : if(c != nullptr)
212 : {
213 15 : return !c->is_mutually_exclusive(f_components);
214 : }
215 :
216 0 : return false;
217 : }
218 :
219 :
220 15 : void message::add_component(component::pointer_t c)
221 : {
222 15 : if(c != nullptr)
223 : {
224 15 : if(!can_add_component(c))
225 : {
226 : throw conflict_error(
227 : "component \""
228 0 : + c->get_name()
229 0 : + "\" cannot be added to this message as it is mutually exclusive with one or more of the other components that were already added to this message.");
230 : }
231 :
232 15 : f_components.insert(c);
233 : }
234 15 : }
235 :
236 :
237 99171 : void message::add_field(std::string const & name, std::string const & value)
238 : {
239 99171 : if(!name.empty())
240 : {
241 99171 : if(name[0] == '_')
242 : {
243 : throw invalid_parameter(
244 : "field name \""
245 0 : + name
246 0 : + "\" is a system name (whether reserved or already defined) and as such is read-only."
247 0 : " Do not start your field names with an underscore (_).");
248 : }
249 :
250 99171 : f_fields[name] = value;
251 : }
252 99171 : }
253 :
254 :
255 48716 : std::shared_ptr<logger> message::get_logger() const
256 : {
257 48716 : return f_logger;
258 : }
259 :
260 :
261 198408 : severity_t message::get_severity() const
262 : {
263 198408 : return f_severity;
264 : }
265 :
266 :
267 4 : timespec const & message::get_timestamp() const
268 : {
269 4 : return f_timestamp;
270 : }
271 :
272 :
273 2 : std::string const & message::get_filename() const
274 : {
275 2 : return f_filename;
276 : }
277 :
278 :
279 2 : std::string const & message::get_function() const
280 : {
281 2 : return f_funcname;
282 : }
283 :
284 :
285 2 : int message::get_line() const
286 : {
287 2 : return f_line;
288 : }
289 :
290 :
291 48537 : bool message::get_recursive_message() const
292 : {
293 48537 : return f_recursive_message;
294 : }
295 :
296 :
297 0 : bool message::has_component(component::pointer_t c) const
298 : {
299 0 : return f_components.find(c) != f_components.end();
300 : }
301 :
302 :
303 147711 : component::set_t const & message::get_components() const
304 : {
305 147711 : return f_components;
306 : }
307 :
308 :
309 47 : environment::pointer_t message::get_environment() const
310 : {
311 47 : return f_environment;
312 : }
313 :
314 :
315 48535 : std::string message::get_message() const
316 : {
317 48535 : std::string s(str());
318 :
319 97070 : if(!s.empty()
320 48535 : && s.back() == '\n')
321 : {
322 4 : s.pop_back();
323 : }
324 :
325 97070 : if(!s.empty()
326 48535 : && s.back() == '\r')
327 : {
328 2 : s.pop_back();
329 : }
330 :
331 48535 : return s;
332 : }
333 :
334 :
335 0 : char const * message::get_system_field_name(system_field_t field)
336 : {
337 0 : std::size_t const idx(static_cast<std::size_t>(field));
338 0 : if(idx >= static_cast<std::size_t>(system_field_t::SYSTEM_FIELD_max))
339 : {
340 0 : return "_unknown";
341 : }
342 0 : return g_system_field_names[idx];
343 : }
344 :
345 :
346 0 : system_field_t message::get_system_field_from_name(std::string const & name)
347 : {
348 0 : if(name.length() >= 2
349 0 : && name[0] == '_')
350 : {
351 0 : switch(name[1])
352 : {
353 0 : case 'f':
354 0 : if(name == "_filename")
355 : {
356 0 : return system_field_t::SYSTEM_FIELD_FILENAME;
357 : }
358 0 : if(name == "_function_name")
359 : {
360 0 : return system_field_t::SYSTEM_FIELD_FUNCTION_NAME;
361 : }
362 0 : break;
363 :
364 0 : case 'l':
365 0 : if(name == "_line")
366 : {
367 0 : return system_field_t::SYSTEM_FIELD_LINE;
368 : }
369 0 : break;
370 :
371 0 : case 'm':
372 0 : if(name == "_message")
373 : {
374 0 : return system_field_t::SYSTEM_FIELD_MESSAGE;
375 : }
376 0 : break;
377 :
378 0 : case 's':
379 0 : if(name == "_severity")
380 : {
381 0 : return system_field_t::SYSTEM_FIELD_SEVERITY;
382 : }
383 0 : break;
384 :
385 0 : case 't':
386 0 : if(name == "_timestamp")
387 : {
388 0 : return system_field_t::SYSTEM_FIELD_TIMESTAMP;
389 : }
390 0 : break;
391 :
392 : }
393 : }
394 :
395 0 : return system_field_t::SYSTEM_FIELD_UNDEFINED;
396 : }
397 :
398 :
399 2 : std::string message::get_field(std::string const & name) const
400 : {
401 4 : if(!name.empty()
402 2 : && name[0] == '_')
403 : {
404 0 : switch(get_system_field_from_name(name))
405 : {
406 0 : case system_field_t::SYSTEM_FIELD_MESSAGE:
407 0 : return get_message();
408 :
409 0 : case system_field_t::SYSTEM_FIELD_TIMESTAMP:
410 : // TODO: offer ways to get the date & time converted to strings
411 : {
412 0 : std::string timestamp(std::to_string(f_timestamp.tv_sec));
413 0 : if(f_timestamp.tv_nsec != 0)
414 : {
415 0 : std::string nsec(std::to_string(f_timestamp.tv_nsec));
416 0 : while(nsec.length() < 9)
417 : {
418 0 : nsec = '0' + nsec;
419 : }
420 0 : while(nsec.back() == '0')
421 : {
422 0 : nsec.pop_back();
423 : }
424 0 : timestamp += '.';
425 0 : timestamp += nsec;
426 : }
427 0 : return timestamp;
428 : }
429 :
430 0 : case system_field_t::SYSTEM_FIELD_SEVERITY:
431 : {
432 0 : severity::pointer_t sev(snaplogger::get_severity(f_severity));
433 0 : return sev == nullptr ? "<unknown>" : sev->get_name();
434 : }
435 :
436 0 : case system_field_t::SYSTEM_FIELD_FILENAME:
437 0 : return f_filename;
438 :
439 0 : case system_field_t::SYSTEM_FIELD_FUNCTION_NAME:
440 0 : return f_funcname;
441 :
442 0 : case system_field_t::SYSTEM_FIELD_LINE:
443 0 : return std::to_string(f_line);
444 :
445 0 : default:
446 0 : return std::string();
447 :
448 : }
449 : }
450 :
451 2 : auto it(f_fields.find(name));
452 2 : if(it == f_fields.end())
453 : {
454 0 : return std::string();
455 : }
456 2 : return it->second;
457 : }
458 :
459 :
460 1 : field_map_t message::get_fields() const
461 : {
462 1 : return f_fields;
463 : }
464 :
465 :
466 65594 : message::pointer_t create_message(
467 : severity_t sev
468 : , char const * file
469 : , char const * func
470 : , int line)
471 : {
472 65594 : return std::make_shared<message>(sev, file, func, line);
473 : }
474 :
475 :
476 99158 : void send_message(std::basic_ostream<char> & out)
477 : {
478 99158 : message * msg(dynamic_cast<message *>(&out));
479 99158 : if(msg == nullptr)
480 : {
481 1 : throw not_a_message("the 'out' parameter to the send_message() function is expected to be a snaplogger::message object.");
482 : }
483 :
484 99158 : logger::get_instance()->log_message(*msg);
485 99156 : }
486 :
487 :
488 0 : void send_stack_trace(libexcept::exception_base_t const & e)
489 : {
490 0 : libexcept::stack_trace_t const & stack(e.get_stack_trace());
491 0 : for(auto const & s : stack)
492 : {
493 0 : SNAP_LOG_EXCEPTION
494 : << s
495 : << SNAP_LOG_SEND;
496 : }
497 0 : }
498 :
499 :
500 6 : } // snaplogger namespace
501 : // vim: ts=4 sw=4 et
|