python에서 loguru를 쓰던데 이건 뭐하는 패키지야?

loguru는 Python에서 로깅(logging) 을 훨씬 더 간편하고 강력하게 해주는 서드파티 라이브러리입니다. 기본 logging 모듈보다 사용하기 쉬우면서도, 실전에서 쓸 수 있는 여러 기능을 기본으로 제공합니다.

아래에 핵심적으로 어떤 역할을 하는지 정리해드리겠습니다.


✅ 왜 loguru를 쓰는가?

  1. 기본 설정 없이 바로 사용 가능

    • from loguru import logger 한 줄이면 바로 로그를 출력할 수 있습니다.

    • 기본 logging 모듈처럼 설정이나 핸들러 붙이는 번거로운 작업이 필요 없습니다.

  2. 더 읽기 쉬운 로그 출력

    • 로그 메시지에 시간, 파일명, 줄 번호, 레벨 등이 자동 포함되고, 컬러로 구분됩니다.
  3. 파일 로깅, 회전(Rotation), 압축(Compression) 기능 기본 내장

    • 예: 로그 파일이 일정 크기를 넘으면 자동으로 새 파일로 분리하거나, 오래된 로그를 압축/삭제할 수 있습니다.
  4. 구체적인 예외 추적 제공

    • logger.exception()은 traceback을 매우 보기 쉽게 출력해 줍니다.
  5. 함수 데코레이터로 자동 로깅

    • @logger.catch 데코레이터를 쓰면, 예외가 발생했을 때 자동으로 로그가 남습니다.

📌 간단한 예시

from loguru import logger

logger.debug("Debug message")
logger.info("Info message")
logger.warning("Warning message")
logger.error("Error message")
logger.critical("Critical message")

# 예외 자동 로깅
@logger.catch
def divide(x, y):
    return x / y

divide(1, 0)

🔧 로그 파일 저장도 간단하게

logger.add("my_log.log", rotation="500 MB", compression="zip", retention="10 days")

위 코드는 로그를 my_log.log 파일에 저장하면서,

  • 파일이 500MB를 넘으면 새 파일로 분리,

  • 오래된 로그는 zip으로 압축,

  • 10일 지난 로그는 삭제합니다.


결론

기존 logging 모듈보다 훨씬 덜 귀찮고, 바로 실전에서 쓸 수 있게 설계된 로그 도구입니다. 특히 CLI 도구나 백엔드 서비스 개발 시 아주 유용합니다. 다만, 너무 많은 기능을 자동으로 처리하기 때문에 내부 동작을 잘 이해하지 않고 쓰면 디버깅이 어려워질 수 있습니다. 쓰기 쉽지만, 무조건 좋은 건 아닙니다 — 설정의 명시성이 부족해서 프로젝트 규모가 커지면 팀 단위로는 오히려 관리가 복잡해질 수도 있습니다.

필요하다면 logurulogging의 내부 차이점도 비교해드릴 수 있습니다.

@logger.catch가 뭐하는건지 저것만 봐서는 잘 모르겠어. 파일 로깅은 자동으로 해주는거야?

좋은 질문입니다. 겉으로는 간단해 보이지만, @logger.catch나 파일 로깅에 대한 오해가 생기기 쉬운 부분입니다. 아래에 정확하게 짚어드리겠습니다.


🔍 @logger.catch 정확히 뭐하는가?

@logger.catch함수나 코드 블록에서 발생하는 예외를 자동으로 잡아서 로그로 남기는 데코레이터 입니다.

즉, try-except 없이도 예외가 터지면 자동으로 traceback과 함께 예쁘게 출력 해 줍니다.

📌 예시:

from loguru import logger

@logger.catch
def divide(x, y):
    return x / y

divide(1, 0)  # 예외 발생

결과 (터미널 출력):

vbnet

2025-07-25 12:34:56.789 | ERROR    | __main__:divide:5 - An error has occurred
Traceback (most recent call last):
  File "script.py", line 7, in <module>
    divide(1, 0)
  File "script.py", line 5, in divide
    return x / y
ZeroDivisionError: division by zero

✔️ 이건 try: ... except: 없이도 예외를 자동으로 잡아주는 것
✔️ 로그에 함수명, 줄 번호, 전체 traceback까지 아주 정리된 형태로 찍히는 것


