feat(api): Implement EventManager error logging and add coverage (#29204)
- Ensure `EventManager._notify_layers` logs exceptions instead of silently swallowing them so GraphEngine layer failures surface for debugging - Introduce unit tests to assert the logger captures the runtime error when collecting events - Enable the `S110` lint rule to catch `try-except-pass` patterns - Add proper error logging for existing `try-except-pass` blocks.
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import json
|
||||
import logging
|
||||
from abc import ABC, abstractmethod
|
||||
from collections.abc import Generator, Mapping, Sequence
|
||||
from typing import Any
|
||||
@@ -23,6 +24,8 @@ from core.tools.entities.tool_entities import ToolInvokeMeta
|
||||
from core.tools.tool_engine import ToolEngine
|
||||
from models.model import Message
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class CotAgentRunner(BaseAgentRunner, ABC):
|
||||
_is_first_iteration = True
|
||||
@@ -400,8 +403,8 @@ class CotAgentRunner(BaseAgentRunner, ABC):
|
||||
action_input=json.loads(message.tool_calls[0].function.arguments),
|
||||
)
|
||||
current_scratchpad.action_str = json.dumps(current_scratchpad.action.to_dict())
|
||||
except:
|
||||
pass
|
||||
except Exception:
|
||||
logger.exception("Failed to parse tool call from assistant message")
|
||||
elif isinstance(message, ToolPromptMessage):
|
||||
if current_scratchpad:
|
||||
assert isinstance(message.content, str)
|
||||
|
||||
@@ -253,7 +253,7 @@ class ProviderConfiguration(BaseModel):
|
||||
try:
|
||||
credentials[key] = encrypter.decrypt_token(tenant_id=self.tenant_id, token=credentials[key])
|
||||
except Exception:
|
||||
pass
|
||||
logger.exception("Failed to decrypt credential secret variable %s", key)
|
||||
|
||||
return self.obfuscated_credentials(
|
||||
credentials=credentials,
|
||||
@@ -765,7 +765,7 @@ class ProviderConfiguration(BaseModel):
|
||||
try:
|
||||
credentials[key] = encrypter.decrypt_token(tenant_id=self.tenant_id, token=credentials[key])
|
||||
except Exception:
|
||||
pass
|
||||
logger.exception("Failed to decrypt model credential secret variable %s", key)
|
||||
|
||||
current_credential_id = credential_record.id
|
||||
current_credential_name = credential_record.credential_name
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import logging
|
||||
from collections.abc import Sequence
|
||||
|
||||
import httpx
|
||||
@@ -8,6 +9,7 @@ from core.helper.download import download_with_size_limit
|
||||
from core.plugin.entities.marketplace import MarketplacePluginDeclaration
|
||||
|
||||
marketplace_api_url = URL(str(dify_config.MARKETPLACE_API_URL))
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def get_plugin_pkg_url(plugin_unique_identifier: str) -> str:
|
||||
@@ -55,7 +57,9 @@ def batch_fetch_plugin_manifests_ignore_deserialization_error(
|
||||
try:
|
||||
result.append(MarketplacePluginDeclaration.model_validate(plugin))
|
||||
except Exception:
|
||||
pass
|
||||
logger.exception(
|
||||
"Failed to deserialize marketplace plugin manifest for %s", plugin.get("plugin_id", "unknown")
|
||||
)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
@@ -521,4 +521,4 @@ class TencentDataTrace(BaseTraceInstance):
|
||||
if hasattr(self, "trace_client"):
|
||||
self.trace_client.shutdown()
|
||||
except Exception:
|
||||
pass
|
||||
logger.exception("[Tencent APM] Failed to shutdown trace client during cleanup")
|
||||
|
||||
@@ -723,7 +723,7 @@ class ToolManager:
|
||||
)
|
||||
except Exception:
|
||||
# app has been deleted
|
||||
pass
|
||||
logger.exception("Failed to transform workflow provider %s to controller", workflow_provider.id)
|
||||
|
||||
labels = ToolLabelManager.get_tools_labels(
|
||||
[cast(ToolProviderController, controller) for controller in workflow_provider_controllers]
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
Unified event manager for collecting and emitting events.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import threading
|
||||
import time
|
||||
from collections.abc import Generator
|
||||
@@ -12,6 +13,8 @@ from core.workflow.graph_events import GraphEngineEvent
|
||||
|
||||
from ..layers.base import GraphEngineLayer
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@final
|
||||
class ReadWriteLock:
|
||||
@@ -180,5 +183,4 @@ class EventManager:
|
||||
try:
|
||||
layer.on_event(event)
|
||||
except Exception:
|
||||
# Silently ignore layer errors during collection
|
||||
pass
|
||||
_logger.exception("Error in layer on_event, layer_type=%s", type(layer))
|
||||
|
||||
@@ -6,12 +6,15 @@ using the new Redis command channel, without requiring user permission checks.
|
||||
Supports stop, pause, and resume operations.
|
||||
"""
|
||||
|
||||
import logging
|
||||
from typing import final
|
||||
|
||||
from core.workflow.graph_engine.command_channels.redis_channel import RedisChannel
|
||||
from core.workflow.graph_engine.entities.commands import AbortCommand, GraphEngineCommand, PauseCommand
|
||||
from extensions.ext_redis import redis_client
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@final
|
||||
class GraphEngineManager:
|
||||
@@ -57,4 +60,4 @@ class GraphEngineManager:
|
||||
except Exception:
|
||||
# Silently fail if Redis is unavailable
|
||||
# The legacy control mechanisms will still work
|
||||
pass
|
||||
logger.exception("Failed to send graph engine command %s for task %s", command.__class__.__name__, task_id)
|
||||
|
||||
Reference in New Issue
Block a user