def test_remote_logging_bind_simple(self): """ Process Parent receives logs from child process """ # Configure the log in the parent so that all logs received from the child # goes to a dedicated file from which we can count the number of messages # produced by the child tmpdir = tempfile.mkdtemp(suffix="tmp", prefix=BXIRemoteLoggerTest.__name__) all = os.path.join(tmpdir, 'all.bxilog') child = os.path.join(tmpdir, 'child.bxilog') parent_config = { 'handlers': ['all', 'child'], 'all': { 'module': 'bxi.base.log.file_handler', 'filters': ':lowest', 'path': all, 'append': True, }, 'child': { 'module': 'bxi.base.log.file_handler', 'filters': ':off,%s:lowest' % LOGGER_CMD, 'path': child, 'append': True, } } bxilog.set_config(configobj.ConfigObj(parent_config)) print("Logging output: all: %s, child: %s" % (all, child)) url = 'ipc://%s/rh-cfg.zock' % tmpdir logs_nb = 25 full_cmd_path = os.path.join( os.path.dirname(os.path.abspath(__file__)), LOGGER_CMD) logger_output_file = os.path.join( tmpdir, os.path.splitext(LOGGER_CMD)[0] + '.bxilog') args = [ full_cmd_path, logger_output_file, url, 'False', '1', str(logs_nb) ] bxilog.out("Executing '%s': it must produce %d logs", ' '.join(args), logs_nb) popen = subprocess.Popen(args) bxilog.out("Starting logs reception thread on %s", url) receiver = remote_receiver.RemoteReceiver([url], bind=True) receiver.start() bxilog.out("Waiting for the child termination") popen.wait() rc = popen.returncode bxilog.out("Child exited with return code %s", rc) self.assertEquals(rc, logs_nb) # Wait a bit for the logs to be processed time.sleep(1) bxilog.out("Stopping the receiver") receiver.stop(True) bxilog.out("Flushing bxilog") bxilog.flush() with open(child) as file_: lines = file_.readlines() self.assertEquals(len(lines), logs_nb)
def test_config_file(self): """Test logging with configuration items""" orig_size = os.stat(FILENAME).st_size if os.path.exists( FILENAME) else 0 conf = { 'handlers': ['out', 'null'], 'setsighandler': True, 'out': { 'module': 'bxi.base.log.file_handler', 'filters': ':output,foo:debug,foo.bar:trace', 'path': FILENAME, 'append': True }, 'null': { 'module': 'bxi.base.log.null_handler', 'filters': ':off,foo:fine,foo.bar:debug', }, } bxilog.set_config(configobj.ConfigObj(conf)) # It is required to initialize the library for the configuration to be used bxilog.init() foo = bxilog.getLogger('foo') self.assertEqual(foo.level, bxilog.FINE) bar = bxilog.getLogger('foo.bar') self.assertEqual(bar.level, bxilog.TRACE) any = bxilog.getLogger('bebopalula') self.assertEqual(any.level, bxilog.OUTPUT) any.info('This message must not appear in file %s', FILENAME) bxilog.flush() newsize = os.stat(FILENAME).st_size self.assertEquals(newsize, orig_size) bar.lowest('This message must also not appear in file %s', FILENAME) bxilog.flush() newsize = os.stat(FILENAME).st_size self.assertEquals(newsize, orig_size) foo.fine('This message must not appear in file %s', FILENAME) bxilog.flush() newsize = os.stat(FILENAME).st_size self.assertEquals(newsize, orig_size) self._check_log_produced(FILENAME, any.output, 'This message must appear in file %s', FILENAME) self._check_log_produced(FILENAME, bar.trace, 'This message must also appear in file %s', FILENAME) self._check_log_produced(FILENAME, foo.debug, 'This message must also appear in file %s', FILENAME) self._check_log_produced(FILENAME, bxilog.output, "This message must also appear in file %s", FILENAME)
def setUp(self): unittest.TestCase.setUp(self) if not hasattr(self, 'bxilog_config'): self.bxilog_config = bxilog.get_config() bxilog.set_config(self.bxilog_config) try: self.bxiconfigdir = tempfile.mkdtemp() except KeyError: pass
def main(file_out, url, bind, sync_nb, logs_nb): config = { 'handlers': ['file', 'remote'], 'remote': { 'module': 'bxi.base.log.remote_handler', 'filters': ':all', 'url': url, 'bind': bind, }, 'file': { 'module': 'bxi.base.log.file_handler', 'filters': ':all', 'path': file_out, 'append': True, } } bxilog.set_config(config) nb = 0 nb += _do_log(0, logs_nb / 2) bxilog.cleanup() bxilog.set_config(config) nb += _do_log(logs_nb / 2, logs_nb) return nb
def _configure_log(parser): """ Configure the bxilog options for the given parser @param[inout] parser the parser to add options to """ def _add_common(target_parser): """ Add common options to the given target_parser @param[inout] target_parser the target_parser to add options to """ # Warning: do not introduce --log-STUFF unless STUFF is actually the name # of a bxilog handler. group = target_parser.add_argument_group('BXI Log options') group.add_argument("--loglevels", mustbeprinted=False, action=LogLevelsAction, help="Displays all log levels and exit.") group.add_argument( "--logoutput-default-config", dest='output_default_logcfg', action='store_true', mustbeprinted=False, help="Output the default logging configuration and exit.") group.add_argument( "--logcfgfile", mustbeprinted=False, metavar='logcfgfile', default=None, help="Logging configuration file. " " Use '--logoutput-default-config' to customize one to your " "own needs. Value: %(default)s.") return group def _add_others(target_parser, args, group, config): """ Add remaining options to the given target_parser @param[inout] target_parser the target parser to add options to @param[in] args the current set of parsed arguments @param[in] group the group in which the arguments must be set @param[in] config the configuration """ sections = find_logconfigs(bxilog_consolehandler.__name__, config) if len(sections) > 1: target_parser.error("Multiple instances of module %s is " "currently unsupported. Configuration: %s " % (bxilog_consolehandler.__name__, config)) if len(sections) == 0: console_handler = None else: console_handler = sections[0] console_section = config[console_handler] if 'filters' not in console_section: target_parser.error( "Bad logging configuration: 'filters' is missing " "in section '%s' of config %s" % (console_handler, config)) default = console_section['filters'] group.add_argument( "-l", "--log-%s-filters" % console_handler, mustbeprinted=False, metavar='log-%s-filters' % console_handler, envvar='BXILOG_%s_FILTERS' % console_handler.upper(), default=default, help="Define the logging filters for the " " %s handler " % console_handler + "Value: '%(default)s'. " "Logging filters are defined by the following " "format: logger_name_prefix:level[,prefix:level]*") group.add_argument("--quiet", action='store_true', mustbeprinted=True, help="Set log console filter to off.") if 'colors' not in console_section: default = DEFAULT_CONSOLE_COLORS else: default = console_section['colors'] group.add_argument( "--log-%s-colors" % console_handler, mustbeprinted=False, metavar='log-%s-colors' % console_handler, envvar='BXILOG_%s_COLORS' % console_handler.upper(), default=default, choices=list(bxilog_consolehandler.COLORS), help="Define the logging colors for the %s handler " % console_handler + "Value: '%(default)s'." + " choices=%s. " % list(bxilog_consolehandler.COLORS)) sections = find_logconfigs(bxilog_filehandler.__name__, config) for section in sections: conf = config[section] if 'filters' not in conf: target_parser.error( "Bad logging configuration: 'filters' is missing " "in section '%s' of config %s" % (section, config)) default = conf['filters'] if console_handler is not None: auto_help_msg = "If set to '%s', " % bxilog_filehandler.FILTERS_AUTO + \ "filters are automatically computed to " + \ "provide two levels more details than handler '%s' " % \ console_handler + \ "and at least error levels and above. " else: auto_help_msg = "" group.add_argument( "--log-%s-filters" % section, metavar='log-%s-filters' % section, mustbeprinted=False, envvar='BXILOG_%s_FILTERS' % section.upper(), default=default, help="Define the logging filters for the " "%s handler " % section + "of the default logging configuration. " + auto_help_msg + "The format is the one defined by " "console_filters option. Value: '%(default)s'. ") if 'path' not in conf: target_parser.error( "Bad logging configuration: 'path' is missing " "in section '%s' of config %s" % (section, config)) default = conf['path'] group.add_argument( "--log-%s-path" % section, metavar='PATH', envvar='BXILOGPATH', mustbeprinted=False, default=default, help="Define the destination file for the %s handler " % section + "Value: %(default)s") if 'append' not in conf: default = True else: default = conf['append'] group.add_argument( "--log-%s-append" % section, metavar='bool', mustbeprinted=False, default=default, help="When true, append to destination path of handler " "%s, instead of owerwriting. " % section + "Value: %(default)s") def _override_logconfig(config, known_args, parser): """ Override the given logging configuration with given known_args @param[inout] config the logging configuration @param[in] know_args known arguments """ def _override_kv(option, key, config, args): """ Override the config[key] value by the one given by args[option] @param[in] option the option name (long format) @param[in] key the key in the config file for the current handler @param[inout] config the configuration @param[in] known_args the currently known arguments """ # option has the following format: --log-handler-key=value if option.endswith(key): handler_name = option[len('log_'):option.index(key) - 1] assert handler_name in config # replace in config # print("Overriding: %s -> %s[%s]=%s" % # (option, handler_name, key, args[option])) config[handler_name][key] = args[option] args = vars(known_args) for option in args: _override_kv(option, 'filters', config, args) _override_kv(option, 'colors', config, args) _override_kv(option, 'path', config, args) _override_kv(option, 'append', config, args) # if --quiet option is provided, set output log level for console handlers # to minimal settings so that nothing is printed on stdout (nothing change # for stderr). if option is "quiet": if args[option] is True: sections = find_logconfigs(bxilog_consolehandler.__name__, config) if len(sections) > 1: parser.error( "Multiple instances of module %s is " "currently unsupported. Configuration: %s " % (bxilog_consolehandler.__name__, config)) if len(sections) == 1: option = "log_console_filters" key = 'filters' handler_name = option[len('log_'):option.index(key) - 1] args[option] = ":%s" % config[handler_name][ "stderr_level"] _override_kv(option, key, config, args) parser.add_argument('--help-logs', action=_LoggedHelpAction, default=posless.SUPPRESS, help=_('Show detailed logging options and exit')) group = _add_common(parser) known_args = parser.get_known_args()[0] if known_args is None: return baseconf = { 'handlers': ['console', 'file'], 'setsighandler': True, 'console': { 'module': bxilog_consolehandler.__name__, 'filters': ':output', 'stderr_level': 'WARNING', 'colors': '216_dark', }, 'file': { 'module': bxilog_filehandler.__name__, 'filters': 'auto', 'path': os.path.join(tempfile.gettempdir(), '%(prog)s') + '.bxilog', 'append': True, } } if known_args.output_default_logcfg: config = configobj.ConfigObj(infile=baseconf, interpolation=False) for line in config.write(): print(line) sys.exit(0) if known_args.logcfgfile is None: # The Environment and command line does not define a specific file # Use the one from the configuration default_logcfgfile = getdefaultvalue(parser, ['Defaults'], BXILOG_DEFAULT_CONFIGFILE_KEY, None) parser.set_defaults(logcfgfile=default_logcfgfile) infile = default_logcfgfile if infile is None: # Default case: no logging configuration given and no file found by default infile = baseconf logcfg_msg = "No logging configuration file given, using default." else: if not os.path.isabs(infile): # Find the absolute path from config-file... config_dir = os.path.dirname(parser.known_config_file) infile = os.path.join(config_dir, infile) if not os.path.exists(infile): parser.error("Logging configuration file " "not found: %s. " % infile + "Check your configuration " "from file: %s" % parser.known_config_file) logcfg_msg = "Using logging configuration file '%s' specified by '%s'" %\ (infile, parser.known_config_file) config = configobj.ConfigObj(infile=infile, interpolation=False) elif not os.path.exists(known_args.logcfgfile): parser.error("For option logcfgfile, provided file not found: %s" % known_args.logcfgfile) else: config = configobj.ConfigObj(infile=known_args.logcfgfile, interpolation=False) logcfg_msg = "Using logging configuration file '%s' specified by command line" %\ known_args.logcfgfile _add_others(parser, known_args, group, config) known_args = parser.get_known_args()[0] _override_logconfig(config, known_args, parser) logging.captureWarnings(True) if logging.is_configured(): logging.info("Reconfiguration of the logging system") logging.cleanup() logging.set_config(config) if parser.known_config_file is not None: _LOGGER.info("Configuration based on '%s': %s", parser.known_config_file, parser.config) else: _LOGGER.info("No configuration file found, using default values: %s", parser.config) _LOGGER.debug(logcfg_msg)