Line data Source code
1 : /*
2 : * Copyright (c) 2013-2019 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 15 : appender::appender(std::string const & name, std::string const & type)
73 : : f_type(type)
74 : , f_name(name)
75 15 : , f_normal_component(get_component(COMPONENT_NORMAL))
76 : {
77 30 : guard g;
78 :
79 15 : f_format = get_private_logger()->get_default_format();
80 15 : }
81 :
82 :
83 15 : appender::~appender()
84 : {
85 15 : }
86 :
87 :
88 3 : std::string const & appender::get_type() const
89 : {
90 3 : return f_type;
91 : }
92 :
93 :
94 1 : std::string const & appender::get_name() const
95 : {
96 1 : return f_name;
97 : }
98 :
99 :
100 96411 : bool appender::is_enabled() const
101 : {
102 96411 : return f_enabled;
103 : }
104 :
105 :
106 0 : void appender::set_enabled(bool status)
107 : {
108 0 : guard g;
109 :
110 0 : f_enabled = status;
111 0 : }
112 :
113 :
114 15 : bool appender::unique() const
115 : {
116 15 : return false;
117 : }
118 :
119 :
120 302 : severity_t appender::get_severity() const
121 : {
122 604 : guard g;
123 :
124 604 : return f_severity;
125 : }
126 :
127 :
128 289 : void appender::set_severity(severity_t severity_level)
129 : {
130 578 : guard g;
131 :
132 289 : f_severity = severity_level;
133 289 : logger::get_instance()->severity_changed(severity_level);
134 289 : }
135 :
136 :
137 0 : void appender::reduce_severity(severity_t severity_level)
138 : {
139 0 : guard g;
140 :
141 0 : if(severity_level < f_severity)
142 : {
143 0 : set_severity(severity_level);
144 : }
145 0 : }
146 :
147 :
148 0 : bool appender::operator < (appender const & rhs) const
149 : {
150 0 : return f_severity < rhs.f_severity;
151 : }
152 :
153 :
154 15 : void appender::set_config(advgetopt::getopt const & opts)
155 : {
156 30 : guard g;
157 :
158 : // ENABLE
159 : //
160 : {
161 30 : std::string const specialized_enabled(f_name + "::enabled");
162 15 : if(opts.is_defined(specialized_enabled))
163 : {
164 0 : f_enabled = opts.get_string(specialized_enabled) != "false";
165 : }
166 15 : else if(opts.is_defined("enabled"))
167 : {
168 0 : f_enabled = opts.get_string("enabled") != "false";
169 : }
170 : else
171 : {
172 15 : f_enabled = true;
173 : }
174 : }
175 :
176 : // FORMAT
177 : //
178 : {
179 30 : std::string const specialized_format(f_name + "::format");
180 15 : if(opts.is_defined(specialized_format))
181 : {
182 0 : f_format = std::make_shared<format>(opts.get_string(specialized_format));
183 : }
184 15 : else if(opts.is_defined("format"))
185 : {
186 0 : f_format = std::make_shared<format>(opts.get_string("format"));
187 : }
188 : }
189 :
190 : // SEVERITY
191 : //
192 30 : std::string const specialized_severity(f_name + "::severity");
193 15 : if(opts.is_defined(specialized_severity))
194 : {
195 0 : std::string const severity_name(opts.get_string(specialized_severity));
196 0 : severity::pointer_t sev(snaplogger::get_severity(severity_name));
197 0 : if(sev != nullptr)
198 : {
199 0 : set_severity(sev->get_severity());
200 : }
201 : else
202 : {
203 : throw invalid_severity(
204 : "severity level named \""
205 0 : + severity_name
206 0 : + "\" not found.");
207 : }
208 : }
209 15 : else if(opts.is_defined("severity"))
210 : {
211 0 : std::string const severity_name(opts.get_string("severity"));
212 0 : severity::pointer_t sev(snaplogger::get_severity(severity_name));
213 0 : if(sev != nullptr)
214 : {
215 0 : set_severity(sev->get_severity());
216 : }
217 : else
218 : {
219 : throw invalid_severity(
220 : "severity level named \""
221 0 : + severity_name
222 0 : + "\" not found.");
223 : }
224 : }
225 :
226 : // COMPONENTS
227 : //
228 30 : std::string comp;
229 30 : std::string const components(f_name + "::components");
230 15 : if(opts.is_defined(components))
231 : {
232 0 : comp = opts.get_string(components);
233 : }
234 15 : else if(opts.is_defined("components"))
235 : {
236 0 : comp = opts.get_string("components");
237 : }
238 15 : if(comp.empty())
239 : {
240 15 : add_component(f_normal_component);
241 : }
242 : else
243 : {
244 0 : advgetopt::string_list_t component_names;
245 0 : advgetopt::split_string(comp, component_names, {","});
246 0 : for(auto name : component_names)
247 : {
248 0 : add_component(get_component(name));
249 : }
250 : }
251 :
252 : // FILTER
253 : //
254 : {
255 30 : std::string filter;
256 30 : std::string const specialized_filter(f_name + "::filter");
257 15 : if(opts.is_defined(specialized_filter))
258 : {
259 0 : filter = opts.get_string(specialized_filter);
260 : }
261 15 : else if(opts.is_defined("filter"))
262 : {
263 0 : filter = opts.get_string("filter");
264 : }
265 15 : if(!filter.empty())
266 : {
267 0 : std::regex_constants::syntax_option_type flags(std::regex::nosubs | std::regex::optimize);
268 0 : std::regex_constants::syntax_option_type type(std::regex::extended);
269 0 : if(filter[0] == '/')
270 : {
271 0 : std::string::size_type pos(filter.rfind('/'));
272 0 : if(pos == 0)
273 : {
274 : throw invalid_variable(
275 : "invalid filter \""
276 0 : + filter
277 0 : + "\"; missing ending '/'.");
278 : }
279 0 : std::string const flag_list(filter.substr(pos + 1));
280 0 : filter = filter.substr(1, pos - 2);
281 0 : if(filter.empty())
282 : {
283 : throw invalid_variable(
284 : "invalid filter \""
285 0 : + filter
286 0 : + "\"; the regular expression is empty.");
287 : }
288 : // TODO: for errors we would need to iterate using the libutf8
289 : // (since we could have a Unicode character after the /)
290 : //
291 : // TODO: if two type flags are found, err too
292 : //
293 0 : int count(0);
294 0 : for(auto f : flag_list)
295 : {
296 0 : switch(f)
297 : {
298 : case 'i':
299 0 : flags |= std::regex::icase;
300 0 : break;
301 :
302 : case 'c':
303 0 : flags |= std::regex::collate;
304 0 : break;
305 :
306 : case 'j':
307 0 : type = std::regex::ECMAScript;
308 0 : ++count;
309 0 : break;
310 :
311 : case 'b':
312 0 : type = std::regex::basic;
313 0 : ++count;
314 0 : break;
315 :
316 : case 'x':
317 0 : type = std::regex::extended;
318 0 : ++count;
319 0 : break;
320 :
321 : case 'a':
322 0 : type = std::regex::awk;
323 0 : ++count;
324 0 : break;
325 :
326 : case 'g':
327 0 : type = std::regex::grep;
328 0 : ++count;
329 0 : break;
330 :
331 : case 'e':
332 0 : type = std::regex::egrep;
333 0 : ++count;
334 0 : break;
335 :
336 : default:
337 : throw invalid_variable(
338 : "in \""
339 0 : + filter
340 0 : + "\", found invalid flag '"
341 0 : + f
342 0 : + "'.");
343 :
344 : }
345 0 : if(count > 1)
346 : {
347 : throw invalid_variable(
348 : "found multiple types in \""
349 0 : + filter
350 0 : + "\".");
351 : }
352 : }
353 : }
354 0 : f_filter = std::make_shared<std::regex>(filter, flags | type);
355 : }
356 : }
357 15 : }
358 :
359 :
360 0 : void appender::reopen()
361 : {
362 0 : }
363 :
364 :
365 16 : void appender::add_component(component::pointer_t comp)
366 : {
367 16 : f_components.insert(comp);
368 16 : }
369 :
370 :
371 18 : format::pointer_t appender::set_format(format::pointer_t new_format)
372 : {
373 36 : guard g;
374 :
375 18 : format::pointer_t old(f_format);
376 18 : f_format = new_format;
377 36 : return old;
378 : }
379 :
380 :
381 96411 : void appender::send_message(message const & msg)
382 : {
383 192822 : if(!is_enabled()
384 96411 : || msg.get_severity() < f_severity)
385 : {
386 101131 : return;
387 : }
388 :
389 48730 : component::set_t const & components(msg.get_components());
390 48730 : if(components.empty())
391 : {
392 : // user did not supply any component in 'msg', check for
393 : // the normal component
394 : //
395 146178 : if(!f_components.empty()
396 194904 : && f_components.find(f_normal_component) == f_components.end())
397 : {
398 0 : return;
399 : }
400 : }
401 : else
402 : {
403 4 : if(snap::empty_set_intersection(f_components, components))
404 : {
405 2 : return;
406 : }
407 : }
408 :
409 91687 : std::string formatted_message(f_format->process_message(msg));
410 48726 : if(formatted_message.empty())
411 : {
412 5767 : return;
413 : }
414 :
415 85918 : if(f_filter != nullptr
416 42959 : && !std::regex_match(formatted_message, *f_filter))
417 : {
418 0 : return;
419 : }
420 :
421 85918 : if(formatted_message.back() != '\n'
422 42959 : && formatted_message.back() != '\r')
423 : {
424 : // TODO: add support to define line terminator (cr, nl, cr nl)
425 : //
426 42959 : formatted_message += '\n';
427 : }
428 :
429 42959 : process_message(msg, formatted_message);
430 : }
431 :
432 :
433 0 : void appender::process_message(message const & msg, std::string const & formatted_message)
434 : {
435 : // the default is a "null appender" -- do nothing
436 0 : snap::NOTUSED(msg);
437 0 : snap::NOTUSED(formatted_message);
438 0 : }
439 :
440 :
441 :
442 :
443 :
444 8 : appender_factory::appender_factory(std::string const & type)
445 8 : : f_type(type)
446 : {
447 8 : }
448 :
449 :
450 8 : appender_factory::~appender_factory()
451 : {
452 8 : }
453 :
454 :
455 16 : std::string const & appender_factory::get_type() const
456 : {
457 16 : return f_type;
458 : }
459 :
460 :
461 :
462 :
463 8 : void register_appender_factory(appender_factory::pointer_t factory)
464 : {
465 8 : get_private_logger()->register_appender_factory(factory);
466 8 : }
467 :
468 :
469 2 : appender::pointer_t create_appender(std::string const & type, std::string const & name)
470 : {
471 2 : return get_private_logger()->create_appender(type, name);
472 : }
473 :
474 :
475 :
476 :
477 :
478 :
479 0 : safe_format::safe_format(appender::pointer_t a, format::pointer_t new_format)
480 : : f_appender(a)
481 0 : , f_old_format(a->set_format(new_format))
482 : {
483 0 : }
484 :
485 :
486 0 : safe_format::~safe_format()
487 : {
488 0 : snap::NOTUSED(f_appender->set_format(f_old_format));
489 0 : }
490 :
491 :
492 :
493 :
494 :
495 :
496 :
497 6 : } // snaplogger namespace
498 : // vim: ts=4 sw=4 et
|