Skip to main content

Aleatorik PyCommon - 구조화된 로깅 및 공통 유틸리티 라이브러리

Project description

소개

PyLogger는 Python 애플리케이션에서 구조화된 로그를 남기고, Fluent Bit 등 외부 로깅 시스템으로 전송할 수 있도록 도와주는 로깅 유틸리티입니다.

설정

환경 변수 설정

PyLogger는 프로젝트 .env 파일에서 다음 값을 읽어옵니다:

  • FLUENTBIT_URL: 로그를 전송할 Fluent Bit의 URL
  • COMPONENT_NAME: 로그에 출력할 서비스 이름 (e.g. datatransfer, noti)
  • SYSTEM_NAME: 로그에 출력할 시스템 이름 (e.g. aps, dp, common, cp)

지원하는 로그 레벨 및 카테고리

로그 레벨

  • trace
  • debug
  • info
  • warn
  • error
  • critical

로그 카테고리

  • request
  • response
  • service
  • outbound
  • excel
  • access
  • query
  • engine
  • authorize

주요 메서드

  • bind_base_info(): 컴포넌트와 시스템 정보로 기본 로그 컨텍스트를 초기화합니다.
  • bind_request_properties(request): 요청 정보(URL, 메서드, tenant-id, tenant-name, project-name 등 헤더)를 로그 컨텍스트에 바인딩합니다.
  • send_log(level, category, message, data=None, force_clean=False): 구조화된 로그를 Fluent Bit으로 전송합니다. data 딕셔너리로 추가 속성을 전달할 수 있으며, force_clean=True로 설정하면 기존 컨텍스트 없이 로깅합니다.
  • begin_request(): 새로운 요청 로그 컨텍스트를 시작하고 Token을 반환합니다.
  • end_request(token): 요청이 끝난 후 로그 컨텍스트를 정리합니다.

설치

필요한 패키지

  • loguru
  • pydantic-settings
  • requests

설치 방법

  1. Aleatorik-UI-Backend-Net 디렉토리로 이동하세요.
  2. uv 또는 pip로 설치하세요:
cd pycommon
uv sync

또는 pip 사용 시:

cd pycommon
pip install -e .

기본 사용법

1. 인스턴스 가져오기

from pylogger.logger import logger_instance

2. 로그 컨텍스트 바인딩

로그를 남기기 전에 기본 정보와 요청 정보를 바인딩합니다:

from fastapi import Request

async def some_endpoint(request: Request):
    logger_instance.bind_base_info()
    logger_instance.bind_request_properties(request)
    # ... 로직 ...

3. 로그 보내기

logger_instance.send_log(level="info", category="response", message="사용자가 로그인했습니다.")

커스텀 속성 추가

send_logdata 파라미터를 사용하여 추가 정보를 포함할 수 있습니다:

@app.get("/items/{item_id}")
async def read_item(request: Request, item_id: int):
    request.state.logger.send(
        level="info",
        category="request",
        message=f"아이템 {item_id} 조회 요청",
        data={"item_price": 20.5, "user_role": "guest"}
    )
    return {"item_id": item_id}

FastAPI 미들웨어 연동

LoggingClient 패턴을 사용하여 요청별 로깅 컨텍스트를 관리합니다.

LoggingClient 정의

from typing import Any
from pydantic import BaseModel
from pylogger.logger import logger_instance
from pylogger.utils.template import LogCategory, LogLevel


class LoggingClient:
    """요청 컨텍스트를 관리하고 모든 로그에 첨부합니다."""

    class RequestEntry(BaseModel):
        traceId: str | None = None
        userId: str | None = None
        userAgent: str | None = None
        requestPath: str | None = None
        method: str | None = None
        tenantInfo: dict[str, Any] | None = None

    def __init__(self, request_entry: RequestEntry | None = None):
        self._request_entry = request_entry or self.RequestEntry()

    def send(
        self,
        level: LogLevel,
        category: LogCategory,
        message: str,
        data: dict[str, Any] | None = None,
        force_clean: bool = False,
    ) -> None:
        request_data = self._request_entry.model_dump(exclude_none=True)
        merged = {**request_data, **(data or {})}

        logger_instance.bind_base_info()
        logger_instance.send_log(
            level=level,
            category=category,
            message=message,
            data=merged,
            force_clean=force_clean,
        )


