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 0 : bool process_logger_options(advgetopt::getopt & opts
187 : , std::string const & config_path
188 : , std::basic_ostream<char> & out)
189 : {
190 0 : set_diagnostic(DIAG_KEY_PROGNAME, opts.get_program_name());
191 :
192 : // COMMANDS
193 : //
194 0 : if(opts.is_defined("logger-version"))
195 : {
196 0 : std::cout << snaplogger::get_version_string() << std::endl;
197 0 : throw advgetopt::getopt_exception_exit("logger command processed.", 0);
198 : }
199 :
200 : // LOG CONFIG
201 : //
202 0 : int log_config(0);
203 0 : if(opts.is_defined("no-log"))
204 : {
205 0 : log_config |= OPTION_NO_LOG;
206 : }
207 0 : if(opts.is_defined("log-file"))
208 : {
209 0 : log_config |= OPTION_LOG_FILE;
210 : }
211 0 : if(opts.is_defined("log-config"))
212 : {
213 0 : log_config |= OPTION_LOG_CONFIG;
214 : }
215 0 : if(opts.is_defined("syslog"))
216 : {
217 0 : log_config |= OPTION_SYSLOG;
218 : }
219 0 : if(opts.is_defined("console"))
220 : {
221 0 : log_config |= OPTION_CONSOLE;
222 : }
223 :
224 0 : bool const show_logger_configuration_files(opts.is_defined("logger-configuration-filenames"));
225 0 : switch(log_config)
226 : {
227 : case 0:
228 : // defaults apply as normal
229 :
230 : {
231 0 : advgetopt::options_environment opt_env;
232 :
233 0 : std::string user_config("~/.config/");
234 0 : user_config += opts.get_project_name();
235 : char const * config_dirs[] =
236 : {
237 : "/usr/share/snaplogger/etc"
238 0 : , config_path.c_str()
239 0 : , user_config.c_str()
240 : , nullptr
241 0 : };
242 :
243 0 : if(opts.is_defined("log-config-path"))
244 : {
245 0 : config_dirs[0] = opts.get_string("log-config-path").c_str();
246 : }
247 :
248 0 : std::string const keep_project_name(opts.get_project_name());
249 0 : opt_env.f_project_name = keep_project_name.c_str();
250 0 : opt_env.f_environment_variable_name = "SNAPLOGGER";
251 : //opt_env.f_configuration_files = nullptr;
252 0 : opt_env.f_configuration_filename = "snaplogger.conf";
253 0 : opt_env.f_configuration_directories = config_dirs;
254 0 : opt_env.f_environment_flags = advgetopt::GETOPT_ENVIRONMENT_FLAG_DYNAMIC_PARAMETERS;
255 :
256 0 : advgetopt::getopt system_opts(opt_env);
257 :
258 0 : if(show_logger_configuration_files)
259 : {
260 0 : advgetopt::string_list_t list(system_opts.get_configuration_filenames(false, false));
261 0 : out << "Logger common configuration filenames:" << std::endl;
262 0 : for(auto n : list)
263 : {
264 0 : out << " . " << n << "\n";
265 : }
266 : }
267 :
268 : // load the system configuration file first
269 : //
270 0 : system_opts.parse_configuration_files();
271 0 : if(opts.get_program_fullname().empty())
272 : {
273 : // process environment variable now if no user filename
274 : // is going to be loaded
275 : //
276 0 : system_opts.parse_environment_variable();
277 : }
278 0 : logger::get_instance()->set_config(system_opts);
279 :
280 0 : if(!opts.get_program_fullname().empty())
281 : {
282 : // if we have a valid program name (non-empty) then try
283 : // to load these configuration files
284 : //
285 0 : std::string filename(opts.get_program_name());
286 0 : filename += ".conf";
287 0 : opt_env.f_configuration_filename = filename.c_str();
288 0 : advgetopt::getopt config_opts(opt_env);
289 :
290 0 : if(show_logger_configuration_files)
291 : {
292 0 : advgetopt::string_list_t list(config_opts.get_configuration_filenames(false, false));
293 0 : out << "Logger application configuration filenames:" << std::endl;
294 0 : for(auto n : list)
295 : {
296 0 : out << " . " << n << "\n";
297 : }
298 : }
299 :
300 0 : config_opts.parse_configuration_files();
301 0 : config_opts.parse_environment_variable();
302 0 : logger::get_instance()->set_config(config_opts);
303 : }
304 : }
305 0 : break;
306 :
307 : case OPTION_NO_LOG:
308 : // do nothing
309 0 : break;
310 :
311 : case OPTION_LOG_FILE:
312 0 : configure_file(opts.get_string("log-file"));
313 0 : break;
314 :
315 : case OPTION_LOG_CONFIG:
316 0 : configure_config(opts.get_string("log-config"));
317 0 : break;
318 :
319 : case OPTION_SYSLOG:
320 0 : configure_syslog(opts.get_string("syslog"));
321 0 : break;
322 :
323 : case OPTION_CONSOLE:
324 0 : configure_console();
325 0 : break;
326 :
327 : default:
328 0 : advgetopt::log << advgetopt::log_level_t::error
329 0 : << "only one of --no-log, --log-file, --log-config, --syslog, --console can be used on your command line."
330 0 : << advgetopt::end;
331 0 : return false;
332 :
333 : }
334 :
335 0 : if(show_logger_configuration_files)
336 : {
337 0 : if(log_config != 0)
338 : {
339 0 : if(log_config == OPTION_LOG_CONFIG)
340 : {
341 0 : out << "Logger application configuration filename:" << std::endl
342 0 : << " . " << opts.get_string("log-config") << std::endl;
343 : }
344 : else
345 : {
346 0 : out << "No logger application configuration filenames available with the current command line options." << std::endl;
347 : }
348 : }
349 0 : throw advgetopt::getopt_exception_exit("logger command processed.", 0);
350 : }
351 :
352 : // SEVERITY
353 : //
354 0 : if(opts.is_defined("debug")
355 0 : || opts.is_defined("log-severity")
356 0 : || opts.is_defined("force-severity"))
357 : {
358 0 : advgetopt::log << advgetopt::log_level_t::error
359 0 : << "only one of --debug, --log-severity, --force-severity can be used on your command line."
360 0 : << advgetopt::end;
361 0 : return false;
362 : }
363 :
364 0 : if(opts.is_defined("debug"))
365 : {
366 0 : logger::get_instance()->reduce_severity(severity_t::SEVERITY_DEBUG);
367 :
368 0 : configure_console(true);
369 : }
370 0 : else if(opts.is_defined("log-severity"))
371 : {
372 0 : std::string const severity_name(opts.get_string("log-severity"));
373 0 : severity::pointer_t sev(get_severity(severity_name));
374 0 : if(sev == nullptr)
375 : {
376 0 : advgetopt::log << advgetopt::log_level_t::error
377 0 : << "unknown severity level \""
378 0 : << severity_name
379 0 : << "\"; please check your spelling."
380 0 : << advgetopt::end;
381 0 : return false;
382 : }
383 0 : logger::get_instance()->reduce_severity(sev->get_severity());
384 : }
385 0 : else if(opts.is_defined("force-severity"))
386 : {
387 0 : std::string const severity_name(opts.get_string("force-severity"));
388 0 : severity::pointer_t sev(get_severity(severity_name));
389 0 : if(sev == nullptr)
390 : {
391 0 : advgetopt::log << advgetopt::log_level_t::error
392 0 : << "unknown severity level \""
393 0 : << severity_name
394 0 : << "\"; please check your spelling."
395 0 : << advgetopt::end;
396 0 : return false;
397 : }
398 0 : logger::get_instance()->set_severity(sev->get_severity());
399 : }
400 :
401 : // FILTERS
402 : //
403 0 : if(opts.is_defined("log-component"))
404 : {
405 0 : size_t const max(opts.size("log-component"));
406 0 : for(size_t idx(0); idx < max; ++idx)
407 : {
408 0 : std::string log_component(opts.get_string("log-component", idx));
409 0 : if(!log_component.empty())
410 : {
411 0 : if(log_component[0] == '!')
412 : {
413 0 : log_component = log_component.substr(1);
414 0 : if(!log_component.empty())
415 : {
416 0 : component::pointer_t comp(get_component(log_component));
417 0 : logger::get_instance()->add_component_to_ignore(comp);
418 : }
419 : }
420 : else
421 : {
422 0 : component::pointer_t comp(get_component(log_component));
423 0 : logger::get_instance()->add_component_to_include(comp);
424 : }
425 : }
426 : }
427 : }
428 :
429 0 : return true;
430 : }
431 :
432 :
433 6 : } // snaplogger namespace
434 : // vim: ts=4 sw=4 et
|