-
Notifications
You must be signed in to change notification settings - Fork 63
add structure file logging with log files rotating #209
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -11,27 +11,63 @@ | |||||||||||||||||||||||||
from app.utils.singleton import SingletonMetaNoArgs | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
# TODO: merge this wrapper with the one in structlog under one hood of AppLogger | ||||||||||||||||||||||||||
class BytesToTextIOWrapper: | ||||||||||||||||||||||||||
def __init__(self, handler, encoding="utf-8"): | ||||||||||||||||||||||||||
class RotatingBytesLogger: | ||||||||||||||||||||||||||
"""Logger that respects RotatingFileHandler's rotation capabilities.""" | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
def __init__(self, handler): | ||||||||||||||||||||||||||
self.handler = handler | ||||||||||||||||||||||||||
self.encoding = encoding | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
def write(self, b): | ||||||||||||||||||||||||||
if isinstance(b, bytes): | ||||||||||||||||||||||||||
self.handler.stream.write(b.decode(self.encoding)) | ||||||||||||||||||||||||||
else: | ||||||||||||||||||||||||||
self.handler.stream.write(b) | ||||||||||||||||||||||||||
self.handler.flush() | ||||||||||||||||||||||||||
def msg(self, message): | ||||||||||||||||||||||||||
"""Process a message and pass it through the handler's emit method.""" | ||||||||||||||||||||||||||
if isinstance(message, bytes): | ||||||||||||||||||||||||||
message = message.decode("utf-8") | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
# Create a log record that will trigger rotation checks | ||||||||||||||||||||||||||
record = logging.LogRecord( | ||||||||||||||||||||||||||
name="structlog", | ||||||||||||||||||||||||||
level=logging.INFO, | ||||||||||||||||||||||||||
pathname="", | ||||||||||||||||||||||||||
lineno=0, | ||||||||||||||||||||||||||
Comment on lines
+26
to
+30
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The LogRecord is created with hardcoded values for
Suggested change
Copilot uses AI. Check for mistakes. Positive FeedbackNegative Feedback |
||||||||||||||||||||||||||
msg=message.rstrip("\n"), | ||||||||||||||||||||||||||
args=(), | ||||||||||||||||||||||||||
exc_info=None | ||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
# Check if rotation is needed before emitting | ||||||||||||||||||||||||||
if self.handler.shouldRollover(record): | ||||||||||||||||||||||||||
self.handler.doRollover() | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
# Emit the record through the handler | ||||||||||||||||||||||||||
self.handler.emit(record) | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
# Required methods to make it compatible with structlog | ||||||||||||||||||||||||||
def debug(self, message): | ||||||||||||||||||||||||||
self.msg(message) | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
def info(self, message): | ||||||||||||||||||||||||||
self.msg(message) | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
def flush(self): | ||||||||||||||||||||||||||
self.handler.flush() | ||||||||||||||||||||||||||
def warning(self, message): | ||||||||||||||||||||||||||
self.msg(message) | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
def error(self, message): | ||||||||||||||||||||||||||
self.msg(message) | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
def critical(self, message): | ||||||||||||||||||||||||||
self.msg(message) | ||||||||||||||||||||||||||
Comment on lines
+45
to
+57
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. All logging level methods (debug, info, warning, error, critical) delegate to the same Copilot uses AI. Check for mistakes. Positive FeedbackNegative Feedback |
||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
class RotatingBytesLoggerFactory: | ||||||||||||||||||||||||||
"""Factory that creates loggers that respect file rotation.""" | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
def __init__(self, handler): | ||||||||||||||||||||||||||
self.handler = handler | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
def close(self): | ||||||||||||||||||||||||||
self.handler.close() | ||||||||||||||||||||||||||
def __call__(self, *args, **kwargs): | ||||||||||||||||||||||||||
return RotatingBytesLogger(self.handler) | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
@define(slots=True) | ||||||||||||||||||||||||||
@define | ||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The
Suggested change
Copilot uses AI. Check for mistakes. Positive FeedbackNegative Feedback There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. slots are true by default There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The
Suggested change
Copilot uses AI. Check for mistakes. Positive FeedbackNegative Feedback |
||||||||||||||||||||||||||
class AppStructLogger(metaclass=SingletonMetaNoArgs): | ||||||||||||||||||||||||||
_logger: structlog.BoundLogger = field(init=False) | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
|
@@ -40,8 +76,7 @@ def __attrs_post_init__(self): | |||||||||||||||||||||||||
_log_path = Path(f"{_log_date}_{os.getpid()}.log") | ||||||||||||||||||||||||||
_handler = RotatingFileHandler( | ||||||||||||||||||||||||||
filename=_log_path, | ||||||||||||||||||||||||||
mode="a", | ||||||||||||||||||||||||||
maxBytes=10 * 1024 * 1024, | ||||||||||||||||||||||||||
maxBytes=10 * 1024 * 1024, # 10MB | ||||||||||||||||||||||||||
backupCount=5, | ||||||||||||||||||||||||||
encoding="utf-8" | ||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||
|
@@ -55,9 +90,7 @@ def __attrs_post_init__(self): | |||||||||||||||||||||||||
structlog.processors.TimeStamper(fmt="iso", utc=True), | ||||||||||||||||||||||||||
structlog.processors.JSONRenderer(serializer=orjson.dumps), | ||||||||||||||||||||||||||
], | ||||||||||||||||||||||||||
logger_factory=structlog.BytesLoggerFactory( | ||||||||||||||||||||||||||
file=BytesToTextIOWrapper(_handler) | ||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||
logger_factory=RotatingBytesLoggerFactory(_handler) | ||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||
self._logger = structlog.get_logger() | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The async logger methods like
ainfo
do not appear to be defined in theRotatingBytesLogger
class. Only synchronous methods (debug, info, warning, error, critical) are implemented, so this call will likely result in an AttributeError.Copilot uses AI. Check for mistakes.