Python: Logging

calico·2025년 12월 8일

Python

목록 보기
5/10

1. Python Logging


Logging이 필요한 이유(print 대비 장점)


  • 이벤트 기록(에러, 성능, 상태)을 구조적으로 남김

  • 로그 레벨 기반 필터링(DEBUG, INFO, WARNING, ERROR) 가능

  • 파일/콘솔/네트워크 등 다양한 출력 방식 지원

  • 모듈별 logger 계층 구조로 충돌이 없음

  • 운영환경(Docker, Kubernetes)에서 로그 수집 시스템과 자연스럽게 연동

즉, print는 화면 출력일 뿐이고, logging은 운영 가능한 기록 시스템이다.



2. Logging의 역할


  • Diagnostic Logging: 문제 분석용

  • Audit Logging: 사용자 행동 기록/보안/성과 분석

개발·운영 모두에서 필수.



3. 라이브러리와 애플리케이션에서의 로깅 차이


라이브러리에서는 “NullHandler”만 사용


import logging
logging.getLogger(__name__).addHandler(logging.NullHandler())

라이브러리가 로그 출력을 결정하지 않도록 하기 위함
(출력은 애플리케이션이 결정)



4. Logger 설정 방식 3가지


1) INI(fileConfig)


  • 장점: 설정 파일로 분리

  • 단점: 확장성 떨어짐 → 최근 실무에서 거의 사용 안 함



2) dict / JSON(dictConfig)


  • YAML도 내부적으로 dict → 가장 실무에서 많이 사용

  • FastAPI/Django/Flask 모두 dictConfig 적용 가능



3) 코드 구성


  • 가장 유연하지만, 소스 코드 수정 필요

  • 작은 프로젝트에 적합



5. Logging 설정 실무 Best Practice


  • 설정은 YAML(dictConfig)로 분리

  • Docker/K8S 환경이면 stdout 로깅 사용

  • 로거 이름은 name 사용

  • 파일 기반 로그는 RotatingFileHandler 또는 TimedRotatingFileHandler

  • JSON structured logging 사용 시 운영 환경에서 검색 및 분석 매우 유리



6. JSON Structured Logging(실무 필수)


운영 환경에서는 대부분 ELK/Datadog/CloudWatch 등을 사용하므로
JSON 형태로 로그를 출력해야 검색·필터링·집계가 용이하다.



6.1 JSON Formatter 예시


  • logging_json_formatter.py
import json
import logging

class JsonFormatter(logging.Formatter):
    def format(self, record):
        log = {
            "timestamp": self.formatTime(record, self.datefmt),
            "level": record.levelname,
            "logger": record.name,
            "message": record.getMessage(),
            "filename": record.filename,
            "function": record.funcName,
            "line": record.lineno
        }
        return json.dumps(log)



7. YAML 기반 Logging 설정 예(logging.yaml)


  • FastAPI에서 사용할 수 있는 실무용 구성이며 uvicorn 서버 로그 + 애플리케이션 로그를 모두 JSON으로 출력한다.
version: 1
disable_existing_loggers: False

formatters:
  json:
    (): logging_json_formatter.JsonFormatter
    datefmt: "%Y-%m-%dT%H:%M:%S"

handlers:
  console:
    class: logging.StreamHandler
    formatter: json
    level: INFO
    stream: ext://sys.stdout

loggers:
  uvicorn:
    level: INFO
    handlers: [console]
    propagate: False

  uvicorn.error:
    level: INFO
    handlers: [console]
    propagate: False

  uvicorn.access:
    level: INFO
    handlers: [console]
    propagate: False

  app:
    level: DEBUG
    handlers: [console]
    propagate: False

root:
  level: INFO
  handlers: [console]



8. FastAPI에서 logging.yaml 불러오기


  • main.py
import yaml
import logging.config
from fastapi import FastAPI

def setup_logging():
    with open("logging.yaml", "rt") as f:
        config = yaml.safe_load(f)
        logging.config.dictConfig(config)

setup_logging()

logger = logging.getLogger("app")

app = FastAPI()

@app.get("/ping")
def ping():
    logger.info("ping called")
    return {"message": "pong"}



9. FastAPI 실무 Log 흐름 이해


  • uvicorn.access: HTTP 요청 로그

  • uvicorn.error: 서버 에러

  • uvicorn: uvicorn 자체 로그

  • app: FastAPI 애플리케이션 로직 로그


JSON 포맷으로 출력하면 다음과 같은 형태가 된다.

{"timestamp":"2025-01-01T12:00:00","level":"INFO","message":"ping called","logger":"app"}

K8S + ELK/Datadog에서 바로 읽힌다.



10. 로컬 서버 환경에서 파일 로그가 필요할 때


  • YAML에 핸들러만 추가
daily_file:
  class: logging.handlers.TimedRotatingFileHandler
  level: INFO
  formatter: json
  filename: logs/app.log
  when: midnight
  backupCount: 7
  encoding: utf-8
  • root에 추가
root:
  handlers: [console, daily_file]



profile
All views expressed here are solely my own and do not represent those of any affiliated organization.

0개의 댓글