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