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 Variables are used to dynamically add parameters to log messages.
22 : *
23 : * This file declares the base variable class.
24 : *
25 : * The format defines \em functions which are written as in
26 : * `${function-name}`.
27 : *
28 : * Parameters can be passed to these functions by adding `:<param>`
29 : * to those definitions. These are named parameters and their default
30 : * value is "present" or not. A specific value can be assignd using
31 : * the equal sign as in `:param=<value>`.
32 : *
33 : * For example, the date function can be called as follow:
34 : *
35 : * \code
36 : * ${date:year:align=right:exact_width=2}
37 : * \endcode
38 : *
39 : * The `year` parameter is specific to the `date` function. The other
40 : * parameters are available whatever the function. This variable asks
41 : * to truncate the year to 2 character right aligned (i.e. "18" in
42 : * "2018".)
43 : *
44 : * In C, this would look something like:
45 : *
46 : * \code
47 : * date(FLAG_YEAR, ALIGN_RIGHT, 2);
48 : * \endcode
49 : */
50 :
51 :
52 : // self
53 : //
54 : #include "snaplogger/variable.h"
55 :
56 : #include "snaplogger/exception.h"
57 : #include "snaplogger/guard.h"
58 : #include "snaplogger/private_logger.h"
59 :
60 :
61 : // libutf8 lib
62 : //
63 : #include <libutf8/exception.h>
64 : #include <libutf8/libutf8.h>
65 :
66 :
67 : // C++ lib
68 : //
69 : #include <iostream>
70 : #include <queue>
71 :
72 :
73 : // last include
74 : //
75 : #include <snapdev/poison.h>
76 :
77 :
78 :
79 : namespace snaplogger
80 : {
81 :
82 :
83 :
84 : namespace
85 : {
86 :
87 :
88 :
89 :
90 :
91 425 : DEFINE_LOGGER_VARIABLE(direct)
92 : {
93 123 : snap::NOTUSED(msg);
94 :
95 : // apply all our parameters as is
96 : //
97 246 : auto const & params(get_params());
98 246 : for(auto p : params)
99 : {
100 123 : value += p->get_value();
101 : }
102 :
103 : // do NOT apply parameters further, the user has no access to those
104 : // anyway; this is the direct text we find in between variables
105 : //
106 : //variable::process_value(msg, value);
107 123 : }
108 :
109 :
110 :
111 : }
112 : // no name namespace
113 :
114 :
115 :
116 : //////////////////////////////
117 : // PARAM
118 : //
119 :
120 194 : param::param(std::string const & name)
121 195 : : f_name(name)
122 : {
123 194 : if(f_name.empty())
124 : {
125 1 : throw invalid_parameter("a parameter must have a non-empty name.");
126 : }
127 193 : }
128 :
129 :
130 137 : std::string const & param::get_name() const
131 : {
132 137 : return f_name;
133 : }
134 :
135 :
136 20 : param::type_t param::get_type() const
137 : {
138 20 : return f_type;
139 : }
140 :
141 :
142 203 : std::string param::get_value() const
143 : {
144 203 : if(f_type != type_t::TYPE_STRING)
145 : {
146 : // TBD: we may instead want to return the integer as a string
147 : //
148 : throw invalid_parameter(
149 : "the ${...:"
150 2 : + f_name
151 3 : + "=<value>} parameter must be a valid string (not an integer).");
152 : }
153 202 : return f_value;
154 : }
155 :
156 :
157 154 : void param::set_value(std::string const & value)
158 : {
159 154 : f_value = value;
160 154 : f_type = type_t::TYPE_STRING;
161 154 : }
162 :
163 :
164 27 : int64_t param::get_integer() const
165 : {
166 27 : if(f_type != type_t::TYPE_INTEGER)
167 : {
168 : // TBD: we may want to check whether the string represents a valid
169 : // integer first and return that if so
170 : //
171 : throw invalid_parameter(
172 : "the ${...:"
173 2 : + f_name
174 3 : + "=<value>} parameter must be a valid integer.");
175 : }
176 26 : return f_integer;
177 : }
178 :
179 :
180 30 : void param::set_integer(std::int64_t integer)
181 : {
182 30 : f_integer = integer;
183 30 : f_type = type_t::TYPE_INTEGER;
184 30 : }
185 :
186 :
187 :
188 :
189 :
190 : //////////////////////////////
191 : // VARIABLE
192 : //
193 :
194 217 : variable::~variable()
195 : {
196 217 : }
197 :
198 :
199 0 : bool variable::ignore_on_no_repeat() const
200 : {
201 0 : return false;
202 : }
203 :
204 :
205 193 : void variable::add_param(param::pointer_t p)
206 : {
207 386 : guard g;
208 :
209 193 : f_params.push_back(p);
210 193 : }
211 :
212 :
213 188 : param::vector_t variable::get_params() const
214 : {
215 376 : guard g;
216 :
217 376 : return f_params;
218 : }
219 :
220 :
221 45591 : std::string variable::get_value(message const & msg) const
222 : {
223 91182 : guard g;
224 :
225 45591 : std::string value;
226 45591 : process_value(msg, value);
227 91172 : return value;
228 : }
229 :
230 :
231 45466 : void variable::process_value(message const & msg, std::string & value) const
232 : {
233 90932 : auto l(get_private_logger(msg));
234 :
235 : {
236 90932 : guard g;
237 :
238 45466 : if(!l->has_functions())
239 : {
240 : // no functions available, we're done
241 : return; // LCOV_EXCL_LINE
242 : }
243 : }
244 :
245 90932 : function_data d;
246 : try
247 : {
248 45466 : d.set_value(value);
249 : }
250 0 : catch(libutf8::libutf8_exception_decoding const & e)
251 : {
252 : // ignore this exception because an invalid byte in a message is
253 : // something somewhat common and here it just means the functions
254 : // won't be applied, not that this specific log fails 100%
255 : //
256 0 : value += " {WARNING: your value has invalid UTF-8 characters; do you use std::int8_t or std::uint8_t? those are often inserted as characters instead of numbers; exception message: \"";
257 0 : value += e.what();
258 0 : value += "\"} ";
259 0 : return;
260 : }
261 :
262 45562 : for(auto p : f_params)
263 : {
264 96 : std::string const & name(p->get_name());
265 192 : auto func(l->get_function(name));
266 96 : if(func != nullptr)
267 : {
268 73 : func->apply(msg, d, p);
269 : }
270 : // else -- ignore missing functions
271 : }
272 :
273 45461 : value = libutf8::to_u8string(d.get_value());
274 : }
275 :
276 :
277 :
278 :
279 :
280 :
281 :
282 :
283 :
284 :
285 :
286 : //////////////////////////////
287 : // VARIABLE FACTORY
288 : //
289 :
290 61 : variable_factory::variable_factory(std::string const & type)
291 61 : : f_type(type)
292 : {
293 61 : }
294 :
295 :
296 61 : variable_factory::~variable_factory()
297 : {
298 61 : }
299 :
300 :
301 122 : std::string const & variable_factory::get_type() const
302 : {
303 122 : return f_type;
304 : }
305 :
306 :
307 :
308 :
309 61 : void register_variable_factory(variable_factory::pointer_t factory)
310 : {
311 62 : get_private_logger()->register_variable_factory(factory);
312 60 : }
313 :
314 :
315 :
316 218 : variable::pointer_t get_variable(std::string const & type)
317 : {
318 218 : return get_private_logger()->get_variable(type);
319 : }
320 :
321 :
322 :
323 :
324 :
325 :
326 :
327 : //////////////////////////////
328 : // FUNCTION DATA
329 : //
330 :
331 45466 : void function_data::set_value(std::string value)
332 : {
333 45466 : f_value = libutf8::to_u32string(value);
334 45466 : }
335 :
336 26 : void function_data::set_value(std::u32string value)
337 : {
338 26 : f_value = value;
339 26 : }
340 :
341 45520 : std::u32string & function_data::get_value()
342 : {
343 45520 : return f_value;
344 : }
345 :
346 16 : void function_data::set_param(std::string const & name, std::string const & value)
347 : {
348 16 : f_params[name] = libutf8::to_u32string(value);
349 16 : }
350 :
351 18 : void function_data::set_param(std::string const & name, std::u32string const & value)
352 : {
353 18 : f_params[name] = value;
354 18 : }
355 :
356 26 : std::u32string function_data::get_param(std::string const & name, std::u32string const & default_value)
357 : {
358 26 : auto it(f_params.find(name));
359 26 : if(it == f_params.end())
360 : {
361 1 : return default_value;
362 : }
363 25 : return it->second;
364 : }
365 :
366 :
367 :
368 :
369 :
370 : //////////////////////////////
371 : // FUNCTION
372 : //
373 :
374 23 : function::function(std::string const & function_name)
375 23 : : f_name(function_name)
376 : {
377 23 : }
378 :
379 :
380 23 : function::~function()
381 : {
382 23 : }
383 :
384 :
385 46 : std::string const & function::get_name() const
386 : {
387 46 : return f_name;
388 : }
389 :
390 :
391 :
392 23 : void register_function(function::pointer_t func)
393 : {
394 46 : guard g;
395 :
396 24 : get_private_logger()->register_function(func);
397 22 : }
398 :
399 :
400 :
401 :
402 6 : } // snaplogger namespace
403 : // vim: ts=4 sw=4 et
|