Line data Source code
1 : /*
2 : * Copyright (c) 2013-2021 Made to Order Software Corp. All Rights Reserved
3 : *
4 : * https://snapwebsites.org/project/snaplogger
5 : * contact@m2osw.com
6 : *
7 : * This program is free software; you can redistribute it and/or modify
8 : * it under the terms of the GNU General Public License as published by
9 : * the Free Software Foundation; either version 2 of the License, or
10 : * (at your option) any later version.
11 : *
12 : * This program is distributed in the hope that it will be useful,
13 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 : * GNU General Public License for more details.
16 : *
17 : * You should have received a copy of the GNU General Public License along
18 : * with this program; if not, write to the Free Software Foundation, Inc.,
19 : * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 : */
21 :
22 : /** \file
23 : * \brief Appenders are used to append data to somewhere.
24 : *
25 : * This file implements the base appender class.
26 : */
27 :
28 :
29 : // self
30 : //
31 : #include "snaplogger/appender.h"
32 :
33 : #include "snaplogger/exception.h"
34 : #include "snaplogger/guard.h"
35 : #include "snaplogger/private_logger.h"
36 :
37 :
38 : // snapdev lib
39 : //
40 : #include <snapdev/empty_set_intersection.h>
41 : #include <snapdev/not_used.h>
42 :
43 :
44 : // C++ lib
45 : //
46 : #include <iostream>
47 :
48 :
49 : // last include
50 : //
51 : #include <snapdev/poison.h>
52 :
53 :
54 :
55 : namespace snaplogger
56 : {
57 :
58 :
59 : namespace
60 : {
61 :
62 :
63 :
64 : // if we want to be able to reference such we need to create it TBD
65 : // (and it should probably be in the null_appender.cpp file instead)
66 : //APPENDER_FACTORY(null);
67 :
68 :
69 : }
70 :
71 :
72 18 : appender::appender(std::string const & name, std::string const & type)
73 : : f_type(type)
74 : , f_name(name)
75 18 : , f_normal_component(get_component(COMPONENT_NORMAL))
76 : {
77 36 : guard g;
78 :
79 18 : f_format = get_private_logger()->get_default_format();
80 18 : }
81 :
82 :
83 18 : appender::~appender()
84 : {
85 18 : }
86 :
87 :
88 3 : std::string const & appender::get_type() const
89 : {
90 3 : return f_type;
91 : }
92 :
93 :
94 0 : void appender::set_name(std::string const & name)
95 : {
96 0 : if(f_name != "console"
97 0 : && f_name != "syslog")
98 : {
99 : throw invalid_parameter(
100 0 : "the appender set_name() can only be used for the console & syslog appenders to rename them to your own appender name (and done internally only).");
101 : }
102 :
103 0 : f_name = name;
104 0 : }
105 :
106 :
107 1 : std::string const & appender::get_name() const
108 : {
109 1 : return f_name;
110 : }
111 :
112 :
113 91746 : bool appender::is_enabled() const
114 : {
115 91746 : return f_enabled;
116 : }
117 :
118 :
119 0 : void appender::set_enabled(bool status)
120 : {
121 0 : guard g;
122 :
123 0 : f_enabled = status;
124 0 : }
125 :
126 :
127 18 : bool appender::unique() const
128 : {
129 18 : return false;
130 : }
131 :
132 :
133 300 : severity_t appender::get_severity() const
134 : {
135 600 : guard g;
136 :
137 600 : return f_severity;
138 : }
139 :
140 :
141 284 : void appender::set_severity(severity_t severity_level)
142 : {
143 568 : guard g;
144 :
145 284 : f_severity = severity_level;
146 284 : logger::get_instance()->severity_changed(severity_level);
147 284 : }
148 :
149 :
150 0 : void appender::reduce_severity(severity_t severity_level)
151 : {
152 0 : guard g;
153 :
154 0 : if(severity_level < f_severity)
155 : {
156 0 : set_severity(severity_level);
157 : }
158 0 : }
159 :
160 :
161 0 : bool appender::operator < (appender const & rhs) const
162 : {
163 0 : return f_severity < rhs.f_severity;
164 : }
165 :
166 :
167 18 : void appender::set_config(advgetopt::getopt const & opts)
168 : {
169 36 : guard g;
170 :
171 : // ENABLE
172 : //
173 : {
174 36 : std::string const specialized_enabled(f_name + "::enabled");
175 18 : if(opts.is_defined(specialized_enabled))
176 : {
177 0 : f_enabled = opts.get_string(specialized_enabled) != "false";
178 : }
179 18 : else if(opts.is_defined("enabled"))
180 : {
181 0 : f_enabled = opts.get_string("enabled") != "false";
182 : }
183 : else
184 : {
185 18 : f_enabled = true;
186 : }
187 : }
188 :
189 : // FORMAT
190 : //
191 : {
192 36 : std::string const specialized_format(f_name + "::format");
193 18 : if(opts.is_defined(specialized_format))
194 : {
195 0 : f_format = std::make_shared<format>(opts.get_string(specialized_format));
196 : }
197 18 : else if(opts.is_defined("format"))
198 : {
199 0 : f_format = std::make_shared<format>(opts.get_string("format"));
200 : }
201 : }
202 :
203 : // SEVERITY
204 : //
205 36 : std::string const specialized_severity(f_name + "::severity");
206 18 : if(opts.is_defined(specialized_severity))
207 : {
208 0 : std::string const severity_name(opts.get_string(specialized_severity));
209 0 : severity::pointer_t sev(snaplogger::get_severity(severity_name));
210 0 : if(sev != nullptr)
211 : {
212 0 : set_severity(sev->get_severity());
213 : }
214 : else
215 : {
216 : throw invalid_severity(
217 : "severity level named \""
218 0 : + severity_name
219 0 : + "\" not found.");
220 : }
221 : }
222 18 : else if(opts.is_defined("severity"))
223 : {
224 0 : std::string const severity_name(opts.get_string("severity"));
225 0 : severity::pointer_t sev(snaplogger::get_severity(severity_name));
226 0 : if(sev != nullptr)
227 : {
228 0 : set_severity(sev->get_severity());
229 : }
230 : else
231 : {
232 : throw invalid_severity(
233 : "severity level named \""
234 0 : + severity_name
235 0 : + "\" not found.");
236 : }
237 : }
238 :
239 : // COMPONENTS
240 : //
241 36 : std::string comp;
242 36 : std::string const components(f_name + "::components");
243 18 : if(opts.is_defined(components))
244 : {
245 0 : comp = opts.get_string(components);
246 : }
247 18 : else if(opts.is_defined("components"))
248 : {
249 0 : comp = opts.get_string("components");
250 : }
251 18 : if(comp.empty())
252 : {
253 18 : add_component(f_normal_component);
254 : }
255 : else
256 : {
257 0 : advgetopt::string_list_t component_names;
258 0 : advgetopt::split_string(comp, component_names, {","});
259 0 : for(auto name : component_names)
260 : {
261 0 : add_component(get_component(name));
262 : }
263 : }
264 :
265 : // FILTER
266 : //
267 : {
268 36 : std::string filter;
269 36 : std::string const specialized_filter(f_name + "::filter");
270 18 : if(opts.is_defined(specialized_filter))
271 : {
272 0 : filter = opts.get_string(specialized_filter);
273 : }
274 18 : else if(opts.is_defined("filter"))
275 : {
276 0 : filter = opts.get_string("filter");
277 : }
278 18 : if(!filter.empty())
279 : {
280 0 : std::regex_constants::syntax_option_type flags(std::regex::nosubs | std::regex::optimize);
281 0 : std::regex_constants::syntax_option_type type(std::regex::extended);
282 0 : if(filter[0] == '/')
283 : {
284 0 : std::string::size_type pos(filter.rfind('/'));
285 0 : if(pos == 0)
286 : {
287 : throw invalid_variable(
288 : "invalid filter \""
289 0 : + filter
290 0 : + "\"; missing ending '/'.");
291 : }
292 0 : std::string const flag_list(filter.substr(pos + 1));
293 0 : filter = filter.substr(1, pos - 2);
294 0 : if(filter.empty())
295 : {
296 : throw invalid_variable(
297 : "invalid filter \""
298 0 : + filter
299 0 : + "\"; the regular expression is empty.");
300 : }
301 : // TODO: for errors we would need to iterate using the libutf8
302 : // (since we could have a Unicode character after the /)
303 : //
304 : // TODO: if two type flags are found, err too
305 : //
306 0 : int count(0);
307 0 : for(auto f : flag_list)
308 : {
309 0 : switch(f)
310 : {
311 0 : case 'i':
312 0 : flags |= std::regex::icase;
313 0 : break;
314 :
315 0 : case 'c':
316 0 : flags |= std::regex::collate;
317 0 : break;
318 :
319 0 : case 'j':
320 0 : type = std::regex::ECMAScript;
321 0 : ++count;
322 0 : break;
323 :
324 0 : case 'b':
325 0 : type = std::regex::basic;
326 0 : ++count;
327 0 : break;
328 :
329 0 : case 'x':
330 0 : type = std::regex::extended;
331 0 : ++count;
332 0 : break;
333 :
334 0 : case 'a':
335 0 : type = std::regex::awk;
336 0 : ++count;
337 0 : break;
338 :
339 0 : case 'g':
340 0 : type = std::regex::grep;
341 0 : ++count;
342 0 : break;
343 :
344 0 : case 'e':
345 0 : type = std::regex::egrep;
346 0 : ++count;
347 0 : break;
348 :
349 0 : default:
350 : throw invalid_variable(
351 : "in \""
352 0 : + filter
353 0 : + "\", found invalid flag '"
354 0 : + f
355 0 : + "'.");
356 :
357 : }
358 0 : if(count > 1)
359 : {
360 : throw invalid_variable(
361 : "found multiple types in \""
362 0 : + filter
363 0 : + "\".");
364 : }
365 : }
366 : }
367 0 : f_filter = std::make_shared<std::regex>(filter, flags | type);
368 : }
369 : }
370 :
371 : // REPEAT
372 : //
373 : {
374 18 : long no_repeat_size(0);
375 36 : std::string const no_repeat(f_name + "::no-repeat");
376 18 : if(opts.is_defined(no_repeat))
377 : {
378 0 : no_repeat_size = opts.get_long(no_repeat, 0, 0, 100);
379 : }
380 18 : else if(opts.is_defined("no-repeat"))
381 : {
382 0 : no_repeat_size = opts.get_long("no-repeat", 0, 0, 100);
383 : }
384 :
385 18 : if(no_repeat_size > 0)
386 : {
387 0 : if(no_repeat_size > 100)
388 : {
389 : // for now the top limit is 100 messages; much more and the
390 : // search would start to be slow
391 : //
392 0 : no_repeat_size = 100;
393 : }
394 0 : f_last_messages.resize(no_repeat_size);
395 : }
396 : }
397 18 : }
398 :
399 :
400 0 : void appender::reopen()
401 : {
402 0 : }
403 :
404 :
405 19 : void appender::add_component(component::pointer_t comp)
406 : {
407 19 : f_components.insert(comp);
408 19 : }
409 :
410 :
411 40 : format::pointer_t appender::set_format(format::pointer_t new_format)
412 : {
413 80 : guard g;
414 :
415 40 : format::pointer_t old(f_format);
416 40 : f_format = new_format;
417 80 : return old;
418 : }
419 :
420 :
421 91746 : void appender::send_message(message const & msg)
422 : {
423 183492 : if(!is_enabled()
424 91746 : || msg.get_severity() < f_severity)
425 : {
426 96591 : return;
427 : }
428 :
429 45724 : component::set_t const & components(msg.get_components());
430 45724 : if(components.empty())
431 : {
432 : // user did not supply any component in 'msg', check for
433 : // the normal component
434 : //
435 91440 : if(!f_components.empty()
436 45720 : && f_components.find(f_normal_component) == f_components.end())
437 : {
438 0 : return;
439 : }
440 : }
441 : else
442 : {
443 4 : if(snap::empty_set_intersection(f_components, components))
444 : {
445 2 : return;
446 : }
447 : }
448 :
449 86894 : std::string formatted_message(f_format->process_message(msg));
450 45717 : if(formatted_message.empty())
451 : {
452 4545 : return;
453 : }
454 :
455 82344 : if(f_filter != nullptr
456 41172 : && !std::regex_match(formatted_message, *f_filter))
457 : {
458 0 : return;
459 : }
460 :
461 82344 : if(formatted_message.back() != '\n'
462 41172 : && formatted_message.back() != '\r')
463 : {
464 : // TODO: add support to define line terminator (cr, nl, cr nl)
465 : //
466 41172 : formatted_message += '\n';
467 : }
468 :
469 41172 : if(!f_last_messages.empty())
470 : {
471 0 : if(std::find(f_last_messages.begin(), f_last_messages.end(), formatted_message) != f_last_messages.end())
472 : {
473 : // completely ignore repeated messages
474 : //
475 : // TODO: look into a way to count said messages and print out
476 : // the total number or something of the sort...
477 : // (maybe we store those messages in a buffer and once
478 : // we are to replace a message, that's when we forward
479 : // it and that can include the count?)
480 : //
481 0 : return;
482 : }
483 0 : if(f_last_message_index >= f_last_messages.size())
484 : {
485 0 : f_last_message_index = 0;
486 : }
487 0 : f_last_messages[f_last_message_index] = formatted_message;
488 0 : ++f_last_message_index;
489 : }
490 :
491 41172 : process_message(msg, formatted_message);
492 : }
493 :
494 :
495 0 : void appender::process_message(message const & msg, std::string const & formatted_message)
496 : {
497 : // the default is a "null appender" -- do nothing
498 0 : snap::NOTUSED(msg);
499 0 : snap::NOTUSED(formatted_message);
500 0 : }
501 :
502 :
503 :
504 :
505 :
506 8 : appender_factory::appender_factory(std::string const & type)
507 8 : : f_type(type)
508 : {
509 8 : }
510 :
511 :
512 8 : appender_factory::~appender_factory()
513 : {
514 8 : }
515 :
516 :
517 16 : std::string const & appender_factory::get_type() const
518 : {
519 16 : return f_type;
520 : }
521 :
522 :
523 :
524 :
525 8 : void register_appender_factory(appender_factory::pointer_t factory)
526 : {
527 8 : get_private_logger()->register_appender_factory(factory);
528 8 : }
529 :
530 :
531 2 : appender::pointer_t create_appender(std::string const & type, std::string const & name)
532 : {
533 2 : return get_private_logger()->create_appender(type, name);
534 : }
535 :
536 :
537 :
538 :
539 :
540 :
541 0 : safe_format::safe_format(appender::pointer_t a, format::pointer_t new_format)
542 : : f_appender(a)
543 0 : , f_old_format(a->set_format(new_format))
544 : {
545 0 : }
546 :
547 :
548 0 : safe_format::~safe_format()
549 : {
550 0 : snap::NOTUSED(f_appender->set_format(f_old_format));
551 0 : }
552 :
553 :
554 :
555 :
556 :
557 :
558 :
559 6 : } // snaplogger namespace
560 : // vim: ts=4 sw=4 et
|