Line data Source code
1 : /*
2 : * Copyright (c) 2013-2021 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 Handle logger specific command line and other options.
24 : *
25 : * The logger supports a few options to override configuration files
26 : * and tweak settings from the command line. Since the user is in
27 : * control of the environment variable, we do not offer that option
28 : * here.
29 : */
30 :
31 :
32 : // self
33 : //
34 : #include "snaplogger/options.h"
35 :
36 : #include "snaplogger/logger.h"
37 : #include "snaplogger/map_diagnostic.h"
38 : #include "snaplogger/version.h"
39 :
40 :
41 : // boost lib
42 : //
43 : #include <boost/algorithm/string/replace.hpp>
44 :
45 :
46 : // advgetopt lib
47 : //
48 : #include <advgetopt/exception.h>
49 :
50 :
51 : // cppthread lib
52 : //
53 : #include <cppthread/log.h>
54 :
55 :
56 : // last include
57 : //
58 : #include <snapdev/poison.h>
59 :
60 :
61 :
62 : namespace snaplogger
63 : {
64 :
65 :
66 : namespace
67 : {
68 :
69 :
70 :
71 : advgetopt::option const g_options[] =
72 : {
73 : // DIRECT SELECT
74 : //
75 : advgetopt::define_option(
76 : advgetopt::Name("no-log")
77 : , advgetopt::Flags(advgetopt::standalone_command_flags<advgetopt::GETOPT_FLAG_GROUP_OPTIONS>())
78 : , advgetopt::Help("do not log anything.")
79 : ),
80 : advgetopt::define_option(
81 : advgetopt::Name("log-file")
82 : , advgetopt::Flags(advgetopt::command_flags<advgetopt::GETOPT_FLAG_GROUP_OPTIONS
83 : , advgetopt::GETOPT_FLAG_REQUIRED>())
84 : , advgetopt::Help("log data to this specific log files")
85 : ),
86 : advgetopt::define_option(
87 : advgetopt::Name("log-config")
88 : , advgetopt::Flags(advgetopt::command_flags<advgetopt::GETOPT_FLAG_GROUP_OPTIONS
89 : , advgetopt::GETOPT_FLAG_REQUIRED>())
90 : , advgetopt::Help("only load this specific configuration file.")
91 : ),
92 : advgetopt::define_option(
93 : advgetopt::Name("syslog")
94 : , advgetopt::Flags(advgetopt::command_flags<advgetopt::GETOPT_FLAG_GROUP_OPTIONS>())
95 : , advgetopt::Help("send the logs to syslog only, the argument, if specified, is the name to use as the identity.")
96 : ),
97 : advgetopt::define_option(
98 : advgetopt::Name("console")
99 : , advgetopt::Flags(advgetopt::standalone_command_flags<advgetopt::GETOPT_FLAG_GROUP_OPTIONS>())
100 : , advgetopt::Help("print the logs out to the console.")
101 : ),
102 :
103 : // ALTERNATIVE CONFIG FILES
104 : //
105 : advgetopt::define_option(
106 : advgetopt::Name("log-config-path")
107 : , advgetopt::Flags(advgetopt::all_flags<advgetopt::GETOPT_FLAG_GROUP_OPTIONS
108 : , advgetopt::GETOPT_FLAG_REQUIRED>())
109 : , advgetopt::Help("the path to the configuration folders.")
110 : ),
111 :
112 : // SEVERITY
113 : //
114 : advgetopt::define_option(
115 : advgetopt::Name("debug")
116 : , advgetopt::Flags(advgetopt::standalone_command_flags<advgetopt::GETOPT_FLAG_GROUP_OPTIONS>())
117 : , advgetopt::Help("change the severity level of each appender to DEBUG.")
118 : ),
119 : advgetopt::define_option(
120 : advgetopt::Name("trace")
121 : , advgetopt::Flags(advgetopt::standalone_command_flags<advgetopt::GETOPT_FLAG_GROUP_OPTIONS>())
122 : , advgetopt::Help("change the severity level of each appender to TRACE.")
123 : ),
124 : advgetopt::define_option(
125 : advgetopt::Name("log-severity")
126 : , advgetopt::Flags(advgetopt::command_flags<advgetopt::GETOPT_FLAG_GROUP_OPTIONS
127 : , advgetopt::GETOPT_FLAG_REQUIRED>())
128 : , advgetopt::Help("reduce the severity level of each appender to the specified level unless it is already lower.")
129 : ),
130 : advgetopt::define_option(
131 : advgetopt::Name("force-severity")
132 : , advgetopt::Flags(advgetopt::command_flags<advgetopt::GETOPT_FLAG_GROUP_OPTIONS
133 : , advgetopt::GETOPT_FLAG_REQUIRED>())
134 : , advgetopt::Help("change the severity level of each appender to the specified level.")
135 : ),
136 :
137 : // FILTERS
138 : //
139 : advgetopt::define_option(
140 : advgetopt::Name("log-component")
141 : , advgetopt::Flags(advgetopt::command_flags<
142 : advgetopt::GETOPT_FLAG_GROUP_OPTIONS
143 : , advgetopt::GETOPT_FLAG_MULTIPLE
144 : , advgetopt::GETOPT_FLAG_REQUIRED>())
145 : , advgetopt::Help("filter logs by component, use ! in front of a name to prevent those logs.")
146 : ),
147 :
148 : // COMMANDS
149 : //
150 : advgetopt::define_option(
151 : advgetopt::Name("logger-version")
152 : , advgetopt::Flags(advgetopt::standalone_command_flags<advgetopt::GETOPT_FLAG_GROUP_COMMANDS>())
153 : , advgetopt::Help("show the version of the logger library.")
154 : ),
155 : advgetopt::define_option(
156 : advgetopt::Name("logger-configuration-filenames")
157 : , advgetopt::Flags(advgetopt::standalone_command_flags<advgetopt::GETOPT_FLAG_GROUP_COMMANDS>())
158 : , advgetopt::Help("show the list of configuration filenames that would be loaded with the current options.")
159 : ),
160 :
161 : // END
162 : //
163 : advgetopt::end_options()
164 : };
165 :
166 :
167 :
168 :
169 : }
170 : // no name namespace
171 :
172 :
173 0 : void add_logger_options(advgetopt::getopt & opts)
174 : {
175 0 : auto env(opts.get_options_environment());
176 0 : if(env.f_version != nullptr)
177 : {
178 0 : set_diagnostic(DIAG_KEY_VERSION, env.f_version);
179 : }
180 0 : if(env.f_build_date != nullptr)
181 : {
182 0 : set_diagnostic(DIAG_KEY_BUILD_DATE, env.f_build_date);
183 : }
184 0 : if(env.f_build_time != nullptr)
185 : {
186 0 : set_diagnostic(DIAG_KEY_BUILD_TIME, env.f_build_time);
187 : }
188 0 : set_diagnostic(DIAG_KEY_PROJECT_NAME, opts.get_project_name());
189 :
190 0 : opts.parse_options_info(g_options, true);
191 0 : }
192 :
193 :
194 : constexpr int const OPTION_NO_LOG = 0x001;
195 : constexpr int const OPTION_LOG_FILE = 0x002;
196 : constexpr int const OPTION_LOG_CONFIG = 0x004;
197 : constexpr int const OPTION_SYSLOG = 0x008;
198 : constexpr int const OPTION_CONSOLE = 0x010;
199 :
200 : constexpr int const OPTION_TRACE_SEVERITY = 0x020;
201 : constexpr int const OPTION_DEBUG_SEVERITY = 0x040;
202 : constexpr int const OPTION_LOG_SEVERITY = 0x080;
203 : constexpr int const OPTION_FORCE_SEVERITY = 0x100;
204 :
205 :
206 0 : bool process_logger_options(advgetopt::getopt & opts
207 : , std::string const & config_path
208 : , std::basic_ostream<char> & out)
209 : {
210 0 : set_diagnostic(DIAG_KEY_PROGNAME, opts.get_program_name());
211 :
212 : // COMMANDS
213 : //
214 0 : if(opts.is_defined("logger-version"))
215 : {
216 0 : std::cout << snaplogger::get_version_string() << std::endl;
217 0 : throw advgetopt::getopt_exit("logger command processed.", 0);
218 : }
219 :
220 : // LOG CONFIG
221 : //
222 0 : int log_config(0);
223 0 : if(opts.is_defined("no-log"))
224 : {
225 0 : log_config |= OPTION_NO_LOG;
226 : }
227 0 : if(opts.is_defined("log-file"))
228 : {
229 0 : log_config |= OPTION_LOG_FILE;
230 : }
231 0 : if(opts.is_defined("log-config"))
232 : {
233 0 : log_config |= OPTION_LOG_CONFIG;
234 : }
235 0 : if(opts.is_defined("syslog"))
236 : {
237 0 : log_config |= OPTION_SYSLOG;
238 : }
239 0 : if(opts.is_defined("console"))
240 : {
241 0 : log_config |= OPTION_CONSOLE;
242 : }
243 :
244 0 : bool const show_logger_configuration_files(opts.is_defined("logger-configuration-filenames"));
245 0 : switch(log_config)
246 : {
247 0 : case 0:
248 : // defaults apply as normal
249 :
250 : {
251 0 : advgetopt::options_environment opt_env;
252 :
253 0 : std::string user_config("~/.config/");
254 0 : user_config += opts.get_project_name();
255 0 : user_config += "/logger";
256 0 : char const * config_dirs[] =
257 : {
258 : "/usr/share/snaplogger/etc"
259 0 : , config_path.c_str()
260 0 : , user_config.c_str()
261 : , nullptr
262 0 : };
263 :
264 0 : if(opts.is_defined("log-config-path"))
265 : {
266 0 : config_dirs[0] = opts.get_string("log-config-path").c_str();
267 : }
268 :
269 0 : std::string const keep_project_name(opts.get_project_name());
270 0 : opt_env.f_project_name = keep_project_name.c_str();
271 0 : std::string const keep_group_name(opts.get_group_name());
272 0 : opt_env.f_group_name = keep_group_name.c_str();
273 0 : opt_env.f_environment_variable_name = "SNAPLOGGER";
274 : //opt_env.f_configuration_files = nullptr;
275 0 : opt_env.f_configuration_filename = "snaplogger.conf";
276 0 : opt_env.f_configuration_directories = config_dirs;
277 0 : opt_env.f_environment_flags = advgetopt::GETOPT_ENVIRONMENT_FLAG_DYNAMIC_PARAMETERS;
278 :
279 0 : advgetopt::getopt system_opts(opt_env);
280 :
281 0 : if(show_logger_configuration_files)
282 : {
283 0 : advgetopt::string_list_t list(system_opts.get_configuration_filenames(false, false));
284 0 : out << "Logger common configuration filenames:" << std::endl;
285 0 : for(auto n : list)
286 : {
287 0 : out << " . " << n << "\n";
288 : }
289 : }
290 :
291 : // load the system configuration file first
292 : //
293 0 : system_opts.parse_configuration_files();
294 0 : if(opts.get_program_fullname().empty())
295 : {
296 : // process environment variable now if no user filename
297 : // is going to be loaded
298 : //
299 0 : system_opts.parse_environment_variable();
300 : }
301 0 : logger::get_instance()->set_config(system_opts);
302 :
303 0 : if(!opts.get_program_fullname().empty())
304 : {
305 : // if we have a valid program name (non-empty) then try
306 : // to load these configuration files
307 : //
308 0 : std::string filename(opts.get_program_name());
309 0 : boost::replace_all(filename, "_", "-");
310 0 : filename += ".conf";
311 0 : opt_env.f_configuration_filename = filename.c_str();
312 0 : advgetopt::getopt config_opts(opt_env);
313 :
314 0 : if(show_logger_configuration_files)
315 : {
316 0 : advgetopt::string_list_t list(config_opts.get_configuration_filenames(false, false));
317 0 : out << "Logger application configuration filenames:" << std::endl;
318 0 : for(auto n : list)
319 : {
320 0 : out << " . " << n << "\n";
321 : }
322 : }
323 :
324 0 : config_opts.parse_configuration_files();
325 0 : config_opts.parse_environment_variable();
326 0 : logger::get_instance()->set_config(config_opts);
327 0 : }
328 : }
329 0 : break;
330 :
331 0 : case OPTION_NO_LOG:
332 : // do nothing
333 0 : break;
334 :
335 0 : case OPTION_LOG_FILE:
336 0 : configure_file(opts.get_string("log-file"));
337 0 : break;
338 :
339 0 : case OPTION_LOG_CONFIG:
340 0 : configure_config(opts.get_string("log-config"));
341 0 : break;
342 :
343 0 : case OPTION_SYSLOG:
344 0 : configure_syslog(opts.get_string("syslog"));
345 0 : break;
346 :
347 0 : case OPTION_CONSOLE:
348 0 : configure_console();
349 0 : break;
350 :
351 0 : default:
352 0 : cppthread::log << cppthread::log_level_t::error
353 0 : << "only one of --no-log, --log-file, --log-config, --syslog, --console can be used on your command line."
354 0 : << cppthread::end;
355 0 : return false;
356 :
357 : }
358 :
359 0 : if(show_logger_configuration_files)
360 : {
361 0 : if(log_config != 0)
362 : {
363 0 : if(log_config == OPTION_LOG_CONFIG)
364 : {
365 0 : out << "Logger application configuration filename:" << std::endl
366 0 : << " . " << opts.get_string("log-config") << std::endl;
367 : }
368 : else
369 : {
370 0 : out << "No logger application configuration filenames available with the current command line options." << std::endl;
371 : }
372 : }
373 0 : throw advgetopt::getopt_exit("logger command processed.", 0);
374 : }
375 :
376 : // SEVERITY
377 : //
378 0 : int severity_selection(0);
379 0 : if(opts.is_defined("trace"))
380 : {
381 0 : severity_selection |= OPTION_TRACE_SEVERITY;
382 : }
383 0 : if(opts.is_defined("debug"))
384 : {
385 0 : severity_selection |= OPTION_DEBUG_SEVERITY;
386 : }
387 0 : if(opts.is_defined("log-severity"))
388 : {
389 0 : severity_selection |= OPTION_LOG_SEVERITY;
390 : }
391 0 : if(opts.is_defined("force-severity"))
392 : {
393 0 : severity_selection |= OPTION_FORCE_SEVERITY;
394 : }
395 :
396 0 : switch(severity_selection)
397 : {
398 0 : case 0:
399 : // keep as is
400 0 : break;
401 :
402 0 : case OPTION_TRACE_SEVERITY:
403 0 : logger::get_instance()->reduce_severity(severity_t::SEVERITY_TRACE);
404 0 : configure_console(true);
405 0 : break;
406 :
407 0 : case OPTION_DEBUG_SEVERITY:
408 0 : logger::get_instance()->reduce_severity(severity_t::SEVERITY_DEBUG);
409 0 : configure_console(true);
410 0 : break;
411 :
412 0 : case OPTION_LOG_SEVERITY:
413 : {
414 0 : std::string const severity_name(opts.get_string("log-severity"));
415 0 : severity::pointer_t sev(get_severity(severity_name));
416 0 : if(sev == nullptr)
417 : {
418 0 : cppthread::log << cppthread::log_level_t::error
419 0 : << "unknown severity level \""
420 0 : << severity_name
421 0 : << "\"; please check your spelling."
422 0 : << cppthread::end;
423 0 : return false;
424 : }
425 0 : logger::get_instance()->reduce_severity(sev->get_severity());
426 : }
427 0 : break;
428 :
429 0 : case OPTION_FORCE_SEVERITY:
430 : {
431 0 : std::string const severity_name(opts.get_string("force-severity"));
432 0 : severity::pointer_t sev(get_severity(severity_name));
433 0 : if(sev == nullptr)
434 : {
435 0 : cppthread::log << cppthread::log_level_t::error
436 0 : << "unknown severity level \""
437 0 : << severity_name
438 0 : << "\"; please check your spelling."
439 0 : << cppthread::end;
440 0 : return false;
441 : }
442 0 : logger::get_instance()->set_severity(sev->get_severity());
443 : }
444 0 : break;
445 :
446 0 : default:
447 0 : cppthread::log << cppthread::log_level_t::error
448 0 : << "only one of --debug, --log-severity, --force-severity can be used on your command line."
449 0 : << cppthread::end;
450 0 : return false;
451 :
452 : }
453 :
454 : // FILTERS
455 : //
456 0 : if(opts.is_defined("log-component"))
457 : {
458 0 : size_t const max(opts.size("log-component"));
459 0 : for(size_t idx(0); idx < max; ++idx)
460 : {
461 0 : std::string log_component(opts.get_string("log-component", idx));
462 0 : if(!log_component.empty())
463 : {
464 0 : if(log_component[0] == '!')
465 : {
466 0 : log_component = log_component.substr(1);
467 0 : if(!log_component.empty())
468 : {
469 0 : component::pointer_t comp(get_component(log_component));
470 0 : logger::get_instance()->add_component_to_ignore(comp);
471 : }
472 : }
473 : else
474 : {
475 0 : component::pointer_t comp(get_component(log_component));
476 0 : logger::get_instance()->add_component_to_include(comp);
477 : }
478 : }
479 : }
480 : }
481 :
482 0 : SNAP_LOG_INFO
483 0 : << section(g_normal_component)
484 0 : << section(g_self_component)
485 : << "--------------------------------------------------"
486 : << SNAP_LOG_SEND;
487 0 : SNAP_LOG_INFO
488 0 : << section(g_normal_component)
489 0 : << section(g_self_component)
490 0 : << opts.get_project_name()
491 : << " v"
492 0 : << opts.get_options_environment().f_version
493 : << " started."
494 : << SNAP_LOG_SEND;
495 :
496 0 : return true;
497 : }
498 :
499 :
500 6 : } // snaplogger namespace
501 : // vim: ts=4 sw=4 et
|