Skip to content

Instantly share code, notes, and snippets.

@felixhammerl
Created November 15, 2023 11:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save felixhammerl/dc9623ee22dec0ff2edd3e05bf02af52 to your computer and use it in GitHub Desktop.
Save felixhammerl/dc9623ee22dec0ff2edd3e05bf02af52 to your computer and use it in GitHub Desktop.
Lightweight Python logging
# File Path: src/config/__init__.py
import json
import os
from dataclasses import dataclass
from typing import List
from dacite.core import from_dict
@dataclass(frozen=True)
class Logging:
log_level: str
@dataclass(frozen=True)
class Config:
logging: Logging
def load() -> Config:
base_path = os.path.abspath(os.path.dirname(__file__))
env = os.environ.get("STAGE", "local")
config_path = os.path.join(base_path, f"{env}.json")
with open(config_path, "r") as f:
data = json.loads(f.read())
return from_dict(data_class=Config, data=data)
config = load()
from src.util.logger import get_logger, LogEvents
log = get_logger()
def some_function():
log.info(
event=LogEvents.SOME_INFO_EVENT,
some_stuff="some_structured_stuff",
more_stuff="even_more_stuff"
)
log.error(
event=LogEvents.SOME_ERROR_EVENT,
some_stuff="some_structured_stuff",
more_stuff="even_more_stuff"
)
# File Path: src/config/local.py
# Loaded for unit tests
{
"logging": {
"log_level": "DEBUG",
}
}
# File Path: src/util/logger.py
import inspect
import logging
from enum import Enum, auto
import structlog
from pythonjsonlogger import jsonlogger
from requests.exceptions import HTTPError
from structlog.types import EventDict
from src.config import config
def get_http_error_response(
logger: logging.Logger, method_name: str, event_dict: EventDict
) -> EventDict:
error = event_dict.get("error", None)
if error and isinstance(error, HTTPError):
event_dict["http_error"] = error.response.text
return event_dict
structlog.configure(
processors=[
structlog.processors.TimeStamper(),
structlog.stdlib.filter_by_level,
structlog.stdlib.add_logger_name,
structlog.stdlib.add_log_level,
get_http_error_response,
structlog.stdlib.PositionalArgumentsFormatter(),
structlog.processors.StackInfoRenderer(),
structlog.processors.format_exc_info,
structlog.processors.UnicodeDecoder(),
structlog.stdlib.render_to_log_kwargs,
],
context_class=dict,
logger_factory=structlog.stdlib.LoggerFactory(),
wrapper_class=structlog.stdlib.BoundLogger,
cache_logger_on_first_use=True,
)
handler = logging.StreamHandler()
handler.setFormatter(jsonlogger.JsonFormatter())
logger = logging.getLogger()
logger.addHandler(handler)
logger.setLevel(config.logging.log_level)
def get_logger():
frm = inspect.stack()[1]
mod = inspect.getmodule(frm[0])
return structlog.get_logger(mod.__name__)
class LogEvents(Enum):
def __str__(self) -> str:
return self.name
SOME_INFO_EVENT = auto()
SOME_ERROR_EVENT = auto()
[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true
[packages]
requests = "*"
python-json-logger = "*"
structlog = "*"
[requires]
python_version = "3.10"
# File Path: src/config/prod.py
# Loaded for prod environment
{
"logging": {
"log_level": "INFO",
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment