kailash 0.3.2__py3-none-any.whl → 0.4.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- kailash/__init__.py +33 -1
- kailash/access_control/__init__.py +129 -0
- kailash/access_control/managers.py +461 -0
- kailash/access_control/rule_evaluators.py +467 -0
- kailash/access_control_abac.py +825 -0
- kailash/config/__init__.py +27 -0
- kailash/config/database_config.py +359 -0
- kailash/database/__init__.py +28 -0
- kailash/database/execution_pipeline.py +499 -0
- kailash/middleware/__init__.py +306 -0
- kailash/middleware/auth/__init__.py +33 -0
- kailash/middleware/auth/access_control.py +436 -0
- kailash/middleware/auth/auth_manager.py +422 -0
- kailash/middleware/auth/jwt_auth.py +477 -0
- kailash/middleware/auth/kailash_jwt_auth.py +616 -0
- kailash/middleware/communication/__init__.py +37 -0
- kailash/middleware/communication/ai_chat.py +989 -0
- kailash/middleware/communication/api_gateway.py +802 -0
- kailash/middleware/communication/events.py +470 -0
- kailash/middleware/communication/realtime.py +710 -0
- kailash/middleware/core/__init__.py +21 -0
- kailash/middleware/core/agent_ui.py +890 -0
- kailash/middleware/core/schema.py +643 -0
- kailash/middleware/core/workflows.py +396 -0
- kailash/middleware/database/__init__.py +63 -0
- kailash/middleware/database/base.py +113 -0
- kailash/middleware/database/base_models.py +525 -0
- kailash/middleware/database/enums.py +106 -0
- kailash/middleware/database/migrations.py +12 -0
- kailash/{api/database.py → middleware/database/models.py} +183 -291
- kailash/middleware/database/repositories.py +685 -0
- kailash/middleware/database/session_manager.py +19 -0
- kailash/middleware/mcp/__init__.py +38 -0
- kailash/middleware/mcp/client_integration.py +585 -0
- kailash/middleware/mcp/enhanced_server.py +576 -0
- kailash/nodes/__init__.py +27 -3
- kailash/nodes/admin/__init__.py +42 -0
- kailash/nodes/admin/audit_log.py +794 -0
- kailash/nodes/admin/permission_check.py +864 -0
- kailash/nodes/admin/role_management.py +823 -0
- kailash/nodes/admin/security_event.py +1523 -0
- kailash/nodes/admin/user_management.py +944 -0
- kailash/nodes/ai/a2a.py +24 -7
- kailash/nodes/ai/ai_providers.py +248 -40
- kailash/nodes/ai/embedding_generator.py +11 -11
- kailash/nodes/ai/intelligent_agent_orchestrator.py +99 -11
- kailash/nodes/ai/llm_agent.py +436 -5
- kailash/nodes/ai/self_organizing.py +85 -10
- kailash/nodes/ai/vision_utils.py +148 -0
- kailash/nodes/alerts/__init__.py +26 -0
- kailash/nodes/alerts/base.py +234 -0
- kailash/nodes/alerts/discord.py +499 -0
- kailash/nodes/api/auth.py +287 -6
- kailash/nodes/api/rest.py +151 -0
- kailash/nodes/auth/__init__.py +17 -0
- kailash/nodes/auth/directory_integration.py +1228 -0
- kailash/nodes/auth/enterprise_auth_provider.py +1328 -0
- kailash/nodes/auth/mfa.py +2338 -0
- kailash/nodes/auth/risk_assessment.py +872 -0
- kailash/nodes/auth/session_management.py +1093 -0
- kailash/nodes/auth/sso.py +1040 -0
- kailash/nodes/base.py +344 -13
- kailash/nodes/base_cycle_aware.py +4 -2
- kailash/nodes/base_with_acl.py +1 -1
- kailash/nodes/code/python.py +283 -10
- kailash/nodes/compliance/__init__.py +9 -0
- kailash/nodes/compliance/data_retention.py +1888 -0
- kailash/nodes/compliance/gdpr.py +2004 -0
- kailash/nodes/data/__init__.py +22 -2
- kailash/nodes/data/async_connection.py +469 -0
- kailash/nodes/data/async_sql.py +757 -0
- kailash/nodes/data/async_vector.py +598 -0
- kailash/nodes/data/readers.py +767 -0
- kailash/nodes/data/retrieval.py +360 -1
- kailash/nodes/data/sharepoint_graph.py +397 -21
- kailash/nodes/data/sql.py +94 -5
- kailash/nodes/data/streaming.py +68 -8
- kailash/nodes/data/vector_db.py +54 -4
- kailash/nodes/enterprise/__init__.py +13 -0
- kailash/nodes/enterprise/batch_processor.py +741 -0
- kailash/nodes/enterprise/data_lineage.py +497 -0
- kailash/nodes/logic/convergence.py +31 -9
- kailash/nodes/logic/operations.py +14 -3
- kailash/nodes/mixins/__init__.py +8 -0
- kailash/nodes/mixins/event_emitter.py +201 -0
- kailash/nodes/mixins/mcp.py +9 -4
- kailash/nodes/mixins/security.py +165 -0
- kailash/nodes/monitoring/__init__.py +7 -0
- kailash/nodes/monitoring/performance_benchmark.py +2497 -0
- kailash/nodes/rag/__init__.py +284 -0
- kailash/nodes/rag/advanced.py +1615 -0
- kailash/nodes/rag/agentic.py +773 -0
- kailash/nodes/rag/conversational.py +999 -0
- kailash/nodes/rag/evaluation.py +875 -0
- kailash/nodes/rag/federated.py +1188 -0
- kailash/nodes/rag/graph.py +721 -0
- kailash/nodes/rag/multimodal.py +671 -0
- kailash/nodes/rag/optimized.py +933 -0
- kailash/nodes/rag/privacy.py +1059 -0
- kailash/nodes/rag/query_processing.py +1335 -0
- kailash/nodes/rag/realtime.py +764 -0
- kailash/nodes/rag/registry.py +547 -0
- kailash/nodes/rag/router.py +837 -0
- kailash/nodes/rag/similarity.py +1854 -0
- kailash/nodes/rag/strategies.py +566 -0
- kailash/nodes/rag/workflows.py +575 -0
- kailash/nodes/security/__init__.py +19 -0
- kailash/nodes/security/abac_evaluator.py +1411 -0
- kailash/nodes/security/audit_log.py +103 -0
- kailash/nodes/security/behavior_analysis.py +1893 -0
- kailash/nodes/security/credential_manager.py +401 -0
- kailash/nodes/security/rotating_credentials.py +760 -0
- kailash/nodes/security/security_event.py +133 -0
- kailash/nodes/security/threat_detection.py +1103 -0
- kailash/nodes/testing/__init__.py +9 -0
- kailash/nodes/testing/credential_testing.py +499 -0
- kailash/nodes/transform/__init__.py +10 -2
- kailash/nodes/transform/chunkers.py +592 -1
- kailash/nodes/transform/processors.py +484 -14
- kailash/nodes/validation.py +321 -0
- kailash/runtime/access_controlled.py +1 -1
- kailash/runtime/async_local.py +41 -7
- kailash/runtime/docker.py +1 -1
- kailash/runtime/local.py +474 -55
- kailash/runtime/parallel.py +1 -1
- kailash/runtime/parallel_cyclic.py +1 -1
- kailash/runtime/testing.py +210 -2
- kailash/security.py +1 -1
- kailash/utils/migrations/__init__.py +25 -0
- kailash/utils/migrations/generator.py +433 -0
- kailash/utils/migrations/models.py +231 -0
- kailash/utils/migrations/runner.py +489 -0
- kailash/utils/secure_logging.py +342 -0
- kailash/workflow/__init__.py +16 -0
- kailash/workflow/cyclic_runner.py +3 -4
- kailash/workflow/graph.py +70 -2
- kailash/workflow/resilience.py +249 -0
- kailash/workflow/templates.py +726 -0
- {kailash-0.3.2.dist-info → kailash-0.4.1.dist-info}/METADATA +256 -20
- kailash-0.4.1.dist-info/RECORD +227 -0
- kailash/api/__init__.py +0 -17
- kailash/api/__main__.py +0 -6
- kailash/api/studio_secure.py +0 -893
- kailash/mcp/__main__.py +0 -13
- kailash/mcp/server_new.py +0 -336
- kailash/mcp/servers/__init__.py +0 -12
- kailash-0.3.2.dist-info/RECORD +0 -136
- {kailash-0.3.2.dist-info → kailash-0.4.1.dist-info}/WHEEL +0 -0
- {kailash-0.3.2.dist-info → kailash-0.4.1.dist-info}/entry_points.txt +0 -0
- {kailash-0.3.2.dist-info → kailash-0.4.1.dist-info}/licenses/LICENSE +0 -0
- {kailash-0.3.2.dist-info → kailash-0.4.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,201 @@
|
|
1
|
+
"""
|
2
|
+
Event Emitter Mixin for Nodes
|
3
|
+
|
4
|
+
Provides event emission capabilities for nodes to integrate with the
|
5
|
+
middleware layer for real-time monitoring and communication.
|
6
|
+
"""
|
7
|
+
|
8
|
+
import logging
|
9
|
+
from typing import TYPE_CHECKING, Any, Dict, Optional
|
10
|
+
|
11
|
+
if TYPE_CHECKING:
|
12
|
+
from kailash.middleware.events import EventStream
|
13
|
+
|
14
|
+
logger = logging.getLogger(__name__)
|
15
|
+
|
16
|
+
|
17
|
+
class EventEmitterMixin:
|
18
|
+
"""
|
19
|
+
Mixin that adds event emission capabilities to nodes.
|
20
|
+
|
21
|
+
This mixin allows nodes to emit events that can be consumed by the
|
22
|
+
middleware layer for real-time monitoring, logging, and frontend updates.
|
23
|
+
"""
|
24
|
+
|
25
|
+
def __init__(self, *args, **kwargs):
|
26
|
+
super().__init__(*args, **kwargs)
|
27
|
+
self._event_stream: Optional["EventStream"] = None
|
28
|
+
self._session_id: Optional[str] = None
|
29
|
+
self._workflow_id: Optional[str] = None
|
30
|
+
self._execution_id: Optional[str] = None
|
31
|
+
|
32
|
+
def set_event_context(
|
33
|
+
self,
|
34
|
+
event_stream: "EventStream",
|
35
|
+
session_id: str = None,
|
36
|
+
workflow_id: str = None,
|
37
|
+
execution_id: str = None,
|
38
|
+
):
|
39
|
+
"""Set the event context for this node."""
|
40
|
+
self._event_stream = event_stream
|
41
|
+
self._session_id = session_id
|
42
|
+
self._workflow_id = workflow_id
|
43
|
+
self._execution_id = execution_id
|
44
|
+
|
45
|
+
async def emit_node_started(self, inputs: Dict[str, Any] = None):
|
46
|
+
"""Emit node started event."""
|
47
|
+
if self._event_stream:
|
48
|
+
try:
|
49
|
+
from kailash.middleware.events import EventType, NodeEvent
|
50
|
+
|
51
|
+
event = NodeEvent(
|
52
|
+
type=EventType.NODE_STARTED,
|
53
|
+
workflow_id=self._workflow_id,
|
54
|
+
node_id=getattr(self, "name", "unknown"),
|
55
|
+
node_type=self.__class__.__name__,
|
56
|
+
node_name=getattr(self, "name", "unknown"),
|
57
|
+
inputs=inputs or {},
|
58
|
+
session_id=self._session_id,
|
59
|
+
)
|
60
|
+
|
61
|
+
await self._event_stream.emit(event)
|
62
|
+
except Exception as e:
|
63
|
+
logger.warning(f"Failed to emit node started event: {e}")
|
64
|
+
|
65
|
+
async def emit_node_completed(
|
66
|
+
self, outputs: Dict[str, Any] = None, execution_time_ms: float = None
|
67
|
+
):
|
68
|
+
"""Emit node completed event."""
|
69
|
+
if self._event_stream:
|
70
|
+
try:
|
71
|
+
from kailash.middleware.events import EventType, NodeEvent
|
72
|
+
|
73
|
+
event = NodeEvent(
|
74
|
+
type=EventType.NODE_COMPLETED,
|
75
|
+
workflow_id=self._workflow_id,
|
76
|
+
node_id=getattr(self, "name", "unknown"),
|
77
|
+
node_type=self.__class__.__name__,
|
78
|
+
node_name=getattr(self, "name", "unknown"),
|
79
|
+
outputs=outputs or {},
|
80
|
+
execution_time_ms=execution_time_ms,
|
81
|
+
session_id=self._session_id,
|
82
|
+
)
|
83
|
+
|
84
|
+
await self._event_stream.emit(event)
|
85
|
+
except Exception as e:
|
86
|
+
logger.warning(f"Failed to emit node completed event: {e}")
|
87
|
+
|
88
|
+
async def emit_node_failed(self, error: str):
|
89
|
+
"""Emit node failed event."""
|
90
|
+
if self._event_stream:
|
91
|
+
try:
|
92
|
+
from kailash.middleware.events import EventType, NodeEvent
|
93
|
+
|
94
|
+
event = NodeEvent(
|
95
|
+
type=EventType.NODE_FAILED,
|
96
|
+
workflow_id=self._workflow_id,
|
97
|
+
node_id=getattr(self, "name", "unknown"),
|
98
|
+
node_type=self.__class__.__name__,
|
99
|
+
node_name=getattr(self, "name", "unknown"),
|
100
|
+
error=error,
|
101
|
+
session_id=self._session_id,
|
102
|
+
)
|
103
|
+
|
104
|
+
await self._event_stream.emit(event)
|
105
|
+
except Exception as e:
|
106
|
+
logger.warning(f"Failed to emit node failed event: {e}")
|
107
|
+
|
108
|
+
async def emit_node_progress(self, progress_percent: float, message: str = None):
|
109
|
+
"""Emit node progress event."""
|
110
|
+
if self._event_stream:
|
111
|
+
try:
|
112
|
+
from kailash.middleware.events import EventType, NodeEvent
|
113
|
+
|
114
|
+
event = NodeEvent(
|
115
|
+
type=EventType.NODE_PROGRESS,
|
116
|
+
workflow_id=self._workflow_id,
|
117
|
+
node_id=getattr(self, "name", "unknown"),
|
118
|
+
node_type=self.__class__.__name__,
|
119
|
+
node_name=getattr(self, "name", "unknown"),
|
120
|
+
metadata={"progress_percent": progress_percent, "message": message},
|
121
|
+
session_id=self._session_id,
|
122
|
+
)
|
123
|
+
|
124
|
+
await self._event_stream.emit(event)
|
125
|
+
except Exception as e:
|
126
|
+
logger.warning(f"Failed to emit node progress event: {e}")
|
127
|
+
|
128
|
+
def has_event_stream(self) -> bool:
|
129
|
+
"""Check if event stream is available."""
|
130
|
+
return self._event_stream is not None
|
131
|
+
|
132
|
+
|
133
|
+
class EventAwareNode:
|
134
|
+
"""
|
135
|
+
Enhanced node base class with built-in event emission.
|
136
|
+
|
137
|
+
This class can be used as a base class for nodes that want
|
138
|
+
automatic event emission during execution.
|
139
|
+
"""
|
140
|
+
|
141
|
+
def __init__(self, *args, **kwargs):
|
142
|
+
# Initialize the mixin first
|
143
|
+
if hasattr(super(), "__init__"):
|
144
|
+
super().__init__(*args, **kwargs)
|
145
|
+
|
146
|
+
# Add event emission capabilities
|
147
|
+
self._event_mixin = EventEmitterMixin()
|
148
|
+
|
149
|
+
def set_event_context(self, *args, **kwargs):
|
150
|
+
"""Delegate to mixin."""
|
151
|
+
self._event_mixin.set_event_context(*args, **kwargs)
|
152
|
+
|
153
|
+
async def emit_node_started(self, *args, **kwargs):
|
154
|
+
"""Delegate to mixin."""
|
155
|
+
await self._event_mixin.emit_node_started(*args, **kwargs)
|
156
|
+
|
157
|
+
async def emit_node_completed(self, *args, **kwargs):
|
158
|
+
"""Delegate to mixin."""
|
159
|
+
await self._event_mixin.emit_node_completed(*args, **kwargs)
|
160
|
+
|
161
|
+
async def emit_node_failed(self, *args, **kwargs):
|
162
|
+
"""Delegate to mixin."""
|
163
|
+
await self._event_mixin.emit_node_failed(*args, **kwargs)
|
164
|
+
|
165
|
+
async def emit_node_progress(self, *args, **kwargs):
|
166
|
+
"""Delegate to mixin."""
|
167
|
+
await self._event_mixin.emit_node_progress(*args, **kwargs)
|
168
|
+
|
169
|
+
def has_event_stream(self) -> bool:
|
170
|
+
"""Delegate to mixin."""
|
171
|
+
return self._event_mixin.has_event_stream()
|
172
|
+
|
173
|
+
|
174
|
+
def enable_events_for_node(node_instance, event_stream, **context):
|
175
|
+
"""
|
176
|
+
Enable events for a node instance.
|
177
|
+
|
178
|
+
This function can be used to add event capabilities to existing
|
179
|
+
node instances without requiring them to inherit from EventAwareNode.
|
180
|
+
|
181
|
+
Args:
|
182
|
+
node_instance: The node instance to enhance
|
183
|
+
event_stream: The event stream to use
|
184
|
+
**context: Additional context (session_id, workflow_id, etc.)
|
185
|
+
"""
|
186
|
+
# Add mixin to the instance
|
187
|
+
if not hasattr(node_instance, "_event_mixin"):
|
188
|
+
node_instance._event_mixin = EventEmitterMixin()
|
189
|
+
|
190
|
+
# Add methods to the instance
|
191
|
+
node_instance.set_event_context = node_instance._event_mixin.set_event_context
|
192
|
+
node_instance.emit_node_started = node_instance._event_mixin.emit_node_started
|
193
|
+
node_instance.emit_node_completed = (
|
194
|
+
node_instance._event_mixin.emit_node_completed
|
195
|
+
)
|
196
|
+
node_instance.emit_node_failed = node_instance._event_mixin.emit_node_failed
|
197
|
+
node_instance.emit_node_progress = node_instance._event_mixin.emit_node_progress
|
198
|
+
node_instance.has_event_stream = node_instance._event_mixin.has_event_stream
|
199
|
+
|
200
|
+
# Set the event context
|
201
|
+
node_instance.set_event_context(event_stream, **context)
|
kailash/nodes/mixins/mcp.py
CHANGED
@@ -5,9 +5,11 @@ allowing them to discover and use MCP tools without being an LLM agent.
|
|
5
5
|
"""
|
6
6
|
|
7
7
|
import asyncio
|
8
|
-
from typing import Any
|
8
|
+
from typing import TYPE_CHECKING, Any
|
9
9
|
|
10
|
-
|
10
|
+
# Avoid circular import
|
11
|
+
if TYPE_CHECKING:
|
12
|
+
from kailash.middleware.mcp import MiddlewareMCPClient as MCPClient
|
11
13
|
|
12
14
|
|
13
15
|
class MCPCapabilityMixin:
|
@@ -43,10 +45,13 @@ class MCPCapabilityMixin:
|
|
43
45
|
self._mcp_client = None
|
44
46
|
|
45
47
|
@property
|
46
|
-
def mcp_client(self)
|
48
|
+
def mcp_client(self):
|
47
49
|
"""Get or create MCP client instance."""
|
48
50
|
if self._mcp_client is None:
|
49
|
-
|
51
|
+
# Lazy import to avoid circular dependency
|
52
|
+
from kailash.middleware.mcp import MiddlewareMCPClient
|
53
|
+
|
54
|
+
self._mcp_client = MiddlewareMCPClient()
|
50
55
|
return self._mcp_client
|
51
56
|
|
52
57
|
async def discover_mcp_tools(
|
@@ -0,0 +1,165 @@
|
|
1
|
+
"""
|
2
|
+
Security-related mixins for Kailash nodes.
|
3
|
+
|
4
|
+
These mixins provide security, performance, and logging capabilities
|
5
|
+
that can be mixed into any node class.
|
6
|
+
"""
|
7
|
+
|
8
|
+
import logging
|
9
|
+
import time
|
10
|
+
from functools import wraps
|
11
|
+
from typing import Any, Dict, Optional
|
12
|
+
|
13
|
+
|
14
|
+
class SecurityMixin:
|
15
|
+
"""
|
16
|
+
Mixin that adds security features to nodes.
|
17
|
+
|
18
|
+
Provides:
|
19
|
+
- Security context management
|
20
|
+
- Audit logging capabilities
|
21
|
+
- Access control integration
|
22
|
+
"""
|
23
|
+
|
24
|
+
def __init__(self, *args, **kwargs):
|
25
|
+
super().__init__(*args, **kwargs)
|
26
|
+
self._security_context = {}
|
27
|
+
self._audit_enabled = kwargs.get("audit_enabled", True)
|
28
|
+
self._log_context = {} # Initialize log context for log_with_context method
|
29
|
+
|
30
|
+
def set_security_context(self, context: Dict[str, Any]) -> None:
|
31
|
+
"""Set security context for the node."""
|
32
|
+
self._security_context = context
|
33
|
+
|
34
|
+
def get_security_context(self) -> Dict[str, Any]:
|
35
|
+
"""Get current security context."""
|
36
|
+
return self._security_context
|
37
|
+
|
38
|
+
def audit_log(self, action: str, details: Dict[str, Any]) -> None:
|
39
|
+
"""Log an audit event."""
|
40
|
+
if self._audit_enabled:
|
41
|
+
# In a real implementation, this would integrate with AuditLogNode
|
42
|
+
print(f"[AUDIT] {action}: {details}")
|
43
|
+
|
44
|
+
def validate_and_sanitize_inputs(self, inputs: Dict[str, Any]) -> Dict[str, Any]:
|
45
|
+
"""Validate and sanitize input parameters."""
|
46
|
+
# Basic implementation - in production this would do more validation
|
47
|
+
sanitized = {}
|
48
|
+
for key, value in inputs.items():
|
49
|
+
if isinstance(value, str):
|
50
|
+
# Basic sanitization - remove leading/trailing whitespace
|
51
|
+
sanitized[key] = value.strip()
|
52
|
+
else:
|
53
|
+
sanitized[key] = value
|
54
|
+
return sanitized
|
55
|
+
|
56
|
+
|
57
|
+
class PerformanceMixin:
|
58
|
+
"""
|
59
|
+
Mixin that adds performance monitoring to nodes.
|
60
|
+
|
61
|
+
Provides:
|
62
|
+
- Execution time tracking
|
63
|
+
- Performance metrics collection
|
64
|
+
- SLA monitoring
|
65
|
+
"""
|
66
|
+
|
67
|
+
def __init__(self, *args, **kwargs):
|
68
|
+
super().__init__(*args, **kwargs)
|
69
|
+
self._performance_metrics = []
|
70
|
+
self._sla_target_ms = kwargs.get("sla_target_ms", 1000)
|
71
|
+
|
72
|
+
def track_performance(self, func):
|
73
|
+
"""Decorator to track method performance."""
|
74
|
+
|
75
|
+
@wraps(func)
|
76
|
+
def wrapper(*args, **kwargs):
|
77
|
+
start_time = time.time()
|
78
|
+
try:
|
79
|
+
result = func(*args, **kwargs)
|
80
|
+
execution_time = (time.time() - start_time) * 1000 # Convert to ms
|
81
|
+
self._performance_metrics.append(
|
82
|
+
{
|
83
|
+
"method": func.__name__,
|
84
|
+
"execution_time_ms": execution_time,
|
85
|
+
"sla_met": execution_time <= self._sla_target_ms,
|
86
|
+
}
|
87
|
+
)
|
88
|
+
return result
|
89
|
+
except Exception as e:
|
90
|
+
execution_time = (time.time() - start_time) * 1000
|
91
|
+
self._performance_metrics.append(
|
92
|
+
{
|
93
|
+
"method": func.__name__,
|
94
|
+
"execution_time_ms": execution_time,
|
95
|
+
"error": str(e),
|
96
|
+
}
|
97
|
+
)
|
98
|
+
raise
|
99
|
+
|
100
|
+
return wrapper
|
101
|
+
|
102
|
+
def get_performance_metrics(self) -> list:
|
103
|
+
"""Get collected performance metrics."""
|
104
|
+
return self._performance_metrics
|
105
|
+
|
106
|
+
|
107
|
+
class LoggingMixin:
|
108
|
+
"""
|
109
|
+
Mixin that adds enhanced logging capabilities to nodes.
|
110
|
+
|
111
|
+
Provides:
|
112
|
+
- Structured logging
|
113
|
+
- Context-aware logging
|
114
|
+
- Log level management
|
115
|
+
"""
|
116
|
+
|
117
|
+
def __init__(self, *args, **kwargs):
|
118
|
+
super().__init__(*args, **kwargs)
|
119
|
+
self.logger = logging.getLogger(self.__class__.__name__)
|
120
|
+
self._log_context = {}
|
121
|
+
|
122
|
+
def set_log_context(self, **context):
|
123
|
+
"""Set logging context."""
|
124
|
+
self._log_context.update(context)
|
125
|
+
|
126
|
+
def log_info(self, message: str, **extra):
|
127
|
+
"""Log info message with context."""
|
128
|
+
self.logger.info(message, extra={**self._log_context, **extra})
|
129
|
+
|
130
|
+
def log_error(self, message: str, error: Optional[Exception] = None, **extra):
|
131
|
+
"""Log error message with context."""
|
132
|
+
log_data = {**self._log_context, **extra}
|
133
|
+
if error:
|
134
|
+
log_data["error_type"] = type(error).__name__
|
135
|
+
log_data["error_message"] = str(error)
|
136
|
+
self.logger.error(message, extra=log_data)
|
137
|
+
|
138
|
+
def log_warning(self, message: str, **extra):
|
139
|
+
"""Log warning message with context."""
|
140
|
+
self.logger.warning(message, extra={**self._log_context, **extra})
|
141
|
+
|
142
|
+
def log_error_with_traceback(
|
143
|
+
self, error: Exception, operation: str = "unknown"
|
144
|
+
) -> None:
|
145
|
+
"""Log an error with full traceback information."""
|
146
|
+
import traceback
|
147
|
+
|
148
|
+
self.log_error(
|
149
|
+
f"Operation failed: {operation}",
|
150
|
+
error=error,
|
151
|
+
traceback=traceback.format_exc(),
|
152
|
+
)
|
153
|
+
|
154
|
+
def log_node_execution(self, operation: str, **context) -> None:
|
155
|
+
"""Log node execution information."""
|
156
|
+
self.log_info(f"Node operation: {operation}", **context)
|
157
|
+
|
158
|
+
def log_with_context(self, level: str, message: str, **context) -> None:
|
159
|
+
"""Log a message with additional context."""
|
160
|
+
full_context = {**self._log_context, **context}
|
161
|
+
context_str = " | ".join(f"{k}={v}" for k, v in full_context.items())
|
162
|
+
full_message = f"{message} | {context_str}"
|
163
|
+
|
164
|
+
log_func = getattr(self.logger, level.lower())
|
165
|
+
log_func(full_message)
|