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