7827cda224
- logger.py: note why log path uses abspath(__file__) instead of a relative path - token.py: note why [7:] slice is safe (startswith already verified) - ipcalc.py: explain /32 single-host and /31 RFC-3021 point-to-point special cases; explain why (~netmask) must be masked with 0xFFFFFFFF (Python ~int returns a negative arbitrary-precision value, not a 32-bit unsigned integer) - notes.py: document the module-level _table_ready flag lifetime; explain why tzinfo is stripped before passing datetime to mysql-connector - admin.py: document the module-level _tables_initialized flag lifetime Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
41 lines
978 B
Python
41 lines
978 B
Python
import logging
|
|
import os
|
|
from logging.handlers import RotatingFileHandler
|
|
|
|
# Absolute path so the log dir is always next to this file, regardless of CWD.
|
|
_LOG_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "logs")
|
|
os.makedirs(_LOG_DIR, exist_ok=True)
|
|
|
|
_FMT = "%(asctime)s [%(levelname)s] %(message)s"
|
|
_formatter = logging.Formatter(_FMT)
|
|
|
|
_MAX_BYTES = 5 * 1024 * 1024 # 5 MB per file
|
|
_BACKUP_COUNT = 3
|
|
|
|
|
|
def _rotating(filename, level):
|
|
h = RotatingFileHandler(
|
|
os.path.join(_LOG_DIR, filename),
|
|
maxBytes=_MAX_BYTES,
|
|
backupCount=_BACKUP_COUNT,
|
|
encoding="utf-8",
|
|
)
|
|
h.setLevel(level)
|
|
h.setFormatter(_formatter)
|
|
return h
|
|
|
|
|
|
_console = logging.StreamHandler()
|
|
_console.setFormatter(_formatter)
|
|
|
|
logging.basicConfig(
|
|
level=logging.INFO,
|
|
handlers=[
|
|
_rotating("app.log", logging.INFO),
|
|
_rotating("error.log", logging.ERROR),
|
|
_console,
|
|
],
|
|
)
|
|
|
|
logger = logging.getLogger("main")
|