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
| components | Responsibilities |
|---|---|
| Logger | Log entry, divided into levels |
| Handler | Output destination (file, terminal, network) |
| Formatter | Format output style |
| Filter | Filter by condition |
| Level | DEBUG → 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
| pit | Correct approach |
|---|---|
logger = logging.getLogger() Empty ginseng | use __name__, to facilitate positioning the module |
f"user={user}" Use f-string for placeholder | use "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 characters | FileHandler plus encoding="utf-8" |
| Print DEBUG log in production environment | Use 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.basicConfigDone 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.