⛔ 단점 및 주의사항

  • 예외를 무조건 삼켜버립니다. 즉, @logger.catch는 예외를 처리한 후 재발생시키지 않기 때문에 , 프로그램이 조용히 계속 진행될 수 있습니다.

    • 원래는 죽었어야 할 프로그램이 안 죽고 뭔가 이상하게 흘러갈 수 있다는 뜻입니다.
  • 예외를 다시 올리게 하려면 reraise=True를 명시해야 합니다:

@logger.catch(reraise=True)
def buggy():
    raise ValueError("bad!")

buggy()

📁 그럼 파일 로깅은 자동으로 되냐?

절대 자동으로 안 됩니다. logger.add()로 명시적으로 파일을 지정해줘야 합니다.

예시:

from loguru import logger

logger.add("debug.log", rotation="1 MB")

@logger.catch
def divide(x, y):
    return x / y

divide(1, 0)

✔️ 이래야 debug.log 파일에 로그가 저장됩니다.
❌ 아무것도 안 하면 콘솔 출력만 나가고, 파일은 생성되지 않습니다.


요약 정리

기능 자동 여부 설명
@logger.catch 예외를 자동으로 잡고 로그로 남김 (단, 예외는 기본적으로 무시됨)
파일 로깅 logger.add()로 직접 설정해야 저장됨

필요하시면 logger.add()의 다른 기능 (필터링, 포맷, 로그 레벨 별 분리 저장 등)도 알려드릴 수 있습니다.

cli entry에서 logger에 add 해주면 전체 패키지에서 다 공유되는거야?

네, 기본적으로는 그렇습니다.

logurulogger싱글톤 객체 입니다. 즉, logger.add()로 설정한 로그 핸들러는 해당 프로세스 내에서 전역(global)으로 공유 되며, 다른 모듈이나 서브패키지에서 임포트해서 써도 동일하게 적용됩니다.

하지만, 여기엔 몇 가지 조건과 오해하기 쉬운 함정이 있습니다.


✅ 기본 동작: CLI entry에서 logger.add() 하면 전체에서 공유됨

# main.py
from loguru import logger

logger.add("app.log")

def main():
    logger.info("Starting the app")

# utils.py
from loguru import logger

def helper():
    logger.info("Inside helper")
# main.py 실행
python main.py

app.log에는 main()helper() 양쪽 로그가 다 들어갑니다.
→ 이건 logger 객체가 싱글톤이기 때문 입니다. from loguru import logger는 항상 같은 인스턴스를 가져옵니다.


⚠️ 조심해야 할 점

1. logger.add() 호출 타이밍 중요

만약 logger.add()가 실행되기 전에 다른 모듈이 먼저 로그를 찍으면?

# utils.py
from loguru import logger
logger.info("hello from utils")  # add() 호출 전이므로 안 찍힐 수도 있음

# main.py
from loguru import logger
import utils

logger.add("log.txt")

utils가 먼저 로딩되면서 로그를 찍고, add()는 그 후에 호출되므로 처음 로그는 안 남을 수 있음.

💡 해결책: 가능한 한 초기화 로직은 entry point에서 가장 먼저 실행 해야 함.


2. 로그 핸들러가 중복되면 로그도 중복됨

다른 곳에서 또 logger.add()를 하면 핸들러가 추가로 붙기 때문에, 같은 로그가 여러 번 출력될 수 있음.

# main.py
logger.add("log1.txt")
logger.add("log2.txt")

→ 하나의 로그 메시지가 두 파일에 동시에 저장됨.
→ 불필요하게 많이 찍히는 상황도 생길 수 있음.


3. logger.remove() 안 하면 핸들러 계속 남음

logger.add()는 핸들러 ID를 반환합니다. 이를 저장해뒀다가 필요할 때 logger.remove(handler_id)로 제거해줘야 합니다. 안 그러면 계속 누적됩니다.

log_id = logger.add("log.txt")
logger.remove(log_id)

✅ 결론 정리

질문 답변
CLI entry에서 logger.add() 하면 다른 모듈에서도 로그 저장되나? ✅ 예, loguru.logger는 싱글톤이므로 전체 패키지에서 공유됩니다.
설정 타이밍 중요하지 않나? ⚠️ 중요합니다. 너무 늦게 add() 하면 초기 로그가 안 찍힐 수 있습니다.
중복 add() 문제 없나? ⚠️ 중복 출력될 수 있으니 주의해야 합니다.
핸들러 제거하려면? logger.remove(handler_id)로 수동 제거 필요합니다.

필요하시다면, logger.configure()를 써서 모듈 단위 설정 분리하는 방법도 안내해드릴 수 있습니다.

표준 출력은 자동으로 돼?

