import abc import asyncio import json import sys from asyncio import AbstractEventLoop from typing import Optional, Union from aiologger import settings from aiologger.utils import loop_compat from aiologger.filters import Filterer from aiologger.formatters.base import Formatter from aiologger.formatters.json import JsonFormatter from aiologger.levels import LogLevel, get_level_name, check_level from aiologger.records import LogRecord # Handler relies on any formatter _default_formatter = Formatter() @loop_compat class Handler(Filterer): """ Handler instances dispatch logging events to specific destinations. The base handler class. Acts as a placeholder which defines the Handler interface. Handlers can optionally use Formatter instances to format records as desired. By default, no formatter is specified; in this case, the 'raw' message as determined by record.message is logged. """ def __init__(self, level: LogLevel = LogLevel.NOTSET) -> None: """ Initializes the instance - basically setting the formatter to None and the filter list to empty. """ Filterer.__init__(self) self._level = check_level(level) self.formatter: Formatter = _default_formatter @property @abc.abstractmethod def initialized(self): raise NotImplementedError() @property def level(self): return self._level @level.setter def level(self, value: Union[str, int, LogLevel]): """ Set the logging level of this handler. """ self._level = check_level(value) @abc.abstractmethod async def emit(self, record: LogRecord) -> None: """ Do whatever it takes to actually log the specified logging record. This version is intended to be implemented by subclasses and so raises a NotImplementedError. """ raise NotImplementedError( "emit must be implemented by Handler subclasses" ) async def handle(self, record: LogRecord) -> bool: """ Conditionally emit the specified logging record. Emission depends on filters which may have been added to the handler. Returns whether the filter passed the record for emission. """ rv = self.filter(record) if rv: await self.emit(record) return rv async def flush(self) -> None: """ Ensure all logging output has been flushed. This version does nothing and is intended to be implemented by subclasses. """ pass @abc.abstractmethod async def close(self) -> None: """ Tidy up any resources used by the handler. This version removes the handler from an internal map of handlers, _handlers, which is used for handler lookup by name. Subclasses should ensure that this gets called from overridden close() methods. """ raise NotImplementedError( "close must be implemented by Handler subclasses" ) async def handle_error( self, record: LogRecord, exception: Exception ) -> None: """ Handle errors which occur during an emit() call. This method should be called from handlers when an exception is encountered during an emit() call. This is what is mostly wanted for a logging system - most users will not care about errors in the logging system, they are more interested in application errors. You could, however, replace this with a custom handler if you wish. The record which was being processed is passed in to this method. """ if not settings.HANDLE_ERROR_FALLBACK_ENABLED: return msg = JsonFormatter.format_error_msg(record, exception) json.dump(msg, sys.stderr) sys.stderr.write("\n") def __repr__(self): level = get_level_name(self.level) return f"<${self.__class__.__name__} (${level})>"