[TIPS] Python Mixins - LoggingMixin

By khoanc, at: 21:22 Ngày 07 tháng 9 năm 2025

Thời gian đọc ước tính: __READING_TIME__ phút

[TIPS] Python Mixins - LoggingMixin
[TIPS] Python Mixins - LoggingMixin

Logging doesn’t have to be messy. Instead of sprinkling print() or logging calls everywhere, you can drop in a LoggingToolkitMixin to give any Python class structured, JSON-ready logs with almost no effort.

 

The Complete Mixin

 

import logging, json, datetime, contextvars, time, functools

correlation_id = contextvars.ContextVar("correlation_id", default="-")


class JsonFormatter(logging.Formatter):
    def format(self, record):
        payload = {
            "ts": datetime.datetime.utcfromtimestamp(record.created).isoformat() + "Z",
            "level": record.levelname,
            "logger": record.name,
            "msg": record.getMessage(),
        }
        fields = getattr(record, "fields", None)
        if isinstance(fields, dict):
            payload.update(fields)
        return json.dumps(payload, ensure_ascii=False)


def configure_logging_json(level="INFO"):
    handler = logging.StreamHandler()
    handler.setFormatter(JsonFormatter())
    root = logging.getLogger()
    root.handlers[:] = [handler]
    root.setLevel(getattr(logging, level.upper(), logging.INFO))


class LoggingMixin:
    @property
    def logger(self):
        return logging.getLogger(f"{self.__class__.__module__}.{self.__class__.__name__}")


class LoggingToolkitMixin(LoggingMixin):
    def log(self, level: int, msg: str, **fields):
        fields.setdefault("correlation_id", correlation_id.get())
        self.logger.log(level, msg, extra={"fields": fields})

    def debug(self, msg: str, **f): self.log(logging.DEBUG, msg, **f)
    def info(self, msg: str,  **f): self.log(logging.INFO,  msg, **f)
    def warn(self, msg: str,  **f): self.log(logging.WARNING, msg, **f)
    def error(self, msg: str, **f): self.log(logging.ERROR, msg, **f)

    def is_debug(self) -> bool:
        return self.logger.isEnabledFor(logging.DEBUG)

    def timed(self, name=None):
        def deco(func):
            label = name or func.__name__
            @functools.wraps(func)
            def wrapper(*args, **kwargs):
                t0 = time.perf_counter()
                try:
                    return func(*args, **kwargs)
                finally:
                    self.info("timed", op=label, ms=round((time.perf_counter()-t0)*1000, 2))
            return wrapper
        return deco


# Example usage
class PaymentService(LoggingToolkitMixin):
    def charge(self, order_id: str, amount: float):
        self.info("charge.start", order_id=order_id, amount=amount)
        # ... business logic ...
        self.info("charge.success", order_id=order_id)

 

How to Use It

 

1. Configure JSON logging once at startup

 

configure_logging_json("DEBUG")
correlation_id.set("Glinteco-2025")

 

2. Extend any class with the mixin

 

service = PaymentService()
service.charge("order-42", 19.99)

 

3. ​​​​​​​Get structured, JSON logs like this

 

{"ts": "2025-09-07T14:30:01Z", "level": "INFO", "logger": "__main__.PaymentService", "msg": "charge.start", "order_id": "order-42", "amount": 19.99, "correlation_id": "glinteco-2025"}
{"ts": "2025-09-07T14:30:01Z", "level": "INFO", "logger": "__main__.PaymentService", "msg": "charge.success", "order_id": "order-42", "correlation_id": "glinteco-2025"}

 

Why This Works

 

  • JSON formatter → machine-readable logs for search & monitoring tools
     

  • Correlation IDs → track requests across async tasks
     

  • Timing decorator → measure method execution times
     

  • Reusable Mixin → drop it into any class without boilerplate

 

Tag list:

Theo dõi

Theo dõi bản tin của chúng tôi và không bao giờ bỏ lỡ những tin tức mới nhất.