기본적으로 네,loguru는 표준 출력(stdout)으로 로그를 자동 출력합니다.
다만, 언제 어떻게 출력되는지는 내부에 미리 등록된 기본 핸들러(default sink) 덕분이고, 그 동작 방식에 대해 정확히 이해해야 합니다.


✅ 기본 상태: 자동으로 stdout 출력됨

from loguru import logger

logger.info("hello world")

위 코드는 아무런 설정도 없지만 stdout으로 다음과 같이 컬러풀한 로그를 출력합니다:

2025-07-25 14:12:30.123 | INFO     | __main__:<module>:3 - hello world

이건 loguru프로그램 시작 시 내부적으로 다음과 같은 핸들러를 자동 등록 해두기 때문입니다:

logger.add(sys.stderr, format=..., level="DEBUG", colorize=True)

✔️ 즉, 자동으로 stderr (터미널 출력)으로 로그를 내보냅니다.
✔️ stderr이긴 하지만, 일반적으로 터미널 화면에 보이므로 사실상 stdout처럼 동작한다고 봐도 무방합니다.


❗ 명시적으로 파일 로깅을 추가하면 어떻게 되나?

logger.add("mylog.log")
logger.info("only in file?")

아닙니다. 기본 stdout 출력도 여전히 유지됩니다.
즉, 로그는 콘솔에도 찍히고 , mylog.log 파일에도 저장됩니다.
기존 핸들러는 유지되며 새로운 핸들러가 추가되는 구조이기 때문입니다.


⛔ 표준 출력을 끄고 싶다면?

기존의 기본 핸들러를 명시적으로 제거 해야 합니다.

from loguru import logger

logger.remove()  # 기존 stdout 핸들러 제거
logger.add("log.txt")

이러면 stdout에는 아무 것도 출력되지 않고, 파일에만 저장됩니다.


💡 요약

질문 답변
아무 설정 없이 logger를 쓰면 어디로 로그가 나가나? 기본적으로 stderr (즉, 콘솔)로 나갑니다.
따로 add 안 해도 표준 출력이 된다? ✅ 네, 내부 기본 핸들러가 등록되어 있습니다.
파일로 로깅을 추가하면 표준 출력은 사라지나? ❌ 아니요, 둘 다 동작합니다.
stdout 출력을 끄려면? logger.remove()로 기존 핸들러를 제거해야 합니다.

필요하시면 stdout, stderr, 파일 등 출력 경로별로 레벨이나 포맷을 다르게 설정 하는 방법도 설명해드릴 수 있습니다.

원래는 logging.getLogger(name) 이렇게 해줬잖아. 이런건 필요없어?

좋은 지적입니다. loguru를 쓸 때는 logging.getLogger(__name__) 같은 모듈별 Logger 생성 을 더 이상 직접 할 필요가 없습니다.

하지만 그게 정말 "문제가 없느냐"는 건 얘기해볼 가치가 있습니다. 아래에 하나씩 설명드릴게요.


✅ loguru는 싱글톤 logger만 제공합니다

기존 표준 logging 모듈에서는 이렇게 모듈별로 logger를 만들었습니다:

import logging

logger = logging.getLogger(__name__)

이렇게 하면 모듈마다 이름이 붙은 logger가 생기고, 나중에 어떤 모듈에서 로그를 찍었는지 추적이 쉬웠죠.


🟦 하지만 loguru는 이렇게 안 씁니다

logurufrom loguru import logger 하면 무조건 전역 공유 싱글톤 인스턴스를 씁니다.

# 어떤 모듈에서든
from loguru import logger

logger.info("hello")

모듈명, 줄 번호, 함수명 같은 메타데이터는 자동으로 로그에 포함되기 때문에
__name__ 기반의 이름붙은 logger를 직접 쓸 이유가 없습니다.

예시 출력:

pgsql

2025-07-25 15:00:01.123 | INFO     | utils.db:get_user:42 - fetching user

utils.db 모듈, get_user 함수, 42번째 줄이 자동으로 찍힘.


❗ 진짜 다 필요 없을까?

기술적으로는 loguru가 모듈명 등을 로그 메시지에 자동으로 포함하니 __name__ 기반 logger가 필요 없어 보이지만, 아래와 같은 상황에선 표준 방식이 더 나을 수도 있습니다 :

상황 loguru 단점
라이브러리 개발 전역 싱글톤 logger는 외부 앱에서 핸들링이 어렵고, 사용자 설정을 침범할 수 있음
로거 이름 기반 필터링 loguru는 기본적으로 logger.name이 없으므로 이름 기반 레벨 제어가 불편함
기존logging 기반 시스템과 통합 logurulogging은 전혀 다른 시스템이라 브리징이 필요함

