Line data Source code
1 : /*
2 : * Copyright (c) 2006-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 : // self
23 : //
24 : #include "main.h"
25 :
26 :
27 : // snaplogger lib
28 : //
29 : #include <snaplogger/buffer_appender.h>
30 : #include <snaplogger/exception.h>
31 : #include <snaplogger/format.h>
32 : #include <snaplogger/logger.h>
33 : #include <snaplogger/map_diagnostic.h>
34 : #include <snaplogger/message.h>
35 : #include <snaplogger/severity.h>
36 : #include <snaplogger/version.h>
37 :
38 :
39 : // C lib
40 : //
41 : #include <unistd.h>
42 : #include <netdb.h>
43 : #include <sys/param.h>
44 :
45 :
46 :
47 :
48 :
49 3 : CATCH_TEST_CASE("variable_param", "[variable][param]")
50 : {
51 2 : CATCH_START_SECTION("Param Name is Mandatory")
52 : {
53 1 : CATCH_REQUIRE_THROWS_MATCHES(
54 : new snaplogger::param(std::string())
55 : , snaplogger::invalid_parameter
56 : , Catch::Matchers::ExceptionMessage(
57 : "logger_error: a parameter must have a non-empty name."));
58 : }
59 : CATCH_END_SECTION()
60 1 : }
61 :
62 :
63 :
64 7 : CATCH_TEST_CASE("system_variable", "[variable][param]")
65 : {
66 10 : CATCH_START_SECTION("get_type() to use padding as integer or string (hostname)")
67 : {
68 1 : snaplogger::set_diagnostic(snaplogger::DIAG_KEY_PROGNAME, "message-logging");
69 :
70 2 : snaplogger::logger::pointer_t l(snaplogger::logger::get_instance());
71 2 : snaplogger::buffer_appender::pointer_t buffer(std::make_shared<snaplogger::buffer_appender>("test-buffer"));
72 :
73 1 : advgetopt::options_environment opt_env;
74 1 : opt_env.f_project_name = "test-logger";
75 2 : advgetopt::getopt opts(opt_env);
76 1 : buffer->set_config(opts);
77 :
78 2 : snaplogger::format::pointer_t f(std::make_shared<snaplogger::format>("${hostname:padding=3:align=right:min_width=30} ${message}"));
79 1 : buffer->set_format(f);
80 :
81 1 : l->add_appender(buffer);
82 :
83 : char host[HOST_NAME_MAX + 2 + 30];
84 1 : CATCH_REQUIRE(gethostname(host, HOST_NAME_MAX + 1) == 0);
85 1 : host[HOST_NAME_MAX + 1] = '\0';
86 2 : std::string aligned(host);
87 47 : while(aligned.length() < 30)
88 : {
89 23 : aligned = "3" + aligned;
90 : }
91 :
92 1 : SNAP_LOG_ERROR << "Check the param::get_type()" << SNAP_LOG_SEND;
93 1 : CATCH_REQUIRE(buffer->str() == aligned + " Check the param::get_type()\n");
94 :
95 1 : buffer->clear();
96 :
97 1 : f = std::make_shared<snaplogger::format>("${hostname:padding=\"t\":align=center:min_width=30} ${message}");
98 1 : buffer->set_format(f);
99 :
100 1 : aligned = host;
101 1 : std::string::size_type const low((30 - aligned.length()) / 2 + aligned.length());
102 23 : while(aligned.length() < low)
103 : {
104 11 : aligned = "t" + aligned;
105 : }
106 25 : while(aligned.length() < 30)
107 : {
108 12 : aligned = aligned + "t";
109 : }
110 :
111 1 : SNAP_LOG_ERROR << "Try again with a string" << SNAP_LOG_SEND;
112 1 : CATCH_REQUIRE(buffer->str() == aligned + " Try again with a string\n");
113 :
114 1 : buffer->clear();
115 :
116 1 : f = std::make_shared<snaplogger::format>("${hostname} ${message:padding=\"t\":align=center:max_width=30}");
117 1 : buffer->set_format(f);
118 :
119 1 : SNAP_LOG_ERROR << "This message will have a maximum width of 30 chars" << SNAP_LOG_SEND;
120 1 : CATCH_REQUIRE(buffer->str() == std::string(host) + " ge will have a maximum width o\n");
121 :
122 1 : buffer->clear();
123 :
124 1 : f = std::make_shared<snaplogger::format>("${hostname} ${message:padding=\"t\":align=right:max_width=25}");
125 1 : buffer->set_format(f);
126 :
127 1 : SNAP_LOG_ERROR << "This message will have a maximum width of 25 chars" << SNAP_LOG_SEND;
128 1 : CATCH_REQUIRE(buffer->str() == std::string(host) + " maximum width of 25 chars\n");
129 :
130 1 : buffer->clear();
131 :
132 1 : f = std::make_shared<snaplogger::format>("${hostname} ${message:padding=\"t\":align=center:min_width=25}");
133 1 : buffer->set_format(f);
134 :
135 1 : SNAP_LOG_ERROR << "minimum width 25" << SNAP_LOG_SEND;
136 1 : CATCH_REQUIRE(buffer->str() == std::string(host) + " ttttminimum width 25ttttt\n");
137 :
138 1 : buffer->clear();
139 :
140 1 : f = std::make_shared<snaplogger::format>("${hostname} ${message:padding=\"t\":align=left:min_width=25}");
141 1 : buffer->set_format(f);
142 :
143 1 : SNAP_LOG_ERROR << "minimum width 25" << SNAP_LOG_SEND;
144 1 : CATCH_REQUIRE(buffer->str() == std::string(host) + " minimum width 25ttttttttt\n");
145 :
146 1 : buffer->clear();
147 :
148 1 : f = std::make_shared<snaplogger::format>("${hostname} ${message:padding=\"t\":align=left:exact_width=25}");
149 1 : buffer->set_format(f);
150 :
151 1 : SNAP_LOG_ERROR << "First we get this message cut to the specified width." << SNAP_LOG_SEND;
152 1 : CATCH_REQUIRE(buffer->str() == std::string(host) + " First we get this message\n");
153 :
154 1 : buffer->clear();
155 :
156 1 : f = std::make_shared<snaplogger::format>("${hostname} ${message:padding=\"t\":align=center:exact_width=25}");
157 1 : buffer->set_format(f);
158 :
159 1 : SNAP_LOG_ERROR << "First we get this message cut to the specified width." << SNAP_LOG_SEND;
160 1 : CATCH_REQUIRE(buffer->str() == std::string(host) + " his message cut to the sp\n");
161 :
162 1 : buffer->clear();
163 :
164 1 : f = std::make_shared<snaplogger::format>("${hostname} ${message:padding=\"t\":align=right:exact_width=25}");
165 1 : buffer->set_format(f);
166 :
167 1 : SNAP_LOG_ERROR << "First we get this message cut to the specified width." << SNAP_LOG_SEND;
168 1 : CATCH_REQUIRE(buffer->str() == std::string(host) + " t to the specified width.\n");
169 :
170 1 : buffer->clear();
171 :
172 1 : f = std::make_shared<snaplogger::format>("${hostname} ${message:padding=\"x\":align=left:exact_width=25}");
173 1 : buffer->set_format(f);
174 :
175 1 : SNAP_LOG_ERROR << "Small Message" << SNAP_LOG_SEND;
176 1 : CATCH_REQUIRE(buffer->str() == std::string(host) + " Small Messagexxxxxxxxxxxx\n");
177 :
178 1 : buffer->clear();
179 :
180 1 : f = std::make_shared<snaplogger::format>("${hostname} ${message:padding=\"x\":align=center:exact_width=25}");
181 1 : buffer->set_format(f);
182 :
183 1 : SNAP_LOG_ERROR << "Small Message" << SNAP_LOG_SEND;
184 1 : CATCH_REQUIRE(buffer->str() == std::string(host) + " xxxxxxSmall Messagexxxxxx\n");
185 :
186 1 : buffer->clear();
187 :
188 1 : f = std::make_shared<snaplogger::format>("${hostname} ${message:padding=\"x\":align=center:exact_width=25}");
189 1 : buffer->set_format(f);
190 :
191 1 : SNAP_LOG_ERROR << "Small Message (even)" << SNAP_LOG_SEND;
192 1 : CATCH_REQUIRE(buffer->str() == std::string(host) + " xxSmall Message (even)xxx\n");
193 :
194 1 : buffer->clear();
195 :
196 1 : f = std::make_shared<snaplogger::format>("${hostname} ${message:padding=\"x\":align=right:exact_width=25}");
197 1 : buffer->set_format(f);
198 :
199 1 : SNAP_LOG_ERROR << "Small Message" << SNAP_LOG_SEND;
200 1 : CATCH_REQUIRE(buffer->str() == std::string(host) + " xxxxxxxxxxxxSmall Message\n");
201 :
202 1 : buffer->clear();
203 :
204 1 : f = std::make_shared<snaplogger::format>("${hostname} ${message:prepend=\"(P) \":padding=\"z\":align=right:exact_width=25}");
205 1 : buffer->set_format(f);
206 :
207 1 : SNAP_LOG_ERROR << "Small Message" << SNAP_LOG_SEND;
208 1 : CATCH_REQUIRE(buffer->str() == std::string(host) + " zzzzzzzz(P) Small Message\n");
209 :
210 1 : buffer->clear();
211 :
212 1 : f = std::make_shared<snaplogger::format>("${hostname} ${message:append=\" (A)\":padding=\"z\":align=right:exact_width=25}");
213 1 : buffer->set_format(f);
214 :
215 1 : SNAP_LOG_ERROR << "Small Message" << SNAP_LOG_SEND;
216 1 : CATCH_REQUIRE(buffer->str() == std::string(host) + " zzzzzzzzSmall Message (A)\n");
217 :
218 1 : buffer->clear();
219 :
220 1 : f = std::make_shared<snaplogger::format>("${hostname:padding=\"q\":align=101:min_width=30} ${message}");
221 1 : buffer->set_format(f);
222 :
223 : snaplogger::message::pointer_t msg(std::make_shared<snaplogger::message>
224 2 : (::snaplogger::severity_t::SEVERITY_ERROR, __FILE__, __func__, __LINE__));
225 1 : *msg << "The align=101 parameter is the wrong type";
226 1 : CATCH_REQUIRE_THROWS_MATCHES(
227 : l->log_message(*msg)
228 : , snaplogger::invalid_parameter
229 : , Catch::Matchers::ExceptionMessage(
230 : "logger_error: the ${...:align=<value>} parameter must be a valid string (not an integer)."));
231 :
232 : // this is important here because we want to make sure that the
233 : // `message` destructor works as expected (i.e. it does not call
234 : // std::terminate() because of the throw as the align=101 is
235 : // invalid)
236 : //
237 1 : msg.reset();
238 :
239 1 : buffer->clear();
240 :
241 1 : f = std::make_shared<snaplogger::format>("${hostname:padding=\"t\":align=justify:min_width=30} ${message}");
242 1 : buffer->set_format(f);
243 :
244 1 : msg = std::make_shared<snaplogger::message>
245 2 : (::snaplogger::severity_t::SEVERITY_ERROR, __FILE__, __func__, __LINE__);
246 1 : *msg << "Try align=\"justify\" which has to fail.";
247 1 : CATCH_REQUIRE_THROWS_MATCHES(
248 : l->log_message(*msg)
249 : , snaplogger::invalid_parameter
250 : , Catch::Matchers::ExceptionMessage(
251 : "logger_error: the ${...:align=left|center|right} was expected, got \"justify\"."));
252 :
253 1 : buffer->clear();
254 :
255 1 : f = std::make_shared<snaplogger::format>("${hostname:padding=\"q\":align=left:min_width=wide} ${message}");
256 1 : buffer->set_format(f);
257 :
258 1 : msg = std::make_shared<snaplogger::message>
259 2 : (::snaplogger::severity_t::SEVERITY_ERROR, __FILE__, __func__, __LINE__);
260 1 : *msg << "The min_width=wide parameter is the wrong type";
261 1 : CATCH_REQUIRE_THROWS_MATCHES(
262 : l->log_message(*msg)
263 : , snaplogger::invalid_parameter
264 : , Catch::Matchers::ExceptionMessage(
265 : "logger_error: the ${...:min_width=<value>} parameter must be a valid integer."));
266 :
267 : // this is important here because we want to make sure that the
268 : // `message` destructor works as expected (i.e. it does not call
269 : // std::terminate() because of the throw as the align=101 is
270 : // invalid)
271 : //
272 1 : msg.reset();
273 :
274 1 : buffer->clear();
275 :
276 1 : f = std::make_shared<snaplogger::format>("${hostname:padding=99:align=left:min_width=wide} ${message}");
277 1 : buffer->set_format(f);
278 :
279 1 : msg = std::make_shared<snaplogger::message>
280 2 : (::snaplogger::severity_t::SEVERITY_ERROR, __FILE__, __func__, __LINE__);
281 1 : *msg << "The padding=... accepts a number between 0 and 9 inclusive";
282 1 : CATCH_REQUIRE_THROWS_MATCHES(
283 : l->log_message(*msg)
284 : , snaplogger::invalid_parameter
285 : , Catch::Matchers::ExceptionMessage(
286 : "logger_error: the ${...:padding=<value>} when set to a number must be one digit ('0' to '9'), not \"99\"."));
287 :
288 : // this is important here because we want to make sure that the
289 : // `message` destructor works as expected (i.e. it does not call
290 : // std::terminate() because of the throw as the align=101 is
291 : // invalid)
292 : //
293 1 : msg.reset();
294 :
295 1 : buffer->clear();
296 :
297 1 : f = std::make_shared<snaplogger::format>("${hostname:padding='abc':align=left:min_width=wide} ${message}");
298 1 : buffer->set_format(f);
299 :
300 1 : msg = std::make_shared<snaplogger::message>
301 2 : (::snaplogger::severity_t::SEVERITY_ERROR, __FILE__, __func__, __LINE__);
302 1 : *msg << "The padding=... accepts one character";
303 1 : CATCH_REQUIRE_THROWS_MATCHES(
304 : l->log_message(*msg)
305 : , snaplogger::invalid_parameter
306 : , Catch::Matchers::ExceptionMessage(
307 : "logger_error: the ${...:padding=' '} must be exactly one character, not \"abc\"."));
308 :
309 : // this is important here because we want to make sure that the
310 : // `message` destructor works as expected (i.e. it does not call
311 : // std::terminate() because of the throw as the align=101 is
312 : // invalid)
313 : //
314 1 : msg.reset();
315 :
316 1 : l->reset();
317 : }
318 : CATCH_END_SECTION()
319 :
320 10 : CATCH_START_SECTION("escape")
321 : {
322 1 : snaplogger::set_diagnostic(snaplogger::DIAG_KEY_PROGNAME, "escape");
323 :
324 2 : snaplogger::logger::pointer_t l(snaplogger::logger::get_instance());
325 2 : snaplogger::buffer_appender::pointer_t buffer(std::make_shared<snaplogger::buffer_appender>("test-buffer"));
326 :
327 1 : advgetopt::options_environment opt_env;
328 1 : opt_env.f_project_name = "test-logger";
329 2 : advgetopt::getopt opts(opt_env);
330 1 : buffer->set_config(opts);
331 :
332 2 : snaplogger::format::pointer_t f(std::make_shared<snaplogger::format>("${hostname} ${message:escape}"));
333 1 : buffer->set_format(f);
334 :
335 1 : l->add_appender(buffer);
336 :
337 : char host[HOST_NAME_MAX + 2 + 30];
338 1 : CATCH_REQUIRE(gethostname(host, HOST_NAME_MAX + 1) == 0);
339 1 : host[HOST_NAME_MAX + 1] = '\0';
340 :
341 1 : SNAP_LOG_ERROR << "Default escape for newline (\n), carriage return (\r), and tab (\t)" << SNAP_LOG_SEND;
342 1 : CATCH_REQUIRE(buffer->str() == std::string(host) + " Default escape for newline (\\n), carriage return (\\r), and tab (\\t)\n");
343 :
344 1 : buffer->clear();
345 :
346 1 : f = std::make_shared<snaplogger::format>("${hostname} ${message:escape=\"[]\"}");
347 1 : buffer->set_format(f);
348 :
349 1 : SNAP_LOG_ERROR << "Try again [with a string]" << SNAP_LOG_SEND;
350 1 : CATCH_REQUIRE(buffer->str() == std::string(host) + " Try again \\[with a string\\]\n");
351 :
352 1 : buffer->clear();
353 :
354 1 : f = std::make_shared<snaplogger::format>("${hostname} ${message:escape=\"\a\b\f\n\r\t\v\x1f\xC2\x88\xC2\x97\"}");
355 1 : buffer->set_format(f);
356 :
357 1 : SNAP_LOG_ERROR << "Escape all \a\b\f\n\r\t\v\x1f\xC2\x88\xC2\x97 types" << SNAP_LOG_SEND;
358 1 : CATCH_REQUIRE(buffer->str() == std::string(host) + " Escape all \\a\\b\\f\\n\\r\\t\\v^_@H@W types\n");
359 : }
360 : CATCH_END_SECTION()
361 :
362 10 : CATCH_START_SECTION("caps")
363 : {
364 1 : snaplogger::set_diagnostic(snaplogger::DIAG_KEY_PROGNAME, "caps");
365 :
366 2 : snaplogger::logger::pointer_t l(snaplogger::logger::get_instance());
367 2 : snaplogger::buffer_appender::pointer_t buffer(std::make_shared<snaplogger::buffer_appender>("test-buffer"));
368 :
369 1 : advgetopt::options_environment opt_env;
370 1 : opt_env.f_project_name = "test-logger";
371 2 : advgetopt::getopt opts(opt_env);
372 1 : buffer->set_config(opts);
373 :
374 2 : snaplogger::format::pointer_t f(std::make_shared<snaplogger::format>("${message:caps}"));
375 1 : buffer->set_format(f);
376 :
377 1 : l->add_appender(buffer);
378 :
379 1 : SNAP_LOG_ERROR << "this message words will get their FIRST-LETTER capitalized." << SNAP_LOG_SEND;
380 1 : CATCH_REQUIRE(buffer->str() == "This Message Words Will Get Their First-Letter Capitalized.\n");
381 : }
382 : CATCH_END_SECTION()
383 :
384 10 : CATCH_START_SECTION("lower/upper")
385 : {
386 1 : snaplogger::set_diagnostic(snaplogger::DIAG_KEY_PROGNAME, "case");
387 :
388 2 : snaplogger::logger::pointer_t l(snaplogger::logger::get_instance());
389 2 : snaplogger::buffer_appender::pointer_t buffer(std::make_shared<snaplogger::buffer_appender>("test-buffer"));
390 :
391 1 : advgetopt::options_environment opt_env;
392 1 : opt_env.f_project_name = "test-logger";
393 2 : advgetopt::getopt opts(opt_env);
394 1 : buffer->set_config(opts);
395 :
396 2 : snaplogger::format::pointer_t f(std::make_shared<snaplogger::format>("${message:lower}"));
397 1 : buffer->set_format(f);
398 :
399 1 : l->add_appender(buffer);
400 :
401 1 : SNAP_LOG_ERROR << "This message words will get their FIRST-LETTER capitalized." << SNAP_LOG_SEND;
402 1 : CATCH_REQUIRE(buffer->str() == "this message words will get their first-letter capitalized.\n");
403 :
404 1 : buffer->clear();
405 :
406 1 : f = std::make_shared<snaplogger::format>("${message:upper}");
407 1 : buffer->set_format(f);
408 :
409 1 : SNAP_LOG_ERROR << "This message words will get their FIRST-LETTER capitalized." << SNAP_LOG_SEND;
410 1 : CATCH_REQUIRE(buffer->str() == "THIS MESSAGE WORDS WILL GET THEIR FIRST-LETTER CAPITALIZED.\n");
411 : }
412 : CATCH_END_SECTION()
413 :
414 10 : CATCH_START_SECTION("default align value")
415 : {
416 1 : snaplogger::set_diagnostic(snaplogger::DIAG_KEY_PROGNAME, "message-variable-default-param");
417 :
418 2 : snaplogger::logger::pointer_t l(snaplogger::logger::get_instance());
419 2 : snaplogger::buffer_appender::pointer_t buffer(std::make_shared<snaplogger::buffer_appender>("test-buffer"));
420 :
421 1 : advgetopt::options_environment opt_env;
422 1 : opt_env.f_project_name = "test-logger";
423 2 : advgetopt::getopt opts(opt_env);
424 1 : buffer->set_config(opts);
425 :
426 2 : snaplogger::format::pointer_t f(std::make_shared<snaplogger::format>("${hostname:max_width=3} ${message}"));
427 1 : buffer->set_format(f);
428 :
429 1 : l->add_appender(buffer);
430 :
431 : char host[HOST_NAME_MAX + 2 + 30];
432 1 : CATCH_REQUIRE(gethostname(host, HOST_NAME_MAX + 1) == 0);
433 1 : host[HOST_NAME_MAX + 1] = '\0';
434 2 : std::string aligned(host);
435 1 : aligned = aligned.substr(0, 3);
436 1 : while(aligned.length() < 3)
437 : {
438 0 : aligned = " " + aligned;
439 : }
440 :
441 1 : SNAP_LOG_ERROR << "<- first three letters of hostname" << SNAP_LOG_SEND;
442 1 : CATCH_REQUIRE(buffer->str() == aligned + " <- first three letters of hostname\n");
443 : }
444 : CATCH_END_SECTION()
445 5 : }
446 :
447 :
448 :
449 5 : CATCH_TEST_CASE("duplicate_factory", "[variable][factory]")
450 : {
451 6 : CATCH_START_SECTION("attempt dynamically creating a factory which already exists")
452 : {
453 1 : class fake_variable_factory final
454 : : public snaplogger::variable_factory
455 : {
456 : public:
457 1 : fake_variable_factory()
458 1 : : variable_factory("version")
459 : {
460 1 : }
461 :
462 0 : virtual snaplogger::variable::pointer_t create_variable() override final
463 : {
464 : // we can't even register this one so returning an empty
465 : // pointer is perfectly safe here
466 : //
467 0 : return snaplogger::variable::pointer_t();
468 : }
469 : };
470 :
471 1 : CATCH_REQUIRE_THROWS_MATCHES(
472 : snaplogger::register_variable_factory(std::make_shared<fake_variable_factory>())
473 : , snaplogger::duplicate_error
474 : , Catch::Matchers::ExceptionMessage("logger_error: trying to add two variable factories of type \"version\"."));
475 : }
476 : CATCH_END_SECTION()
477 :
478 6 : CATCH_START_SECTION("attempt creating a variable with a non-existant type")
479 : {
480 1 : CATCH_REQUIRE_THROWS_MATCHES(
481 : snaplogger::get_variable("fake")
482 : , snaplogger::invalid_variable
483 : , Catch::Matchers::ExceptionMessage("logger_error: You can't create variable with type \"fake\", no such variable type was registered."));
484 : }
485 : CATCH_END_SECTION()
486 :
487 6 : CATCH_START_SECTION("attempt creating a function factory with an existing name")
488 : {
489 1 : class fake_function final
490 : : public snaplogger::function
491 : {
492 : public:
493 1 : fake_function()
494 1 : : function("padding")
495 : {
496 1 : }
497 :
498 0 : virtual void apply(
499 : ::snaplogger::message const & msg
500 : , ::snaplogger::function_data & d
501 : , ::snaplogger::param::pointer_t const & p) override
502 : {
503 0 : snap::NOTUSED(msg);
504 0 : snap::NOTUSED(d);
505 0 : snap::NOTUSED(p);
506 0 : }
507 : };
508 :
509 1 : CATCH_REQUIRE_THROWS_MATCHES(
510 : snaplogger::register_function(std::make_shared<fake_function>())
511 : , snaplogger::duplicate_error
512 : , Catch::Matchers::ExceptionMessage("logger_error: trying to add two functions named \"padding\"."));
513 : }
514 : CATCH_END_SECTION()
515 9 : }
516 :
517 :
518 : // vim: ts=4 sw=4 et
|