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