Skip to content

Instantly share code, notes, and snippets.

@gwerbin
Created November 7, 2022 21:09
Show Gist options
  • Save gwerbin/6e997e1e4f68e4cc2d046cd4238e26cb to your computer and use it in GitHub Desktop.
Save gwerbin/6e997e1e4f68e4cc2d046cd4238e26cb to your computer and use it in GitHub Desktop.
Easy logging setup
r"""Quickly configure a single logger.
Based on: https://docs.python.org/3/library/logging.html#logging.basicConfig
Adapted from my rejected PR:
https://github.com/python/cpython/compare/main...gwerbin:cpython:gwerbin/basicconfig-any-logger
"""
import logging
from collections.abc import Callable, Sequence
from enum import Enum
from typing import Protocol, TypeVar
class LogFormatStyle(str, Enum):
percent = "%"
format = "{"
template = "$"
# https://github.com/python/typeshed/blob/main/stdlib/_typeshed/__init__.pyi#L222-L223
_T_contra = TypeVar("_T_contra", contravariant=True)
class SupportsWrite(Protocol[_T_contra]):
def write(self, __s: _T_contra) -> object: ...
def configure(
logger: logging.Logger | str | None = None,
level: int | str = logging.INFO,
# Default Formatter options, for handlers that don't already have a Formatter
format: str | None = None,
date_format: str | None = None,
format_style: LogFormatStyle = LogFormatStyle.percent,
# Create a StreamHandler
stream: SupportsWrite | None = None,
# Create a FileHandler
filename: str | Path | None = None,
file_mode: str = "w",
file_encoding: str = "utf-8",
file_encoding_errors: str = "backslashreplace",
# Additional arbitrary handlers
handlers: logging.Handler | Sequence[logging.Handler] = (),
# Delete existing handlers before adding new ones
clear_existing: bool = False,
):
if not isinstance(logger, logging.Logger):
logger = logging.getLogger(logger)
logger.setLevel(level)
if clear_existing:
# Use [:] to make a copy. Don't mutate while iterating!
existing_handlers = logger.handlers[:]
for handler in existing_handlers:
logger.removeHandler(handler)
default_formatter = logging.Formatter(
fmt=format,
datefmt=date_format,
style=format_style,
)
handlers = list(handlers)
for handler in handlers:
if handler.formatter is None:
handler.setFormatter(default_formatter)
if stream is not None:
stream_handler = StreamHandler(stream)
stream_handler.setFormatter(default_formatter)
handlers.append(stream_handler)
if filename is not None:
file_handler = logging.FileHandler(
filename=filename,
mode=file_mode,
encoding=file_encoding,
error=file_encoding_errors,
)
file_handler.setFormatter(default_formatter)
handlers.append(file_handler)
logger.handlers.extend(handlers)
return logger
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment