Spaces:
Paused
Paused
Mirrowel commited on
Commit ·
1d838ea
1
Parent(s): aa25dd9
refactor(logging): separate detailed failure logs from main stream
Browse filesThis refactoring separates comprehensive failure event logging into a dedicated file, preventing them from cluttering the main application logs.
- Establish a dedicated `failure_logger` for detailed, structured JSON logs written to `failures.log`. This logger does not propagate messages to the root logger.
- Modify `log_failure` to send complete failure context (including timestamp, error details, and request data) to the new `failure_logger`.
- Continue logging a concise summary of failures to the `rotator_library`'s main logger for high-level monitoring.
- Update the JSON formatter to directly handle pre-structured dictionary messages, simplifying log record creation.
src/rotator_library/failure_logger.py
CHANGED
|
@@ -2,57 +2,58 @@ import logging
|
|
| 2 |
import json
|
| 3 |
from logging.handlers import RotatingFileHandler
|
| 4 |
import os
|
|
|
|
| 5 |
|
| 6 |
def setup_failure_logger():
|
| 7 |
-
"""Sets up a dedicated JSON logger for
|
| 8 |
log_dir = "logs"
|
| 9 |
if not os.path.exists(log_dir):
|
| 10 |
os.makedirs(log_dir)
|
| 11 |
|
| 12 |
-
#
|
| 13 |
-
logger
|
| 14 |
-
logger
|
| 15 |
-
|
| 16 |
-
# Prevent logs from propagating to the root logger
|
| 17 |
logger.propagate = False
|
| 18 |
|
| 19 |
-
# Use a rotating file handler
|
| 20 |
handler = RotatingFileHandler(
|
| 21 |
os.path.join(log_dir, 'failures.log'),
|
| 22 |
maxBytes=5*1024*1024, # 5 MB
|
| 23 |
backupCount=2
|
| 24 |
)
|
| 25 |
|
| 26 |
-
# Custom JSON formatter
|
| 27 |
class JsonFormatter(logging.Formatter):
|
| 28 |
def format(self, record):
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
"level": record.levelname,
|
| 32 |
-
"message": record.getMessage()
|
| 33 |
-
}
|
| 34 |
-
return json.dumps(log_record)
|
| 35 |
|
| 36 |
handler.setFormatter(JsonFormatter())
|
| 37 |
|
| 38 |
-
# Add handler only if it hasn't been added before
|
| 39 |
-
if not
|
| 40 |
logger.addHandler(handler)
|
| 41 |
|
| 42 |
return logger
|
| 43 |
|
| 44 |
-
# Initialize the logger for
|
| 45 |
failure_logger = setup_failure_logger()
|
| 46 |
|
|
|
|
|
|
|
|
|
|
| 47 |
def log_failure(api_key: str, model: str, attempt: int, error: Exception, request_data: dict):
|
| 48 |
-
"""
|
| 49 |
-
|
| 50 |
-
|
|
|
|
| 51 |
raw_response = None
|
| 52 |
if hasattr(error, 'response') and hasattr(error.response, 'text'):
|
| 53 |
raw_response = error.response.text
|
| 54 |
|
| 55 |
-
|
|
|
|
| 56 |
"api_key_ending": api_key[-4:],
|
| 57 |
"model": model,
|
| 58 |
"attempt_number": attempt,
|
|
@@ -61,4 +62,11 @@ def log_failure(api_key: str, model: str, attempt: int, error: Exception, reques
|
|
| 61 |
"raw_response": raw_response,
|
| 62 |
"request_data": request_data,
|
| 63 |
}
|
| 64 |
-
failure_logger.error(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
import json
|
| 3 |
from logging.handlers import RotatingFileHandler
|
| 4 |
import os
|
| 5 |
+
from datetime import datetime
|
| 6 |
|
| 7 |
def setup_failure_logger():
|
| 8 |
+
"""Sets up a dedicated JSON logger for writing detailed failure logs to a file."""
|
| 9 |
log_dir = "logs"
|
| 10 |
if not os.path.exists(log_dir):
|
| 11 |
os.makedirs(log_dir)
|
| 12 |
|
| 13 |
+
# Create a logger specifically for failures.
|
| 14 |
+
# This logger will NOT propagate to the root logger.
|
| 15 |
+
logger = logging.getLogger('failure_logger')
|
| 16 |
+
logger.setLevel(logging.INFO)
|
|
|
|
| 17 |
logger.propagate = False
|
| 18 |
|
| 19 |
+
# Use a rotating file handler
|
| 20 |
handler = RotatingFileHandler(
|
| 21 |
os.path.join(log_dir, 'failures.log'),
|
| 22 |
maxBytes=5*1024*1024, # 5 MB
|
| 23 |
backupCount=2
|
| 24 |
)
|
| 25 |
|
| 26 |
+
# Custom JSON formatter for structured logs
|
| 27 |
class JsonFormatter(logging.Formatter):
|
| 28 |
def format(self, record):
|
| 29 |
+
# The message is already a dict, so we just format it as a JSON string
|
| 30 |
+
return json.dumps(record.msg)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 31 |
|
| 32 |
handler.setFormatter(JsonFormatter())
|
| 33 |
|
| 34 |
+
# Add handler only if it hasn't been added before
|
| 35 |
+
if not logger.handlers:
|
| 36 |
logger.addHandler(handler)
|
| 37 |
|
| 38 |
return logger
|
| 39 |
|
| 40 |
+
# Initialize the dedicated logger for detailed failure logs
|
| 41 |
failure_logger = setup_failure_logger()
|
| 42 |
|
| 43 |
+
# Get the main library logger for concise, propagated messages
|
| 44 |
+
main_lib_logger = logging.getLogger('rotator_library')
|
| 45 |
+
|
| 46 |
def log_failure(api_key: str, model: str, attempt: int, error: Exception, request_data: dict):
|
| 47 |
+
"""
|
| 48 |
+
Logs a detailed failure message to a file and a concise summary to the main logger.
|
| 49 |
+
"""
|
| 50 |
+
# 1. Log the full, detailed error to the dedicated failures.log file
|
| 51 |
raw_response = None
|
| 52 |
if hasattr(error, 'response') and hasattr(error.response, 'text'):
|
| 53 |
raw_response = error.response.text
|
| 54 |
|
| 55 |
+
detailed_log_data = {
|
| 56 |
+
"timestamp": datetime.utcnow().isoformat(),
|
| 57 |
"api_key_ending": api_key[-4:],
|
| 58 |
"model": model,
|
| 59 |
"attempt_number": attempt,
|
|
|
|
| 62 |
"raw_response": raw_response,
|
| 63 |
"request_data": request_data,
|
| 64 |
}
|
| 65 |
+
failure_logger.error(detailed_log_data)
|
| 66 |
+
|
| 67 |
+
# 2. Log a concise summary to the main library logger, which will propagate
|
| 68 |
+
summary_message = (
|
| 69 |
+
f"API call failed for model {model} with key ...{api_key[-4:]}. "
|
| 70 |
+
f"Error: {type(error).__name__}. See failures.log for details."
|
| 71 |
+
)
|
| 72 |
+
main_lib_logger.error(summary_message)
|