# Copyright (C) 2014 Linaro Limited # # Author: Senthil Kumaran S # Remi Duraffort # # This file is part of LAVA Dispatcher. # # LAVA Dispatcher is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # LAVA Dispatcher is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along # with this program; if not, see . import datetime import logging import yaml import zmq import zmq.auth from zmq.utils.strtypes import b class ZMQPushHandler(logging.Handler): def __init__(self, logging_url, master_cert, slave_cert, job_id, ipv6): super().__init__() # Keep track of the parameters self.logging_url = logging_url self.master_cert = master_cert self.slave_cert = slave_cert self.ipv6 = ipv6 # Create the PUSH socket # pylint: disable=no-member self.context = zmq.Context() self.socket = self.context.socket(zmq.PUSH) if ipv6: self.socket.setsockopt(zmq.IPV6, 1) # Load the certificates (if encryption is on) if master_cert is not None and slave_cert is not None: (client_public, client_private) = zmq.auth.load_certificate(slave_cert) self.socket.curve_publickey = client_public self.socket.curve_secretkey = client_private (server_public, _) = zmq.auth.load_certificate(master_cert) self.socket.curve_serverkey = server_public self.socket.connect(logging_url) self.job_id = str(job_id) self.formatter = logging.Formatter("%(message)s") def emit(self, record): msg = [b(self.job_id), b(self.formatter.format(record))] self.socket.send_multipart(msg) def close(self, linger=-1): # If the process crashes really early, the handler will be closed # directly by the logging module. In this case, close is called without # any arguments. super().close() self.context.destroy(linger=linger) class YAMLLogger(logging.Logger): def __init__(self, name): super().__init__(name) self.handler = None def addZMQHandler(self, logging_url, master_cert, slave_cert, job_id, ipv6): self.handler = ZMQPushHandler(logging_url, master_cert, slave_cert, job_id, ipv6) self.addHandler(self.handler) return self.handler def close(self, linger=-1): if self.handler is not None: self.handler.close(linger) self.removeHandler(self.handler) self.handler = None def log_message(self, level, level_name, message, *args, **kwargs): # pylint: disable=unused-argument # Build the dictionnary data = {'dt': datetime.datetime.utcnow().isoformat(), 'lvl': level_name} if isinstance(message, str) and args: data['msg'] = message % args else: data['msg'] = message # Set width to a really large value in order to always get one line. # But keep this reasonable because the logs will be loaded by CLoader # that is limited to around 10**7 chars data_str = yaml.dump(data, default_flow_style=True, default_style='"', width=10 ** 6, Dumper=yaml.CDumper)[:-1] # Test the limit and skip if the line is too long if len(data_str) >= 10 ** 6: if isinstance(message, str): data['msg'] = "" else: data['msg'] = {"skip": "line way too long ..."} data_str = yaml.dump(data, default_flow_style=True, default_style='"', width=10 ** 6, Dumper=yaml.CDumper)[:-1] self._log(level, data_str, ()) def exception(self, exc, *args, **kwargs): self.log_message(logging.ERROR, 'exception', exc, *args, **kwargs) def error(self, message, *args, **kwargs): self.log_message(logging.ERROR, 'error', message, *args, **kwargs) def warning(self, message, *args, **kwargs): self.log_message(logging.WARNING, 'warning', message, *args, **kwargs) def info(self, message, *args, **kwargs): self.log_message(logging.INFO, 'info', message, *args, **kwargs) def debug(self, message, *args, **kwargs): self.log_message(logging.DEBUG, 'debug', message, *args, **kwargs) def input(self, message, *args, **kwargs): self.log_message(logging.INFO, 'input', message, *args, **kwargs) def target(self, message, *args, **kwargs): self.log_message(logging.INFO, 'target', message, *args, **kwargs) def feedback(self, message, *args, **kwargs): self.log_message(logging.INFO, 'feedback', message, *args, **kwargs) def event(self, message, *args, **kwargs): self.log_message(logging.INFO, 'event', message, *args, **kwargs) def marker(self, message, *args, **kwargs): self.log_message(logging.INFO, 'marker', message, *args, **kwargs) def results(self, results, *args, **kwargs): if 'extra' in results and 'level' not in results: raise Exception("'level' is mandatory when 'extra' is used") self.log_message(logging.INFO, 'results', results, *args, **kwargs)