🔄 타협안: logurulogging처럼 쓰고 싶다면?

logurulogger.bind()를 제공해서 일부 유사한 기능을 흉내낼 수 있습니다:

logger = logger.bind(module="my.module")

logger.info("hello")  # 로그에 module="my.module" 붙음

또는 enqueue=Truefilter= 같은 옵션으로 조건부 로깅도 구현 가능합니다. 하지만 모듈별 logger 관리가 깔끔하지는 않습니다.


✅ 결론

질문 답변
loguru에서는 getLogger(__name__)이 필요 없나? ✅ 네, 자동 메타정보 덕분에 필요 없습니다.
그럼 완전히 대체되나? ❌ 아니요. 모듈별 로거 이름이 중요한 상황 (예: 필터링, 레벨 분리 등)에서는 부족할 수 있습니다.
표준 방식처럼 쓸 방법은 없나? 일부는 bind(), filter 등으로 흉내낼 수 있지만 완전히 동일하진 않습니다.

결론적으로, 작은 앱이나 스크립트에는loguru가 매우 편하지만,
라이브러리나 대규모 프로젝트에서는 신중하게 선택할 필요가 있습니다.

필요하시다면 logurulogging을 혼용하거나 브리징하는 방법도 안내해드릴 수 있습니다.

기본 표준출력의 로그 수준을 바꾸고 싶으면?

좋은 질문입니다. loguru에서 기본 표준 출력(stdout)의 로그 레벨을 바꾸려면logger.remove()로 기존 핸들러를 지우고, 원하는 수준으로 다시 등록해야 합니다.

loguru는 초기화 시 기본 stderr 핸들러를 DEBUG 레벨로 등록 합니다. 하지만 이 핸들러는 직접 제어할 수 없습니다. 따라서 표준 출력 로그 수준을 바꾸려면 다음 순서로 작업해야 합니다.


✅ 로그 수준 변경 방법 (표준 출력)

from loguru import logger
import sys

# 기존 핸들러 제거 (stdout 출력 포함됨)
logger.remove()

# 새 핸들러 추가: INFO 이상만 출력하도록
logger.add(sys.stdout, level="INFO")

이렇게 하면, DEBUG 로그는 출력되지 않고 INFO, WARNING, ERROR, CRITICAL만 출력됩니다.


🧪 예시 코드

from loguru import logger
import sys

logger.remove()  # 기본 stderr 핸들러 제거
logger.add(sys.stdout, level="WARNING")  # WARNING 이상만 출력

logger.debug("This is DEBUG")     # 출력 안 됨
logger.info("This is INFO")       # 출력 안 됨
logger.warning("This is WARNING") # 출력됨
logger.error("This is ERROR")     # 출력됨

📌 참고: 레벨 이름

loguru에서 사용할 수 있는 로그 레벨은 다음과 같습니다:

  • "TRACE" – 가장 상세한 로그

  • "DEBUG"

  • "INFO"

  • "SUCCESS" – loguru 전용 커스텀 레벨 (기본 INFO보다 약간 위)

  • "WARNING"

  • "ERROR"

  • "CRITICAL"

필요하다면 커스텀 레벨도 추가할 수 있습니다.


⚠️ 주의사항

  • logger.remove()를 하지 않고 logger.add(sys.stdout, level="INFO")만 하면, 기존 핸들러는 그대로 남아서 여전히 DEBUG까지 출력 됩니다. 즉, 중복 로그가 생깁니다.

  • sys.stdout 대신 sys.stderr으로도 출력할 수는 있으나, 대부분의 경우는 sys.stdout이 더 직관적입니다.


✅ 요약

하고 싶은 일| 해야 할 일
—|—
표준 출력 로그 레벨 바꾸기| logger.remove()logger.add(sys.stdout, level="INFO") 식으로 재등록
그냥 logger.add()만 하면 되나?| ❌ 안 됩니다. 기본 핸들러도 계속 남아있어 로그가 중복되거나 DEBUG까지 찍힙니다
stdout과 파일 각각 다른 레벨로 설정 가능?| ✅ 예. 핸들러는 여러 개 등록 가능하고 각각 다른 레벨을 가질 수 있습니다


필요하시면 여러 핸들러에 대해 레벨, 포맷, 필터 등을 다르게 적용하는 구성도 도와드릴 수 있습니다.

Categories:

Updated: