edit : 기본 rest서버구조 추가
This commit is contained in:
211
custom_logger/colorlog/colorlog.py
Normal file
211
custom_logger/colorlog/colorlog.py
Normal file
@@ -0,0 +1,211 @@
|
||||
"""The ColoredFormatter class."""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import logging
|
||||
import sys
|
||||
|
||||
from .escape_codes import escape_codes, parse_colors
|
||||
|
||||
__all__ = ('escape_codes', 'default_log_colors', 'ColoredFormatter',
|
||||
'LevelFormatter', 'TTYColoredFormatter')
|
||||
|
||||
# The default colors to use for the debug levels
|
||||
default_log_colors = {
|
||||
'DEBUG': 'white',
|
||||
'INFO': 'green',
|
||||
'WARNING': 'yellow',
|
||||
'ERROR': 'red',
|
||||
'CRITICAL': 'bold_red',
|
||||
}
|
||||
|
||||
# The default format to use for each style
|
||||
default_formats = {
|
||||
'%': '%(log_color)s%(levelname)s:%(name)s:%(message)s',
|
||||
'{': '{log_color}{levelname}:{name}:{message}',
|
||||
'$': '${log_color}${levelname}:${name}:${message}'
|
||||
}
|
||||
|
||||
|
||||
class ColoredRecord(object):
|
||||
"""
|
||||
Wraps a LogRecord, adding named escape codes to the internal dict.
|
||||
|
||||
The internal dict is used when formatting the message (by the PercentStyle,
|
||||
StrFormatStyle, and StringTemplateStyle classes).
|
||||
"""
|
||||
|
||||
def __init__(self, record):
|
||||
"""Add attributes from the escape_codes dict and the record."""
|
||||
self.__dict__.update(escape_codes)
|
||||
self.__dict__.update(record.__dict__)
|
||||
|
||||
# Keep a reference to the original record so ``__getattr__`` can
|
||||
# access functions that are not in ``__dict__``
|
||||
self.__record = record
|
||||
|
||||
def __getattr__(self, name):
|
||||
return getattr(self.__record, name)
|
||||
|
||||
|
||||
class ColoredFormatter(logging.Formatter):
|
||||
"""
|
||||
A formatter that allows colors to be placed in the format string.
|
||||
|
||||
Intended to help in creating more readable logging output.
|
||||
"""
|
||||
|
||||
def __init__(self, fmt=None, datefmt=None, style='%',
|
||||
log_colors=None, reset=True,
|
||||
secondary_log_colors=None):
|
||||
"""
|
||||
Set the format and colors the ColoredFormatter will use.
|
||||
|
||||
The ``fmt``, ``datefmt`` and ``style`` args are passed on to the
|
||||
``logging.Formatter`` constructor.
|
||||
|
||||
The ``secondary_log_colors`` argument can be used to create additional
|
||||
``log_color`` attributes. Each key in the dictionary will set
|
||||
``{key}_log_color``, using the value to select from a different
|
||||
``log_colors`` set.
|
||||
|
||||
:Parameters:
|
||||
- fmt (str): The format string to use
|
||||
- datefmt (str): A format string for the date
|
||||
- log_colors (dict):
|
||||
A mapping of log level names to color names
|
||||
- reset (bool):
|
||||
Implicitly append a color reset to all records unless False
|
||||
- style ('%' or '{' or '$'):
|
||||
The format style to use. (*No meaning prior to Python 3.2.*)
|
||||
- secondary_log_colors (dict):
|
||||
Map secondary ``log_color`` attributes. (*New in version 2.6.*)
|
||||
"""
|
||||
if fmt is None:
|
||||
if sys.version_info > (3, 2):
|
||||
fmt = default_formats[style]
|
||||
else:
|
||||
fmt = default_formats['%']
|
||||
|
||||
if sys.version_info > (3, 2):
|
||||
super(ColoredFormatter, self).__init__(fmt, datefmt, style)
|
||||
elif sys.version_info > (2, 7):
|
||||
super(ColoredFormatter, self).__init__(fmt, datefmt)
|
||||
else:
|
||||
logging.Formatter.__init__(self, fmt, datefmt)
|
||||
|
||||
self.log_colors = (
|
||||
log_colors if log_colors is not None else default_log_colors)
|
||||
self.secondary_log_colors = secondary_log_colors
|
||||
self.reset = reset
|
||||
|
||||
def color(self, log_colors, level_name):
|
||||
"""Return escape codes from a ``log_colors`` dict."""
|
||||
return parse_colors(log_colors.get(level_name, ""))
|
||||
|
||||
def format(self, record):
|
||||
"""Format a message from a record object."""
|
||||
record = ColoredRecord(record)
|
||||
record.log_color = self.color(self.log_colors, record.levelname)
|
||||
|
||||
# Set secondary log colors
|
||||
if self.secondary_log_colors:
|
||||
for name, log_colors in self.secondary_log_colors.items():
|
||||
color = self.color(log_colors, record.levelname)
|
||||
setattr(record, name + '_log_color', color)
|
||||
|
||||
# Format the message
|
||||
if sys.version_info > (2, 7):
|
||||
message = super(ColoredFormatter, self).format(record)
|
||||
else:
|
||||
message = logging.Formatter.format(self, record)
|
||||
|
||||
# Add a reset code to the end of the message
|
||||
# (if it wasn't explicitly added in format str)
|
||||
if self.reset and not message.endswith(escape_codes['reset']):
|
||||
message += escape_codes['reset']
|
||||
|
||||
return message
|
||||
|
||||
|
||||
class LevelFormatter(ColoredFormatter):
|
||||
"""An extension of ColoredFormatter that uses per-level format strings."""
|
||||
|
||||
def __init__(self, fmt=None, datefmt=None, style='%',
|
||||
log_colors=None, reset=True,
|
||||
secondary_log_colors=None):
|
||||
"""
|
||||
Set the per-loglevel format that will be used.
|
||||
|
||||
Supports fmt as a dict. All other args are passed on to the
|
||||
``colorlog.ColoredFormatter`` constructor.
|
||||
|
||||
:Parameters:
|
||||
- fmt (dict):
|
||||
A mapping of log levels (represented as strings, e.g. 'WARNING') to
|
||||
different formatters. (*New in version 2.7.0)
|
||||
(All other parameters are the same as in colorlog.ColoredFormatter)
|
||||
|
||||
Example:
|
||||
|
||||
formatter = colorlog.LevelFormatter(fmt={
|
||||
'DEBUG':'%(log_color)s%(msg)s (%(module)s:%(lineno)d)',
|
||||
'INFO': '%(log_color)s%(msg)s',
|
||||
'WARNING': '%(log_color)sWARN: %(msg)s (%(module)s:%(lineno)d)',
|
||||
'ERROR': '%(log_color)sERROR: %(msg)s (%(module)s:%(lineno)d)',
|
||||
'CRITICAL': '%(log_color)sCRIT: %(msg)s (%(module)s:%(lineno)d)',
|
||||
})
|
||||
"""
|
||||
if sys.version_info > (2, 7):
|
||||
super(LevelFormatter, self).__init__(
|
||||
fmt=fmt, datefmt=datefmt, style=style, log_colors=log_colors,
|
||||
reset=reset, secondary_log_colors=secondary_log_colors)
|
||||
else:
|
||||
ColoredFormatter.__init__(
|
||||
self, fmt=fmt, datefmt=datefmt, style=style,
|
||||
log_colors=log_colors, reset=reset,
|
||||
secondary_log_colors=secondary_log_colors)
|
||||
self.style = style
|
||||
self.fmt = fmt
|
||||
|
||||
def format(self, record):
|
||||
"""Customize the message format based on the log level."""
|
||||
if isinstance(self.fmt, dict):
|
||||
self._fmt = self.fmt[record.levelname]
|
||||
if sys.version_info > (3, 2):
|
||||
# Update self._style because we've changed self._fmt
|
||||
# (code based on stdlib's logging.Formatter.__init__())
|
||||
if self.style not in logging._STYLES:
|
||||
raise ValueError('Style must be one of: %s' % ','.join(
|
||||
logging._STYLES.keys()))
|
||||
self._style = logging._STYLES[self.style][0](self._fmt)
|
||||
|
||||
if sys.version_info > (2, 7):
|
||||
message = super(LevelFormatter, self).format(record)
|
||||
else:
|
||||
message = ColoredFormatter.format(self, record)
|
||||
|
||||
return message
|
||||
|
||||
|
||||
class TTYColoredFormatter(ColoredFormatter):
|
||||
"""
|
||||
Blanks all color codes if not running under a TTY.
|
||||
|
||||
This is useful when you want to be able to pipe colorlog output to a file.
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""Overwrite the `reset` argument to False if stream is not a TTY."""
|
||||
self.stream = kwargs.pop('stream')
|
||||
|
||||
# Both `reset` and `isatty` must be true to insert reset codes.
|
||||
kwargs['reset'] = kwargs.get('reset', True) and self.stream.isatty()
|
||||
|
||||
ColoredFormatter.__init__(self, *args, **kwargs)
|
||||
|
||||
def color(self, log_colors, level_name):
|
||||
"""Only returns colors if STDOUT is a TTY."""
|
||||
if not self.stream.isatty():
|
||||
log_colors = {}
|
||||
return ColoredFormatter.color(self, log_colors, level_name)
|
||||
61
custom_logger/colorlog/escape_codes.py
Normal file
61
custom_logger/colorlog/escape_codes.py
Normal file
@@ -0,0 +1,61 @@
|
||||
"""
|
||||
Generates a dictionary of ANSI escape codes.
|
||||
|
||||
http://en.wikipedia.org/wiki/ANSI_escape_code
|
||||
|
||||
Uses colorama as an optional dependency to support color on Windows
|
||||
"""
|
||||
|
||||
try:
|
||||
import colorama
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
colorama.init()
|
||||
|
||||
__all__ = ('escape_codes', 'parse_colors')
|
||||
|
||||
|
||||
# Returns escape codes from format codes
|
||||
def esc(*x):
|
||||
return '\033[' + ';'.join(x) + 'm'
|
||||
|
||||
|
||||
# The initial list of escape codes
|
||||
escape_codes = {
|
||||
'reset': esc('0'),
|
||||
'bold': esc('01'),
|
||||
'thin': esc('02')
|
||||
}
|
||||
|
||||
# The color names
|
||||
COLORS = [
|
||||
'black',
|
||||
'red',
|
||||
'green',
|
||||
'yellow',
|
||||
'blue',
|
||||
'purple',
|
||||
'cyan',
|
||||
'white'
|
||||
]
|
||||
|
||||
PREFIXES = [
|
||||
# Foreground without prefix
|
||||
('3', ''), ('01;3', 'bold_'), ('02;3', 'thin_'),
|
||||
|
||||
# Foreground with fg_ prefix
|
||||
('3', 'fg_'), ('01;3', 'fg_bold_'), ('02;3', 'fg_thin_'),
|
||||
|
||||
# Background with bg_ prefix - bold/light works differently
|
||||
('4', 'bg_'), ('10', 'bg_bold_'),
|
||||
]
|
||||
|
||||
for prefix, prefix_name in PREFIXES:
|
||||
for code, name in enumerate(COLORS):
|
||||
escape_codes[prefix_name + name] = esc(prefix + str(code))
|
||||
|
||||
|
||||
def parse_colors(sequence):
|
||||
"""Return escape codes from a color sequence."""
|
||||
return ''.join(escape_codes[n] for n in sequence.split(',') if n)
|
||||
47
custom_logger/colorlog/logging.py
Normal file
47
custom_logger/colorlog/logging.py
Normal file
@@ -0,0 +1,47 @@
|
||||
"""Wrappers around the logging module."""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import functools
|
||||
import logging
|
||||
|
||||
from .colorlog import ColoredFormatter
|
||||
|
||||
BASIC_FORMAT = "%(log_color)s%(levelname)s%(reset)s:%(name)s:%(message)s"
|
||||
|
||||
|
||||
def basicConfig(**kwargs):
|
||||
"""Call ``logging.basicConfig`` and override the formatter it creates."""
|
||||
logging.basicConfig(**kwargs)
|
||||
logging._acquireLock()
|
||||
try:
|
||||
stream = logging.root.handlers[0]
|
||||
stream.setFormatter(
|
||||
ColoredFormatter(
|
||||
fmt=kwargs.get('format', BASIC_FORMAT),
|
||||
datefmt=kwargs.get('datefmt', None)))
|
||||
finally:
|
||||
logging._releaseLock()
|
||||
|
||||
|
||||
def ensure_configured(func):
|
||||
"""Modify a function to call ``basicConfig`` first if no handlers exist."""
|
||||
@functools.wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
if len(logging.root.handlers) == 0:
|
||||
basicConfig()
|
||||
return func(*args, **kwargs)
|
||||
return wrapper
|
||||
|
||||
|
||||
root = logging.root
|
||||
getLogger = logging.getLogger
|
||||
debug = ensure_configured(logging.debug)
|
||||
info = ensure_configured(logging.info)
|
||||
warning = ensure_configured(logging.warning)
|
||||
error = ensure_configured(logging.error)
|
||||
critical = ensure_configured(logging.critical)
|
||||
log = ensure_configured(logging.log)
|
||||
exception = ensure_configured(logging.exception)
|
||||
|
||||
StreamHandler = logging.StreamHandler
|
||||
116
custom_logger/custom_log.py
Normal file
116
custom_logger/custom_log.py
Normal file
@@ -0,0 +1,116 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
@file : custom_log.py
|
||||
@author: hsj100
|
||||
@license: DAOOLDNS
|
||||
@brief:
|
||||
@section Modify History
|
||||
- 2024-05-08 오후 2:33 hsj100 base
|
||||
"""
|
||||
|
||||
# import ai_config
|
||||
import datetime
|
||||
import os
|
||||
import logging
|
||||
|
||||
from custom_logger.colorlog.colorlog import ColoredFormatter
|
||||
|
||||
custom_logger = None
|
||||
_now = datetime.datetime.now()
|
||||
|
||||
LOGGER_NAME = 'dk'
|
||||
LOGGER_LEVEL = logging.INFO
|
||||
LOGGER_DIR = "log/"
|
||||
LOGGER_FILE_NAME = f'{_now.strftime("%Y-%m-%d %H_%M_%S")}_{LOGGER_NAME}.log'
|
||||
|
||||
__LOGGER_FILE_PATH = LOGGER_DIR + LOGGER_FILE_NAME
|
||||
# __LOGGER_FILE_PATH = None
|
||||
|
||||
|
||||
def logger_init(logger,
|
||||
level=logging.DEBUG,
|
||||
# color_log_format = "%(log_color)s%(levelname)s [%(asctime)s] [%(module)s::%(funcName)s()] %(message)s",
|
||||
color_log_format='%(log_color)s%(levelname)s: [%(module)s::%(funcName)s()] %(message)s',
|
||||
file_log_path=None,
|
||||
file_log_format='[%(levelname)s][%(asctime)s][%(module)s::%(funcName)s()] %(message)s'):
|
||||
"""
|
||||
로그 정보 초기화
|
||||
:param logger: 로거 객체
|
||||
:param level: 스트림 로그 출력 레벨 (지정 레벨 이상의 로그 정보만 출력됨)
|
||||
:param color_log_format: 스트림 로그 출력 형식
|
||||
:param file_log_path: 파일 로그 저장 경로 (경로 지정이 없을 경우에는 파일로그는 기록되지 않음)
|
||||
:param file_log_format: 파일 로그 출력 형식
|
||||
:return:
|
||||
"""
|
||||
|
||||
# stream handler
|
||||
color_stream_handler = logging.StreamHandler()
|
||||
color_stream_handler.setLevel(level)
|
||||
formatter = ColoredFormatter(
|
||||
color_log_format,
|
||||
datefmt=None,
|
||||
reset=True,
|
||||
log_colors={
|
||||
'DEBUG': 'cyan',
|
||||
'INFO': 'white,bold',
|
||||
'INFOV': 'cyan,bold',
|
||||
'WARNING': 'yellow',
|
||||
'ERROR': 'red,bold',
|
||||
'CRITICAL': 'red,bg_white',
|
||||
},
|
||||
secondary_log_colors={},
|
||||
style='%'
|
||||
)
|
||||
color_stream_handler.setFormatter(formatter)
|
||||
|
||||
logger.setLevel(level)
|
||||
logger.handlers = [] # No duplicated handlers
|
||||
logger.propagate = False # workaround for duplicated logs in ipython
|
||||
logger.addHandler(color_stream_handler)
|
||||
|
||||
logging.addLevelName(logging.INFO + 1, 'INFOV')
|
||||
|
||||
# file handler
|
||||
if file_log_path:
|
||||
file_handler = logging.FileHandler(file_log_path)
|
||||
formatter = logging.Formatter(file_log_format)
|
||||
file_handler.setFormatter(formatter)
|
||||
logger.addHandler(file_handler)
|
||||
|
||||
|
||||
# def _infov(self, msg, *args, **kwargs):
|
||||
# self.log(logging.INFO + 1, msg, *args, **kwargs)
|
||||
|
||||
|
||||
def get_logger():
|
||||
"""
|
||||
로거 객체 반환
|
||||
로거 객체가 없을 경우에는 로그 초기화를 진행하고 생성된 로그 객체를 반환함
|
||||
:return: 로그 객체
|
||||
"""
|
||||
|
||||
if not custom_logger.handlers:
|
||||
logger_init(custom_logger)
|
||||
|
||||
return custom_logger
|
||||
|
||||
|
||||
if custom_logger is None:
|
||||
custom_logger = logging.getLogger(LOGGER_NAME)
|
||||
|
||||
if not os.path.exists(LOGGER_DIR):
|
||||
os.makedirs(LOGGER_DIR)
|
||||
|
||||
if not __LOGGER_FILE_PATH:
|
||||
logger_init(custom_logger, level=LOGGER_LEVEL)
|
||||
else:
|
||||
if not os.path.exists(LOGGER_DIR):
|
||||
os.makedirs(LOGGER_DIR)
|
||||
logger_init(custom_logger, level=LOGGER_LEVEL, file_log_path=__LOGGER_FILE_PATH)
|
||||
|
||||
def test():
|
||||
custom_logger.info('Module: custom_log.py')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test()
|
||||
46
custom_logger/log_utils.py
Normal file
46
custom_logger/log_utils.py
Normal file
@@ -0,0 +1,46 @@
|
||||
import json
|
||||
import copy
|
||||
|
||||
from datetime import timedelta, datetime
|
||||
|
||||
def router_logger(logger_object, request, request_body=None, error=None):
|
||||
"""
|
||||
router 로깅
|
||||
|
||||
* 안면인식의 경우 이미지데이터가 너무 커서 길이로 return
|
||||
|
||||
:param logger_object: logger
|
||||
:param request: request info
|
||||
:param request_body: request body info, defaults to None
|
||||
:param error: traceback, defaults to None
|
||||
"""
|
||||
_fr_api_name = "/api/services/AE/FR-Recognize"
|
||||
|
||||
time_format = '%Y/%m/%d %H:%M:%S'
|
||||
|
||||
request_body_dict = copy.deepcopy(request_body.dict()) if request_body else None
|
||||
|
||||
if request.url.path == _fr_api_name:
|
||||
|
||||
# NOTE 타겟이 하나일 경우 기준
|
||||
_detect_dict = {
|
||||
"id": request_body_dict["targets"]["detect_list"][0]["id"],
|
||||
"worker_image": str(len(request_body_dict["targets"]["detect_list"][0]["worker_image"])),
|
||||
"target_image": str(len(request_body_dict["targets"]["detect_list"][0]["target_image"]))
|
||||
}
|
||||
_detect_list = [_detect_dict]
|
||||
request_body_dict["targets"]["detect_list"] = _detect_list
|
||||
|
||||
|
||||
log_dict = dict(
|
||||
api = request.url.path,
|
||||
datetimeKST = (datetime.utcnow() + timedelta(hours=9)).strftime(time_format),
|
||||
clientIP = request.state.ip,
|
||||
request = request_body_dict,
|
||||
errorDetail = f"{error.format_exc()}" if error else None
|
||||
)
|
||||
|
||||
if error :
|
||||
logger_object.error(json.dumps(log_dict, ensure_ascii=False))
|
||||
else:
|
||||
logger_object.info(json.dumps(log_dict, ensure_ascii=False))
|
||||
Reference in New Issue
Block a user