Simplify Python Logging with a Wrapper that Captures Accurate Call Sites

Learn how to create a Python logging wrapper that reduces repetitive code, ensures log entries show the correct filename and line number using the inspect module, and loads configuration once for global use, improving readability and maintainability in large applications.

Ops Development & AI Practice
Ops Development & AI Practice
Ops Development & AI Practice
Simplify Python Logging with a Wrapper that Captures Accurate Call Sites

Motivation for a logging wrapper

Direct use of Python's logging module in many places leads to repetitive boiler‑plate and, when the calls are wrapped, the recorded filename and line number point to the wrapper instead of the real call site. A thin wrapper class can centralise configuration, eliminate duplication, and preserve accurate caller information.

Implementation

The wrapper is a class with class‑level logger configuration and convenience methods for each log level. The configuration file is loaded once at class definition time.

import logging
import logging.config
import inspect

class Log(object):
    # Load configuration a single time
    logging.config.fileConfig('/etc/logging.conf')
    __logger = logging.getLogger('script')

    @classmethod
    def set_level(cls, level):
        cls.__logger.setLevel(level)

    @classmethod
    def debug(cls, msg, *args, **kwargs):
        cls._log(logging.DEBUG, msg, *args, **kwargs)

    @classmethod
    def info(cls, msg, *args, **kwargs):
        cls._log(logging.INFO, msg, *args, **kwargs)

    @classmethod
    def warning(cls, msg, *args, **kwargs):
        cls._log(logging.WARNING, msg, *args, **kwargs)

    @classmethod
    def error(cls, msg, *args, **kwargs):
        cls._log(logging.ERROR, msg, *args, **kwargs)

    @classmethod
    def critical(cls, msg, *args, **kwargs):
        cls._log(logging.CRITICAL, msg, *args, **kwargs)

    @classmethod
    def _log(cls, level, msg, *args, **kwargs):
        # Retrieve the frame of the original caller (two levels up)
        frame = inspect.currentframe().f_back.f_back
        caller = inspect.getframeinfo(frame)
        # Pass accurate filename and line number via the extra dict
        cls.__logger.log(level, msg, *args, **kwargs,
                         extra={'filename': caller.filename,
                                'lineno': caller.lineno})

    @classmethod
    def get_logger(cls):
        return cls.__logger

How caller information is obtained

The inspect module inspects the call stack at runtime. In _log the following steps are performed: inspect.currentframe() returns the current stack frame (the _log method). .f_back moves one level up to the public wrapper method ( debug, info, etc.). .f_back.f_back reaches the frame that invoked the wrapper, i.e., the real call site in user code. inspect.getframeinfo(frame) extracts filename and lineno, which are then supplied to the logger via the extra argument.

Convenient logging methods

Each log level (DEBUG, INFO, WARNING, ERROR, CRITICAL) is exposed as a class method that forwards its arguments to _log. This allows developers to write concise statements such as Log.info('operation completed') without manually obtaining a logger or handling configuration.

Configuration loading

The call to logging.config.fileConfig('/etc/logging.conf') occurs when the class is defined, ensuring the configuration file is parsed only once. All subsequent logging calls share the same settings, eliminating repeated I/O and guaranteeing consistent formatting, handlers, and level thresholds across the entire application.

Benefits

Simplified usage : One‑line calls ( Log.debug(...), Log.error(...)) replace repetitive logger acquisition code.

Accurate source location : By inspecting two frames up, the log record contains the true filename and line number of the original call.

Single configuration load : The logging configuration is read once, reducing overhead and ensuring uniform behavior.

Conclusion

Wrapping the standard logging module in a lightweight class that leverages inspect solves the common problem of misplaced log locations while keeping the API terse. This pattern is especially valuable in large Python projects where consistent, readable logging and minimal boiler‑plate are critical for debugging and maintenance.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

PythonloggingWrapperinspect
Ops Development & AI Practice
Written by

Ops Development & AI Practice

DevSecOps engineer sharing experiences and insights on AI, Web3, and Claude code development. Aims to help solve technical challenges, improve development efficiency, and grow through community interaction. Feel free to comment and discuss.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.