1. Why not print?

The production environment requires log classification, persistence, and formatting control.print As soon as I went down, my eyes went dark when I was troubleshooting the problem.

2. Quick start: five major components of logging

componentsResponsibilities
LoggerLog entry, divided into levels
HandlerOutput destination (file, terminal, network)
FormatterFormat output style
FilterFilter by condition
LevelDEBUG → INFO → WARNING → ERROR → CRITICAL

3. One line configuration, plug and play

import logging

# The simplest configuration (output to terminal and file simultaneously)
logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
    datefmt="%Y-%m-%d %H:%M:%S",
    handlers=[
        logging.StreamHandler(),                    # terminal
        logging.FileHandler("app.log", encoding="utf-8"),  # document
    ],
)

log = logging.getLogger(__name__)

log.info("Service started successfully")
log.warning("Disk usage exceeds 80%%")
log.error("Database connection timeout", exc_info=True)  # Automatic print stack

4. Practical Tips: Log Rotation

Unlimited file growth will overwhelm the disk, so use RotatingFileHandler Automatic cutting:

from logging.handlers import RotatingFileHandler

handler = RotatingFileHandler(
    "app.log",
    maxBytes=10 * 1024 * 1024,  # 10MB
    backupCount=5,              # reserve 5 backups
    encoding="utf-8",
)
handler.setFormatter(logging.Formatter(
    "%(asctime)s [%(levelname)s] %(name)s: %(message)s"
))

5. Layered by modularity

For large projects, there is no need to do it all. Each module has its own Logger:

# utils/db.py
logger = logging.getLogger("app.db")

# utils/http.py
logger = logging.getLogger("app.http")

Cooperate logging.getLogger("app") Unified configuration, child Logger inherits parent configuration, worry-free.

6. Advanced: JSON log (connected to ELK / log platform)

import json
import logging


class JsonFormatter(logging.Formatter):
    def format(self, record: logging.LogRecord) -> str:
        log_obj = {
            "time": self.formatTime(record, self.datefmt),
            "level": record.levelname,
            "logger": record.name,
            "message": record.getMessage(),
        }
        if record.exc_info and record.exc_info[0]:
            log_obj["exception"] = self.formatException(record.exc_info)
        return json.dumps(log_obj, ensure_ascii=False)

7. Guide to avoid pitfalls

pitCorrect approach
logger = logging.getLogger() Empty ginsenguse __name__, to facilitate positioning the module
f"user={user}" Use f-string for placeholderuse "user=%s" % user Lazy evaluation to avoid unnecessary overhead
Repeatedly add Handler (call multiple times basicConfig)use not logger.handlers Test empty, or use a singleton
Chinese garbled charactersFileHandler plus encoding="utf-8"
Print DEBUG log in production environmentUse environment variables to control the level:level=getattr(logging, os.getenv("LOG_LEVEL", "INFO"))

8. Out-of-the-box templates

import logging
import logging.handlers
import sys
from pathlib import Path


def setup_logger(
    name: str = __name__,
    level: str = "INFO",
    log_file: str | None = None,
) -> logging.Logger:
    logger = logging.getLogger(name)
    if logger.handlers:
        return logger  # Prevent duplicate additions

    logger.setLevel(getattr(logging, level.upper(), logging.INFO))

    fmt = logging.Formatter(
        "%(asctime)s [%(levelname)s] %(name)s:%(lineno)d: %(message)s"
    )

    # terminal Handler(always output >= WARNING)
    console = logging.StreamHandler(sys.stdout)
    console.setLevel(logging.WARNING)
    console.setFormatter(fmt)
    logger.addHandler(console)

    # document Handler With rotation (output all levels)
    if log_file:
        Path(log_file).parent.mkdir(parents=True, exist_ok=True)
        handler = logging.handlers.RotatingFileHandler(
            log_file, maxBytes=10 << 20, backupCount=5, encoding="utf-8"
        )
        handler.setLevel(logging.DEBUG)
        handler.setFormatter(fmt)
        logger.addHandler(handler)

    return logger


# use
log = setup_logger("my_app", level="DEBUG", log_file="logs/my_app.log")
log.info("✅ Logger Initialization completed")

summary

  • small script: logging.basicConfig Done in one line
  • medium size project: Modular Logger + File Rotation
  • Large/Microservices: JSON format + JSON collection to ELK/Loki

From now on, all Python projects are worth using logging substitute print, this is the watershed between professional developers and novices.