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