# 시작/종료 로깅용 전역 인스턴스
logger_client = LoggingClient()

미들웨어 설정

from urllib.parse import urlparse
from fastapi import FastAPI, Request, Response
from fastapi.responses import JSONResponse
from pylogger.logger import logger_instance


def setup_logging_middleware(app: FastAPI) -> None:
    """FastAPI 앱에 로깅 미들웨어를 등록합니다."""

    @app.middleware("http")
    async def logging_ctx(request: Request, call_next):
        token = logger_instance.begin_request()
        try:
            logger_instance.bind_base_info()
            fields = logger_instance.bind_request_properties(request)

            # 요청별 로깅 클라이언트 생성
            entry = LoggingClient.RequestEntry(**fields)
            request.state.logger = LoggingClient(entry)

            clean_path = urlparse(str(request.url)).path.rstrip("/")
            excluded_suffixes = ("/health", "/openapi.json", "/docs", "/metrics")

            if clean_path.endswith(excluded_suffixes):
                return await call_next(request)

            request.state.logger.send(level="info", category="access", message=clean_path)

            response = await call_next(request)

            if isinstance(response, JSONResponse):
                body = response.body
            else:
                body = b"".join([chunk async for chunk in response.body_iterator])

            return Response(
                content=body,
                status_code=response.status_code,
                headers=dict(response.headers),
                media_type=response.media_type,
            )
        finally:
            logger_instance.end_request(token)

main.py에서 등록

from fastapi import FastAPI
from app.middleware.logging_middleware import setup_logging_middleware
from app.middleware.logging_client import logger_client

app = FastAPI(title="Your Service")
setup_logging_middleware(app)

# 라이프사이클 이벤트에는 logger_client 사용
logger_client.send(level="info", category="service", message="애플리케이션 시작")

# 커스텀 속성을 properties 필드에 추가하려면 data 파라미터 사용
logger_client.send(
    level="info",
    category="service",
    message="애플리케이션 시작",
    data={"partitionVer": "1.0.0", "userId": "me@vms-solutions.com"}
)

백그라운드 태스크 & ProcessPool

백그라운드 태스크나 ProcessPoolExecutor에서 로깅할 때는 (HTTP 요청 컨텍스트가 없음) 로그 컨텍스트를 전달하고 worker logger를 생성합니다:

# 백그라운드 태스크 생성 전에 요청에서 컨텍스트 캡처
log_context = request.state.logger._request_entry.model_dump(exclude_none=True)

# 백그라운드 태스크에서 worker logger 생성
worker_logger = LoggingClient(LoggingClient.RequestEntry(**log_context))
worker_logger.send(level="info", category="service", message="백그라운드 처리 중...")

이렇게 하면 백그라운드 워커의 로그에도 원본 요청의 traceId, userId, tenantInfo가 유지되어 로그 추적이 가능합니다.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

aleatorik_pycommon-1.53.7.tar.gz (10.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

aleatorik_pycommon-1.53.7-py3-none-any.whl (11.0 kB view details)

Uploaded Python 3

File details

Details for the file aleatorik_pycommon-1.53.7.tar.gz.

File metadata

  • Download URL: aleatorik_pycommon-1.53.7.tar.gz
  • Upload date:
  • Size: 10.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.11.13

File hashes

Hashes for aleatorik_pycommon-1.53.7.tar.gz
Algorithm Hash digest
SHA256 c8872f3ab94021d2c01a048868e33128879ebed220290aa09f21014291fb4b6b
MD5 ac3fc705bbd80a75d05d4a9a04ef4561
BLAKE2b-256 9c79fe797b5d9b832d2163555b5a4485293c77727960babe3b9ca56f503437b1

See more details on using hashes here.

File details

Details for the file aleatorik_pycommon-1.53.7-py3-none-any.whl.

File metadata

File hashes

Hashes for aleatorik_pycommon-1.53.7-py3-none-any.whl
Algorithm Hash digest
SHA256 00807482bf4ef2568d2693124de9b2f085ad3653a996662896200e2ba9587bfc
MD5 e1c579b598fce15f3fa5f53ddb04a98d
BLAKE2b-256 08f0031de8437ba65dac6a9a50adf56e94333c3392bbe554d018e22a07c5022d

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page