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