logging shiny
https://docs.python.org/3/library/logging.html
기본 Logging
import logging
# 로그 설정
logging.basicConfig(
level=logging.DEBUG, # 출력할 로그의 레벨 설정
format="%(asctime)s - %(levelname)s - %(message)s", # 로그 출력 형식
datefmt="%Y-%m-%d %H:%M:%S", # 날짜 형식
)
# 로그 출력
logging.debug("이것은 디버그 메시지입니다.")
logging.info("이것은 정보 메시지입니다.")
logging.warning("이것은 경고 메시지입니다.")
logging.error("이것은 에러 메시지입니다.")
logging.critical("이것은 치명적인 에러 메시지입니다.")
Logger사용
import logging
logger = logging.getLogger(__name__)
logging.basicConfig(
level=logging.WARNING,
format = '%(asctime)s | %(levelname)s | %(message)s',
datefmt = '%m-%d-%Y %H:%M:%S',
# filename='example.log',
# encoding='utf-8',
)
logger.debug('This message should go to the log file')
logger.info('So should this')
logger.warning('And this, too')
logger.error('And non-ASCII stuff, too, like Øresund and Malmö')
logger.critical("Critical")
dict사용
Introduction to Modern Logging (00:00 – 00:21)
- The video introduces best practices for logging in Python.
- Emphasizes that best practices are general guidelines, not strict rules.
Problems with Python’s Built-in Logging (00:38 – 01:18)
- The logging package is old, poorly documented, and often misused.
- Despite flaws, it is the de facto standard for Python logging.
Why Basic Logging is Not Enough (01:36 – 02:05)
- Basic
logging.basicConfig()
setups are too simple. - Multiple log destinations (stdout, files, email alerts) require better configuration.
Using dictConfig
for Logging Setup (02:05 – 02:49)
dictConfig
fromlogging.config
is the preferred way to configure logging.- Explicitly defines loggers, handlers, filters, and formatters.
Logging Workflow and Components (03:12 – 03:54)
- Loggers create log records with severity levels and metadata.
- Handlers determine where logs go (stdout, files, email).
- Formatters define the log message structure.
Logger Hierarchy and Propagation (05:19 – 05:55)
- Loggers form a hierarchical tree structure.
- Logs propagate up the hierarchy unless explicitly dropped.
Simplifying Logging Configuration (06:17 – 07:28)
- Keep all handlers at the root logger for simplicity.
- Use
logging.getLogger(name)
instead of top-level logging functions.
Configuring Logging with dictConfig
(08:05 – 09:35)
- Use external JSON or YAML files for configuration.
- JSON is preferred due to Python’s built-in support.
Advanced Logging: File Handlers & Formatting (10:37 – 11:55)
- Logs can be split between
stderr
and files usingRotatingFileHandler
. - Use detailed formatters to include timestamps and structured data.
Logging in JSON Format for Better Parsing (12:34 – 14:49)
- Plain text logs can be difficult to parse.
- Writing logs in JSON allows structured storage and easier analysis.
- Custom formatters may be required since Python lacks a built-in JSON formatter.
기본 logging
import logging –> logger만들기 –> 기본설정 지정 –> Message 기록
여기서 log를 console과 file에 기록하고 싶거나, level별로 따로 log를 저장하거나, 추가적으로 이메일 발송등을 하려면, handler와 filter가 필요하다.
하지만, Handler와 filter 대신 dictConfig을 활용한다.
Logging의 구조
Logger → Log Record → Handlers → Formatters → Output (stdout, file, etc.)
Logger
logger 객체는 logger.info(), logger.debug(), logger.exception() 등을 호출하여 로그를 남깁니다.
로그를 기록할 때, 로그 레코드(log record)라는 객체가 생성됩니다.
Log Record
Log Message 뿐만 아니라, severity, current time, current thread 나 async task, 소스코드에서의 location 등의 유용한 정보를 포함하는 객체입니다.
Logger는 특정 로그 레벨보다 낮은 메시지를 필터링할 수도 있습니다.
Handler
로그를 출력할 위치(예: 표준 출력, 파일, 이메일 전송 등)를 결정합니다.
핸들러는 각 로그 메시지를 받을 때 필터링을 수행할 수도 있습니다.
Formatter
로그 레코드를 문자열로 변환하여 출력 형식을 지정하는 역할을 합니다.
예를 들어, 로그 메시지 앞에 타임스탬프를 추가하거나 JSON 형식으로 변환할 수 있습니다.
이 구조를 그림으로 표현하면 다음과 같습니다.
Logging 설정 방법
로그를 JSON 형식으로 저장하면 분석이 쉬워집니다.
불필요한 로거와 핸들러를 만들지 않고, root logger에서 핸들링하는 것이 좋습니다.
실용적인 로깅 설정 방법
Handler를 root logger에만 배치하세요.
– 불필요한 하위 핸들러를 만들지 마세요.( 이렇게 하면 서드파티 라이브러리에서 생성된 로그도 동일한 방식으로 처리됩니다)
– 필터(filter)는 가급적 사용하지 마세요.
필요하다면 루트 로거 또는 핸들러에서만 사용하세요.
루트 로거를 직접 사용하지 마세요.
logging.getLogger(“my_logger”)를 사용하여 로거를 생성하고, 해당 로거를 사용하세요.
모든 로그는 루트 로거로 전파(propagation)되도록 설정하세요.
handler
의 종류
Python의 logging
모듈에서 제공하는 주요 handler
는 다음과 같습니다:
Handler | 설명 |
---|---|
StreamHandler | 콘솔(터미널)에 로그 출력 |
FileHandler | 파일에 로그 저장 |
RotatingFileHandler | 용량이 넘으면 새로운 파일로 자동 변경 |
TimedRotatingFileHandler | 일정 시간이 지나면 새로운 파일로 자동 변경 |
SocketHandler | 네트워크로 로그 전송 (TCP/IP) |
SMTPHandler | 이메일로 로그 전송 |
HTTPHandler | HTTP 요청을 통해 로그 전송 |
app.py
import logging
formatter_s = logging.Formatter('!!!%(asctime)s|%(levelname)s|%(message)s')
formatter_c = logging.Formatter('%(asctime)s|%(levelname)s|%(message)s')
logger = logging.getLogger("shinylog")
handler_s = logging.StreamHandler()
logger.setLevel(logging.DEBUG)
handler_s.setFormatter(formatter_s)
logger.addHandler(handler_s)
handler_c = logging.FileHandler(ASSET_DIR.joinpath("log_event.log"))
handler_c.setLevel(logging.CRITICAL)
handler_c.setFormatter(formatter_c)
logger.addHandler(handler_c)
# logger
logger.info("Is working well?")
logger.critical("AAAAAAAAAAA")
server.py
import logging
logger = logging.getLogger('shinylog')
logger.info(f"bropress endend {p} loading \n {temp_dr}")
logger.critical("bropress start")
기본 Logger
# assets/log_set.py
import logging
def get_logger(name):
logger = logging.getLogger(name)
logger.setLevel(logging.DEBUG) # 로그 레벨 설정
# 콘솔 핸들러 설정
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
# 로그 포맷 설정
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
ch.setFormatter(formatter)
# 핸들러를 로거에 추가
logger.addHandler(ch)
return logger
app.py
# app.py
from assets.log_set import get_logger
logger = get_logger("my_app")
# logger.info("app start")
logger.debug("debug message", extra={"x": "hello"})
logger.info("info message")
logger.warning("warning message")
logger.error("error message")
logger.critical("critical message")
try:
1 / 0
except ZeroDivisionError:
logger.exception("exception message")
server.py
# basic_server.py
from assets.log_set import get_logger
logger = get_logger(__name__)
logger.info("server start")
import logging
def get_logger(name):
logger = logging.getLogger(name)
logging_config = {
"level": logging.DEBUG,
"format": "%(asctime)s | %(name)s | %(levelname)s | %(message)s",
"datefmt": "%Y-%m-%d %H:%M:%S",
}
logging.basicConfig(**logging_config)
return logger
dictConfig를 통해 config
import logging
def get_logger(name):
logger = logging.getLogger(name)
logging_config = {
"level": logging.DEBUG,
"format": "%(asctime)s | %(name)s | %(levelname)s | %(message)s",
"datefmt": "%Y-%m-%d %H:%M:%S",
}
logging.basicConfig(**logging_config)
return logger
import logging
from shared import LOG_DIR
# print(f">>>>>>{LOG_DIR}")
logging_config = {
"version": 1,
"disable_existing_loggers": False,
"filters": {},
"formatters": {
"simple": {
"format": "%(asctime)s | %(name)s | %(levelname)s | %(message)s",
"datefmt": "%Y-%m-%d %H:%M:%S"
},
"verbose": {
"format": " [%(asctime)s | %(levelname)s | %(module)s | L%(lineno)d ] %(message)s",
"datefmt": "%Y-%m-%dT%H:%M:%S%z"
}
},
"handlers": {
"stdout": {
"class": "logging.StreamHandler",
"level": "INFO",
"formatter": "simple",
"stream": "ext://sys.stdout"
},
"file": {
"class": "logging.handlers.RotatingFileHandler",
"level": "WARNING",
"formatter": "verbose",
"filename": "template/multipage/logs/xxx_app.log",
"maxBytes": 10485760,
"backupCount": 20,
"encoding": "utf8"
}
},
"loggers": {
"root": {
"level": "DEBUG",
"handlers": ["stdout", "file"]
}
}
}
def get_logger(name):
logger = logging.getLogger(name)
logging.config.dictConfig(config=logging_config)
return logger