Line data Source code
1 : // Copyright (c) 2013-2025 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++
36 : //
37 : #include <iostream>
38 :
39 :
40 : // C
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 91296 : int get_next_id()
76 : {
77 91296 : guard g;
78 :
79 91296 : ++g_message_id;
80 91296 : 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 91296 : return g_message_id;
86 91296 : }
87 :
88 :
89 :
90 : }
91 : // no name namespace
92 :
93 :
94 :
95 :
96 1022327 : int null_buffer::overflow(int c)
97 : {
98 1022327 : return c;
99 : }
100 :
101 :
102 :
103 :
104 :
105 91296 : message::message(
106 : severity_t sev
107 91296 : , std::source_location const & location)
108 91296 : : f_logger(logger::get_instance())
109 91296 : , f_severity(sev)
110 182592 : , f_filename(location.file_name())
111 273888 : , f_funcname(location.function_name())
112 91296 : , f_line(location.line())
113 91296 : , f_column(location.column())
114 91296 : , f_environment(create_environment())
115 182592 : , f_fields(f_logger->get_default_fields())
116 : {
117 91296 : clock_gettime(CLOCK_REALTIME_COARSE, &f_timestamp);
118 :
119 273888 : add_field("id", std::to_string(get_next_id()));
120 :
121 91296 : if(f_severity < f_logger->get_lowest_severity()
122 91296 : || f_severity == severity_t::SEVERITY_OFF)
123 : {
124 45548 : f_null.reset(new null_buffer);
125 45548 : std::ostream & ref = *this;
126 45548 : f_saved_buffer = ref.rdbuf(f_null.get());
127 : }
128 91296 : }
129 :
130 :
131 2 : message::message(std::basic_stringstream<char> const & m, message const & msg)
132 2 : : f_logger(msg.f_logger)
133 2 : , f_timestamp(msg.f_timestamp)
134 2 : , f_severity(msg.f_severity)
135 2 : , f_filename(msg.f_filename)
136 2 : , f_funcname(msg.f_funcname)
137 2 : , f_line(msg.f_line)
138 2 : , f_column(msg.f_column)
139 2 : , f_recursive_message(msg.f_recursive_message)
140 2 : , f_environment(msg.f_environment)
141 2 : , f_components(msg.f_components)
142 2 : , f_fields(msg.f_fields)
143 2 : , f_null(null_buffer::pointer_t())
144 2 : , f_saved_buffer(nullptr)
145 2 : , f_copy(true)
146 : {
147 2 : *this << m.rdbuf();
148 2 : }
149 :
150 :
151 91298 : message::~message()
152 : {
153 91298 : if(f_saved_buffer != nullptr)
154 : {
155 45548 : std::ostream & ref = *this;
156 45548 : ref.rdbuf(f_saved_buffer);
157 : }
158 91298 : }
159 :
160 :
161 0 : severity_t message::default_severity()
162 : {
163 0 : return logger::get_instance()->get_default_severity();
164 : }
165 :
166 :
167 25689 : void message::set_severity(severity_t severity)
168 : {
169 25689 : f_severity = severity;
170 25689 : }
171 :
172 :
173 0 : void message::set_location(std::source_location const & location)
174 : {
175 0 : f_filename = location.file_name();
176 0 : f_funcname = location.function_name();
177 0 : f_line = location.line();
178 0 : f_column = location.column();
179 0 : }
180 :
181 :
182 1 : void message::set_filename(std::string const & filename)
183 : {
184 1 : f_filename = filename;
185 1 : }
186 :
187 :
188 1 : void message::set_function(std::string const & funcname)
189 : {
190 1 : f_funcname = funcname;
191 1 : }
192 :
193 :
194 1 : void message::set_line(std::uint_least32_t line)
195 : {
196 1 : f_line = line;
197 1 : }
198 :
199 :
200 1 : void message::set_column(std::uint_least32_t column)
201 : {
202 1 : f_column = column;
203 1 : }
204 :
205 24 : void message::set_recursive_message(bool state) const
206 : {
207 24 : f_recursive_message = state;
208 24 : }
209 :
210 :
211 1 : void message::set_precise_time()
212 : {
213 1 : clock_gettime(CLOCK_REALTIME, &f_timestamp);
214 1 : }
215 :
216 :
217 0 : void message::set_timestamp(timespec const & timestamp)
218 : {
219 0 : f_timestamp = timestamp;
220 0 : }
221 :
222 :
223 15 : bool message::can_add_component(component::pointer_t c) const
224 : {
225 15 : if(c != nullptr)
226 : {
227 15 : return !c->is_mutually_exclusive(f_components);
228 : }
229 :
230 0 : return false;
231 : }
232 :
233 :
234 15 : void message::add_component(component::pointer_t c)
235 : {
236 15 : if(c != nullptr)
237 : {
238 15 : if(!can_add_component(c))
239 : {
240 : throw conflict_error(
241 : "component \""
242 0 : + c->get_name()
243 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.");
244 : }
245 :
246 15 : f_components.insert(c);
247 : }
248 15 : }
249 :
250 :
251 91302 : void message::add_field(std::string const & name, std::string const & value)
252 : {
253 91302 : if(!name.empty())
254 : {
255 91302 : if(name[0] == '_')
256 : {
257 : throw invalid_parameter(
258 : "field name \""
259 0 : + name
260 0 : + "\" is a system name (whether reserved or already defined) and as such is read-only."
261 0 : " Do not start your field names with an underscore (_).");
262 : }
263 :
264 91302 : f_fields[name] = value;
265 : }
266 91302 : }
267 :
268 :
269 41830 : std::shared_ptr<logger> message::get_logger() const
270 : {
271 41830 : return f_logger;
272 : }
273 :
274 :
275 91570 : severity_t message::get_severity() const
276 : {
277 91570 : return f_severity;
278 : }
279 :
280 :
281 4 : timespec const & message::get_timestamp() const
282 : {
283 4 : return f_timestamp;
284 : }
285 :
286 :
287 3 : std::string const & message::get_filename() const
288 : {
289 3 : return f_filename;
290 : }
291 :
292 :
293 3 : std::string const & message::get_function() const
294 : {
295 3 : return f_funcname;
296 : }
297 :
298 :
299 4 : std::uint_least32_t message::get_line() const
300 : {
301 4 : return f_line;
302 : }
303 :
304 :
305 3 : std::uint_least32_t message::get_column() const
306 : {
307 3 : return f_column;
308 : }
309 :
310 :
311 41648 : bool message::get_recursive_message() const
312 : {
313 41648 : return f_recursive_message;
314 : }
315 :
316 :
317 0 : bool message::has_component(component::pointer_t c) const
318 : {
319 0 : return f_components.find(c) != f_components.end();
320 : }
321 :
322 :
323 87405 : component::set_t const & message::get_components() const
324 : {
325 87405 : return f_components;
326 : }
327 :
328 :
329 47 : environment::pointer_t message::get_environment() const
330 : {
331 47 : return f_environment;
332 : }
333 :
334 :
335 41646 : std::string message::get_message() const
336 : {
337 41646 : std::string s(str());
338 :
339 41646 : if(!s.empty()
340 41646 : && s.back() == '\n')
341 : {
342 4 : s.pop_back();
343 : }
344 :
345 41646 : if(!s.empty()
346 41646 : && s.back() == '\r')
347 : {
348 2 : s.pop_back();
349 : }
350 :
351 41646 : return s;
352 : }
353 :
354 :
355 0 : char const * message::get_system_field_name(system_field_t field)
356 : {
357 0 : std::size_t const idx(static_cast<std::size_t>(field));
358 0 : if(idx >= static_cast<std::size_t>(system_field_t::SYSTEM_FIELD_max))
359 : {
360 0 : return "_unknown";
361 : }
362 0 : return g_system_field_names[idx];
363 : }
364 :
365 :
366 0 : system_field_t message::get_system_field_from_name(std::string const & name)
367 : {
368 0 : if(name.length() >= 2
369 0 : && name[0] == '_')
370 : {
371 0 : switch(name[1])
372 : {
373 0 : case 'f':
374 0 : if(name == "_filename")
375 : {
376 0 : return system_field_t::SYSTEM_FIELD_FILENAME;
377 : }
378 0 : if(name == "_function_name")
379 : {
380 0 : return system_field_t::SYSTEM_FIELD_FUNCTION_NAME;
381 : }
382 0 : break;
383 :
384 0 : case 'l':
385 0 : if(name == "_line")
386 : {
387 0 : return system_field_t::SYSTEM_FIELD_LINE;
388 : }
389 0 : break;
390 :
391 0 : case 'm':
392 0 : if(name == "_message")
393 : {
394 0 : return system_field_t::SYSTEM_FIELD_MESSAGE;
395 : }
396 0 : break;
397 :
398 0 : case 's':
399 0 : if(name == "_severity")
400 : {
401 0 : return system_field_t::SYSTEM_FIELD_SEVERITY;
402 : }
403 0 : break;
404 :
405 0 : case 't':
406 0 : if(name == "_timestamp")
407 : {
408 0 : return system_field_t::SYSTEM_FIELD_TIMESTAMP;
409 : }
410 0 : break;
411 :
412 : }
413 : }
414 :
415 0 : return system_field_t::SYSTEM_FIELD_UNDEFINED;
416 : }
417 :
418 :
419 2 : std::string message::get_field(std::string const & name) const
420 : {
421 2 : if(!name.empty()
422 2 : && name[0] == '_')
423 : {
424 0 : switch(get_system_field_from_name(name))
425 : {
426 0 : case system_field_t::SYSTEM_FIELD_MESSAGE:
427 0 : return get_message();
428 :
429 0 : case system_field_t::SYSTEM_FIELD_TIMESTAMP:
430 : // TODO: offer ways to get the date & time converted to strings
431 : {
432 0 : std::string timestamp(std::to_string(f_timestamp.tv_sec));
433 0 : if(f_timestamp.tv_nsec != 0)
434 : {
435 0 : std::string nsec(std::to_string(f_timestamp.tv_nsec));
436 0 : while(nsec.length() < 9)
437 : {
438 0 : nsec = '0' + nsec;
439 : }
440 0 : while(nsec.back() == '0')
441 : {
442 0 : nsec.pop_back();
443 : }
444 0 : timestamp += '.';
445 0 : timestamp += nsec;
446 0 : }
447 0 : return timestamp;
448 0 : }
449 :
450 0 : case system_field_t::SYSTEM_FIELD_SEVERITY:
451 : {
452 0 : severity::pointer_t sev(snaplogger::get_severity(f_severity));
453 0 : return sev == nullptr ? "<unknown>" : sev->get_name();
454 0 : }
455 :
456 0 : case system_field_t::SYSTEM_FIELD_FILENAME:
457 0 : return f_filename;
458 :
459 0 : case system_field_t::SYSTEM_FIELD_FUNCTION_NAME:
460 0 : return f_funcname;
461 :
462 0 : case system_field_t::SYSTEM_FIELD_LINE:
463 0 : return std::to_string(f_line);
464 :
465 0 : case system_field_t::SYSTEM_FIELD_COLUMN:
466 0 : return std::to_string(f_column);
467 :
468 0 : default:
469 0 : return std::string();
470 :
471 : }
472 : }
473 :
474 2 : auto it(f_fields.find(name));
475 2 : if(it == f_fields.end())
476 : {
477 0 : return std::string();
478 : }
479 2 : return it->second;
480 : }
481 :
482 :
483 1 : field_map_t message::get_fields() const
484 : {
485 1 : return f_fields;
486 : }
487 :
488 :
489 65597 : message::pointer_t create_message(
490 : severity_t sev
491 : , std::source_location const & location)
492 : {
493 65597 : return std::make_shared<message>(sev, location);
494 : }
495 :
496 :
497 91289 : void send_message(std::basic_ostream<char> & out)
498 : {
499 91289 : message * msg(dynamic_cast<message *>(&out));
500 91289 : if(msg == nullptr)
501 : {
502 3 : throw not_a_message("the 'out' parameter to the send_message() function is expected to be a snaplogger::message object.");
503 : }
504 :
505 91289 : logger::get_instance()->log_message(*msg);
506 91287 : }
507 :
508 :
509 0 : void send_stack_trace(libexcept::exception_base_t const & e)
510 : {
511 0 : libexcept::stack_trace_t const & stack(e.get_stack_trace());
512 0 : for(auto const & s : stack)
513 : {
514 0 : SNAP_LOG_EXCEPTION
515 : << s
516 : << SNAP_LOG_SEND;
517 : }
518 0 : }
519 :
520 :
521 : } // snaplogger namespace
522 : // vim: ts=4 sw=4 et
|