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 488 : DEFINE_LOGGER_VARIABLE(direct)
92 : {
93 168 : snapdev::NOT_USED(msg);
94 :
95 : // insert all our parameters as is
96 : //
97 336 : auto const & params(get_params());
98 336 : for(auto p : params)
99 : {
100 : // TODO: should we add a space too? or can we have spaces in the params?
101 168 : value += p->get_value();
102 : }
103 :
104 : // do NOT apply parameters further, the user has no access to those
105 : // anyway; this is the direct text we find in between variables
106 : //
107 : //variable::process_value(msg, value);
108 168 : }
109 :
110 :
111 :
112 : }
113 : // no name namespace
114 :
115 :
116 :
117 : //////////////////////////////
118 : // PARAM
119 : //
120 :
121 205 : param::param(std::string const & name)
122 206 : : f_name(name)
123 : {
124 205 : if(f_name.empty())
125 : {
126 1 : throw invalid_parameter("a parameter must have a non-empty name.");
127 : }
128 204 : }
129 :
130 :
131 223 : std::string const & param::get_name() const
132 : {
133 223 : return f_name;
134 : }
135 :
136 :
137 20 : param::type_t param::get_type() const
138 : {
139 20 : return f_type;
140 : }
141 :
142 :
143 278 : std::string param::get_value() const
144 : {
145 278 : if(f_type != type_t::TYPE_STRING)
146 : {
147 : // TBD: we may instead want to return the integer as a string
148 : //
149 : throw invalid_parameter(
150 : "the ${...:"
151 2 : + f_name
152 3 : + "=<value>} parameter must be a valid string (not an integer).");
153 : }
154 277 : return f_value;
155 : }
156 :
157 :
158 165 : void param::set_value(std::string const & value)
159 : {
160 165 : f_value = value;
161 165 : f_type = type_t::TYPE_STRING;
162 165 : }
163 :
164 :
165 36 : int64_t param::get_integer() const
166 : {
167 36 : if(f_type != type_t::TYPE_INTEGER)
168 : {
169 : // TBD: we may want to check whether the string represents a valid
170 : // integer first and return that if so
171 : //
172 : throw invalid_parameter(
173 : "the ${...:"
174 2 : + f_name
175 3 : + "=<value>} parameter must be a valid integer.");
176 : }
177 35 : return f_integer;
178 : }
179 :
180 :
181 30 : void param::set_integer(std::int64_t integer)
182 : {
183 30 : f_integer = integer;
184 30 : f_type = type_t::TYPE_INTEGER;
185 30 : }
186 :
187 :
188 :
189 :
190 :
191 : //////////////////////////////
192 : // VARIABLE
193 : //
194 :
195 229 : variable::~variable()
196 : {
197 229 : }
198 :
199 :
200 0 : bool variable::ignore_on_no_repeat() const
201 : {
202 0 : return false;
203 : }
204 :
205 :
206 204 : void variable::add_param(param::pointer_t p)
207 : {
208 408 : guard g;
209 :
210 204 : f_params.push_back(p);
211 204 : }
212 :
213 :
214 270 : param::vector_t variable::get_params() const
215 : {
216 540 : guard g;
217 :
218 540 : return f_params;
219 : }
220 :
221 :
222 49186 : std::string variable::get_value(message const & msg) const
223 : {
224 98372 : guard g;
225 :
226 49186 : std::string value;
227 49186 : process_value(msg, value);
228 98360 : return value;
229 : }
230 :
231 :
232 49015 : void variable::process_value(message const & msg, std::string & value) const
233 : {
234 98030 : auto l(get_private_logger(msg));
235 :
236 : {
237 98030 : guard g;
238 :
239 49015 : if(!l->has_functions())
240 : {
241 : // no functions available, we're done
242 : return; // LCOV_EXCL_LINE
243 : }
244 : }
245 :
246 98030 : function_data d;
247 : try
248 : {
249 49015 : d.set_value(value);
250 : }
251 0 : catch(libutf8::libutf8_exception_decoding const & e)
252 : {
253 : // ignore this exception because an invalid byte in a message is
254 : // something somewhat common and here it just means the functions
255 : // won't be applied, not that this specific log fails 100%
256 : //
257 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: \"";
258 0 : value += e.what();
259 0 : value += "\"} ";
260 0 : return;
261 : }
262 :
263 49167 : for(auto p : f_params)
264 : {
265 152 : std::string const & name(p->get_name());
266 304 : auto func(l->get_function(name));
267 152 : if(func != nullptr)
268 : {
269 109 : func->apply(msg, d, p);
270 : }
271 : // else -- ignore missing functions
272 : }
273 :
274 49010 : value = libutf8::to_u8string(d.get_value());
275 : }
276 :
277 :
278 :
279 :
280 :
281 :
282 :
283 :
284 :
285 :
286 :
287 : //////////////////////////////
288 : // VARIABLE FACTORY
289 : //
290 :
291 63 : variable_factory::variable_factory(std::string const & type)
292 63 : : f_type(type)
293 : {
294 63 : }
295 :
296 :
297 63 : variable_factory::~variable_factory()
298 : {
299 63 : }
300 :
301 :
302 126 : std::string const & variable_factory::get_type() const
303 : {
304 126 : return f_type;
305 : }
306 :
307 :
308 :
309 :
310 63 : void register_variable_factory(variable_factory::pointer_t factory)
311 : {
312 64 : get_private_logger()->register_variable_factory(factory);
313 62 : }
314 :
315 :
316 :
317 230 : variable::pointer_t get_variable(std::string const & type)
318 : {
319 230 : return get_private_logger()->get_variable(type);
320 : }
321 :
322 :
323 :
324 :
325 :
326 :
327 :
328 : //////////////////////////////
329 : // FUNCTION DATA
330 : //
331 :
332 49015 : void function_data::set_value(std::string value)
333 : {
334 49015 : f_value = libutf8::to_u32string(value);
335 49015 : }
336 :
337 44 : void function_data::set_value(std::u32string value)
338 : {
339 44 : f_value = value;
340 44 : }
341 :
342 49132 : std::u32string & function_data::get_value()
343 : {
344 49132 : return f_value;
345 : }
346 :
347 16 : void function_data::set_param(std::string const & name, std::string const & value)
348 : {
349 16 : f_params[name] = libutf8::to_u32string(value);
350 16 : }
351 :
352 18 : void function_data::set_param(std::string const & name, std::u32string const & value)
353 : {
354 18 : f_params[name] = value;
355 18 : }
356 :
357 35 : std::u32string function_data::get_param(std::string const & name, std::u32string const & default_value)
358 : {
359 35 : auto it(f_params.find(name));
360 35 : if(it == f_params.end())
361 : {
362 10 : return default_value;
363 : }
364 25 : return it->second;
365 : }
366 :
367 :
368 :
369 :
370 :
371 : //////////////////////////////
372 : // FUNCTION
373 : //
374 :
375 23 : function::function(std::string const & function_name)
376 23 : : f_name(function_name)
377 : {
378 23 : }
379 :
380 :
381 23 : function::~function()
382 : {
383 23 : }
384 :
385 :
386 46 : std::string const & function::get_name() const
387 : {
388 46 : return f_name;
389 : }
390 :
391 :
392 :
393 23 : void register_function(function::pointer_t func)
394 : {
395 46 : guard g;
396 :
397 24 : get_private_logger()->register_function(func);
398 22 : }
399 :
400 :
401 :
402 :
403 6 : } // snaplogger namespace
404 : // vim: ts=4 sw=4 et
|