feat(backend): Truncate logging on NotificationManager and LLMBlocks (#9939)

> Log entry with size 865.8K exceeds maximum size of 256.0K

Some logs are just too large.

### Changes 🏗️

Truncate logging on NotificationManager and LLMBlocks

### Checklist 📋

#### For code changes:
- [x] I have clearly listed my changes in the PR description
- [x] I have made a test plan
- [x] I have tested my changes according to the test plan:
  <!-- Put your test plan here: -->
  - [x] Existing CI
This commit is contained in:
Zamil Majdy
2025-05-14 20:18:59 +01:00
committed by GitHub
parent e8fa996c2f
commit 1999ba38d9
4 changed files with 62 additions and 33 deletions

View File

@@ -23,10 +23,11 @@ from backend.data.model import (
)
from backend.integrations.providers import ProviderName
from backend.util import json
from backend.util.logging import TruncatedLogger
from backend.util.settings import BehaveAs, Settings
from backend.util.text import TextFormatter
logger = logging.getLogger(__name__)
logger = TruncatedLogger(logging.getLogger(__name__), "[LLM-Block]")
fmt = TextFormatter()
LLMProviderName = Literal[
@@ -439,7 +440,7 @@ def llm_call(
if not tool_calls and resp.stop_reason == "tool_use":
logger.warning(
"Tool use stop reason but no tool calls found in content. %s", resp
f"Tool use stop reason but no tool calls found in content. {resp}"
)
return LLMResponse(

View File

@@ -65,7 +65,7 @@ from backend.integrations.creds_manager import IntegrationCredentialsManager
from backend.util import json
from backend.util.decorator import error_logged, time_measured
from backend.util.file import clean_exec_files
from backend.util.logging import configure_logging
from backend.util.logging import TruncatedLogger, configure_logging
from backend.util.process import AppProcess, set_service_name
from backend.util.retry import func_retry
from backend.util.service import get_service_client
@@ -86,7 +86,7 @@ utilization_gauge = Gauge(
)
class LogMetadata:
class LogMetadata(TruncatedLogger):
def __init__(
self,
user_id: str,
@@ -95,8 +95,9 @@ class LogMetadata:
node_eid: str,
node_id: str,
block_name: str,
max_length: int = 1000,
):
self.metadata = {
metadata = {
"component": "ExecutionManager",
"user_id": user_id,
"graph_eid": graph_eid,
@@ -105,33 +106,13 @@ class LogMetadata:
"node_id": node_id,
"block_name": block_name,
}
self.prefix = f"[ExecutionManager|uid:{user_id}|gid:{graph_id}|nid:{node_id}]|geid:{graph_eid}|neid:{node_eid}|{block_name}]"
def info(self, msg: str, **extra):
msg = self._wrap(msg, **extra)
logger.info(msg, extra={"json_fields": {**self.metadata, **extra}})
def warning(self, msg: str, **extra):
msg = self._wrap(msg, **extra)
logger.warning(msg, extra={"json_fields": {**self.metadata, **extra}})
def error(self, msg: str, **extra):
msg = self._wrap(msg, **extra)
logger.error(msg, extra={"json_fields": {**self.metadata, **extra}})
def debug(self, msg: str, **extra):
msg = self._wrap(msg, **extra)
logger.debug(msg, extra={"json_fields": {**self.metadata, **extra}})
def exception(self, msg: str, **extra):
msg = self._wrap(msg, **extra)
logger.exception(msg, extra={"json_fields": {**self.metadata, **extra}})
def _wrap(self, msg: str, **extra):
extra_msg = str(extra or "")
if len(extra_msg) > 1000:
extra_msg = extra_msg[:1000] + "..."
return f"{self.prefix} {msg} {extra_msg}"
prefix = f"[ExecutionManager|uid:{user_id}|gid:{graph_id}|nid:{node_id}]|geid:{graph_eid}|neid:{node_eid}|{block_name}]"
super().__init__(
logger,
max_length=max_length,
prefix=prefix,
metadata=metadata,
)
T = TypeVar("T")

View File

@@ -37,6 +37,7 @@ from backend.data.rabbitmq import (
)
from backend.data.user import generate_unsubscribe_link
from backend.notifications.email import EmailSender
from backend.util.logging import TruncatedLogger
from backend.util.metrics import discord_send_alert
from backend.util.service import (
AppService,
@@ -46,7 +47,7 @@ from backend.util.service import (
)
from backend.util.settings import Settings
logger = logging.getLogger(__name__)
logger = TruncatedLogger(logging.getLogger(__name__), "[NotificationManager]")
settings = Settings()

View File

@@ -1,3 +1,5 @@
from logging import Logger
from backend.util.settings import AppEnvironment, BehaveAs, Settings
settings = Settings()
@@ -18,3 +20,47 @@ def configure_logging():
# Silence httpx logger
logging.getLogger("httpx").setLevel(logging.WARNING)
class TruncatedLogger:
def __init__(
self,
logger: Logger,
prefix: str = "",
metadata: dict | None = None,
max_length: int = 1000,
):
self.logger = logger
self.metadata = metadata or {}
self.max_length = max_length
self.prefix = prefix
def info(self, msg: str, **extra):
msg = self._wrap(msg, **extra)
self.logger.info(msg, extra=self._get_metadata(**extra))
def warning(self, msg: str, **extra):
msg = self._wrap(msg, **extra)
self.logger.warning(msg, extra=self._get_metadata(**extra))
def error(self, msg: str, **extra):
msg = self._wrap(msg, **extra)
self.logger.error(msg, extra=self._get_metadata(**extra))
def debug(self, msg: str, **extra):
msg = self._wrap(msg, **extra)
self.logger.debug(msg, extra=self._get_metadata(**extra))
def exception(self, msg: str, **extra):
msg = self._wrap(msg, **extra)
self.logger.exception(msg, extra=self._get_metadata(**extra))
def _get_metadata(self, **extra):
metadata = {**self.metadata, **extra}
return {"json_fields": metadata} if metadata else {}
def _wrap(self, msg: str, **extra):
extra_msg = str(extra or "")
if len(extra_msg) > 1000:
extra_msg = extra_msg[:1000] + "..."
return f"{self.prefix} {msg} {extra_msg}"