def initialize_logging(self): """Set up additional logging handlers. What we do, in detail, is to add a logger to file (whose filename depends on the coords) and a remote logger to a LogService. We also attach the service coords to all log messages. """ filter_ = ServiceFilter(self.name, self.shard) # Update shell handler to attach service coords. shell_handler.addFilter(filter_) # Determine location of log file, and make directories. log_dir = os.path.join(config.log_dir, "%s-%d" % (self.name, self.shard)) mkdir(config.log_dir) mkdir(log_dir) log_filename = time.strftime("%Y-%m-%d-%H-%M-%S.log") # Install a file handler. file_handler = FileHandler(os.path.join(log_dir, log_filename), mode='w', encoding='utf-8') if config.file_log_debug: file_log_level = logging.DEBUG else: file_log_level = logging.INFO file_handler.setLevel(file_log_level) file_handler.setFormatter(DetailedFormatter(False)) file_handler.addFilter(filter_) root_logger.addHandler(file_handler) # Provide a symlink to the latest log file. try: os.remove(os.path.join(log_dir, "last.log")) except OSError: pass os.symlink(log_filename, os.path.join(log_dir, "last.log")) # Setup a remote LogService handler (except when we already are # LogService, to avoid circular logging). if self.name != "LogService": log_service = self.connect_to(ServiceCoord("LogService", 0)) remote_handler = LogServiceHandler(log_service) remote_handler.setLevel(logging.INFO) remote_handler.addFilter(filter_) root_logger.addHandler(remote_handler)
class LogService(Service): """Logger service. """ LAST_MESSAGES_COUNT = 100 def __init__(self, shard): Service.__init__(self, shard) # Determine location of log file, and make directories. log_dir = os.path.join(config.log_dir, "cms") if not mkdir(config.log_dir) or \ not mkdir(log_dir): logger.error("Cannot create necessary directories.") self.exit() return log_filename = "%d.log" % int(time.time()) # Install a global file handler. self.file_handler = FileHandler(os.path.join(log_dir, log_filename), mode='w', encoding='utf-8') self.file_handler.setLevel(logging.DEBUG) self.file_handler.setFormatter(DetailedFormatter(False)) root_logger.addHandler(self.file_handler) # Provide a symlink to the latest log file. try: os.remove(os.path.join(log_dir, "last.log")) except OSError: pass os.symlink(log_filename, os.path.join(log_dir, "last.log")) self._last_messages = deque(maxlen=self.LAST_MESSAGES_COUNT) @rpc_method def Log(self, **kwargs): """Log a message. Receive the attributes of a LogRecord, rebuild and handle it. The given keyword arguments will contain: msg (string): the message to log. levelname (string): the name of the level, one of "DEBUG", "INFO", "WARNING", "ERROR" and "CRITICAL". levelno (int): a numeric value denoting the level, one of the constants defined by the logging module. In fact, `levelno == getattr(logging, levelname)` (yes, it is redundant). created (float): when the log message was emitted (as an UNIX timestamp, seconds from epoch). And they may contain: service_name (string) and service_shard (int): the coords of the service where this message was generated. operation (string): a high-level description of the long-term operation that is going on in the service. exc_text (string): the text of the logged exception. """ record = logging.makeLogRecord(kwargs) # Show in stdout, together with the messages we produce # ourselves. shell_handler.handle(record) # Write on the global log file. self.file_handler.handle(record) if record.levelno >= logging.WARNING: if hasattr(record, "service_name") and \ hasattr(record, "service_shard"): coord = "%s,%s" % (record.service_name, record.service_shard) else: coord = "" self._last_messages.append({ "message": record.msg, "coord": coord, "operation": getattr(record, "operation", ""), "severity": record.levelname, "timestamp": record.created, "exc_text": getattr(record, "exc_text", None) }) @rpc_method def last_messages(self): return list(self._last_messages)
class LogService(Service): """Logger service. """ LAST_MESSAGES_COUNT = 100 def __init__(self, shard): Service.__init__(self, shard) # Determine location of log file, and make directories. log_dir = os.path.join(config.log_dir, "cms") if not mkdir(config.log_dir) or \ not mkdir(log_dir): logger.error("Cannot create necessary directories.") self.exit() return log_filename = "%d.log" % int(time.time()) # Install a global file handler. self.file_handler = FileHandler(os.path.join(log_dir, log_filename), mode='w', encoding='utf-8') self.file_handler.setLevel(logging.DEBUG) self.file_handler.setFormatter(DetailedFormatter(False)) root_logger.addHandler(self.file_handler) # Provide a symlink to the latest log file. try: os.remove(os.path.join(log_dir, "last.log")) except OSError: pass os.symlink(log_filename, os.path.join(log_dir, "last.log")) self._last_messages = deque(maxlen=self.LAST_MESSAGES_COUNT) @rpc_method def Log(self, **kwargs): """Log a message. Receive the attributes of a LogRecord, rebuild and handle it. The given keyword arguments will contain: msg (string): the message to log. levelname (string): the name of the level, one of "DEBUG", "INFO", "WARNING", "ERROR" and "CRITICAL". levelno (int): a numeric value denoting the level, one of the constants defined by the logging module. In fact, `levelno == getattr(logging, levelname)` (yes, it is redundant). created (float): when the log message was emitted (as an UNIX timestamp, seconds from epoch). And they may contain: service_name (string) and service_shard (int): the coords of the service where this message was generated. operation (string): a high-level description of the long-term operation that is going on in the service. exc_text (string): the text of the logged exception. """ record = logging.makeLogRecord(kwargs) # Show in stdout, together with the messages we produce # ourselves. shell_handler.handle(record) # Write on the global log file. self.file_handler.handle(record) if record.levelno >= logging.WARNING: if hasattr(record, "service_name") and \ hasattr(record, "service_shard"): coord = "%s,%s" % (record.service_name, record.service_shard) else: coord = "" self._last_messages.append({ "message": record.msg, "coord": coord, "operation": getattr(record, "operation", ""), "severity": record.levelname, "timestamp": record.created, "exc_text": getattr(record, "exc_text", None)}) @rpc_method def last_messages(self): return list(self._last_messages)