provide-foundation 0.0.0.dev0__py3-none-any.whl → 0.0.0.dev1__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.
- provide/foundation/__init__.py +12 -20
- provide/foundation/archive/__init__.py +23 -0
- provide/foundation/archive/base.py +70 -0
- provide/foundation/archive/bzip2.py +157 -0
- provide/foundation/archive/gzip.py +159 -0
- provide/foundation/archive/operations.py +336 -0
- provide/foundation/archive/tar.py +164 -0
- provide/foundation/archive/zip.py +203 -0
- provide/foundation/config/base.py +2 -2
- provide/foundation/config/sync.py +19 -4
- provide/foundation/core.py +1 -2
- provide/foundation/crypto/__init__.py +2 -0
- provide/foundation/crypto/certificates/__init__.py +34 -0
- provide/foundation/crypto/certificates/base.py +173 -0
- provide/foundation/crypto/certificates/certificate.py +290 -0
- provide/foundation/crypto/certificates/factory.py +213 -0
- provide/foundation/crypto/certificates/generator.py +138 -0
- provide/foundation/crypto/certificates/loader.py +130 -0
- provide/foundation/crypto/certificates/operations.py +198 -0
- provide/foundation/crypto/certificates/trust.py +107 -0
- provide/foundation/eventsets/__init__.py +0 -0
- provide/foundation/eventsets/display.py +84 -0
- provide/foundation/eventsets/registry.py +160 -0
- provide/foundation/eventsets/resolver.py +192 -0
- provide/foundation/eventsets/sets/das.py +128 -0
- provide/foundation/eventsets/sets/database.py +125 -0
- provide/foundation/eventsets/sets/http.py +153 -0
- provide/foundation/eventsets/sets/llm.py +139 -0
- provide/foundation/eventsets/sets/task_queue.py +107 -0
- provide/foundation/eventsets/types.py +70 -0
- provide/foundation/hub/components.py +7 -133
- provide/foundation/logger/__init__.py +3 -10
- provide/foundation/logger/config/logging.py +6 -6
- provide/foundation/logger/core.py +0 -2
- provide/foundation/logger/custom_processors.py +1 -0
- provide/foundation/logger/factories.py +11 -2
- provide/foundation/logger/processors/main.py +20 -84
- provide/foundation/logger/setup/__init__.py +5 -1
- provide/foundation/logger/setup/coordinator.py +75 -23
- provide/foundation/logger/setup/processors.py +2 -9
- provide/foundation/logger/trace.py +27 -0
- provide/foundation/metrics/otel.py +10 -10
- provide/foundation/process/lifecycle.py +82 -26
- provide/foundation/testing/__init__.py +77 -0
- provide/foundation/testing/archive/__init__.py +24 -0
- provide/foundation/testing/archive/fixtures.py +217 -0
- provide/foundation/testing/common/__init__.py +34 -0
- provide/foundation/testing/common/fixtures.py +263 -0
- provide/foundation/testing/file/__init__.py +40 -0
- provide/foundation/testing/file/fixtures.py +523 -0
- provide/foundation/testing/logger.py +41 -11
- provide/foundation/testing/mocking/__init__.py +46 -0
- provide/foundation/testing/mocking/fixtures.py +331 -0
- provide/foundation/testing/process/__init__.py +48 -0
- provide/foundation/testing/process/fixtures.py +577 -0
- provide/foundation/testing/threading/__init__.py +38 -0
- provide/foundation/testing/threading/fixtures.py +520 -0
- provide/foundation/testing/time/__init__.py +32 -0
- provide/foundation/testing/time/fixtures.py +409 -0
- provide/foundation/testing/transport/__init__.py +30 -0
- provide/foundation/testing/transport/fixtures.py +280 -0
- provide/foundation/tools/__init__.py +58 -0
- provide/foundation/tools/base.py +348 -0
- provide/foundation/tools/cache.py +266 -0
- provide/foundation/tools/downloader.py +213 -0
- provide/foundation/tools/installer.py +254 -0
- provide/foundation/tools/registry.py +223 -0
- provide/foundation/tools/resolver.py +321 -0
- provide/foundation/tools/verifier.py +186 -0
- provide/foundation/tracer/otel.py +7 -11
- provide/foundation/transport/__init__.py +155 -0
- provide/foundation/transport/base.py +171 -0
- provide/foundation/transport/client.py +266 -0
- provide/foundation/transport/config.py +209 -0
- provide/foundation/transport/errors.py +79 -0
- provide/foundation/transport/http.py +232 -0
- provide/foundation/transport/middleware.py +366 -0
- provide/foundation/transport/registry.py +167 -0
- provide/foundation/transport/types.py +45 -0
- {provide_foundation-0.0.0.dev0.dist-info → provide_foundation-0.0.0.dev1.dist-info}/METADATA +5 -28
- {provide_foundation-0.0.0.dev0.dist-info → provide_foundation-0.0.0.dev1.dist-info}/RECORD +85 -34
- provide/foundation/cli/commands/logs/generate_old.py +0 -569
- provide/foundation/crypto/certificates.py +0 -896
- provide/foundation/logger/emoji/__init__.py +0 -44
- provide/foundation/logger/emoji/matrix.py +0 -209
- provide/foundation/logger/emoji/sets.py +0 -458
- provide/foundation/logger/emoji/types.py +0 -56
- provide/foundation/logger/setup/emoji_resolver.py +0 -64
- {provide_foundation-0.0.0.dev0.dist-info → provide_foundation-0.0.0.dev1.dist-info}/WHEEL +0 -0
- {provide_foundation-0.0.0.dev0.dist-info → provide_foundation-0.0.0.dev1.dist-info}/entry_points.txt +0 -0
- {provide_foundation-0.0.0.dev0.dist-info → provide_foundation-0.0.0.dev1.dist-info}/licenses/LICENSE +0 -0
- {provide_foundation-0.0.0.dev0.dist-info → provide_foundation-0.0.0.dev1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,107 @@
|
|
1
|
+
"""Certificate trust chain and verification utilities."""
|
2
|
+
|
3
|
+
try:
|
4
|
+
from cryptography import x509
|
5
|
+
from cryptography.hazmat.primitives.asymmetric import ec, rsa
|
6
|
+
|
7
|
+
_HAS_CRYPTO = True
|
8
|
+
except ImportError:
|
9
|
+
x509 = None
|
10
|
+
ec = None
|
11
|
+
rsa = None
|
12
|
+
_HAS_CRYPTO = False
|
13
|
+
|
14
|
+
from provide.foundation import logger
|
15
|
+
from provide.foundation.crypto.certificates.base import CertificateError
|
16
|
+
from provide.foundation.crypto.certificates.operations import validate_signature
|
17
|
+
|
18
|
+
|
19
|
+
def verify_trust(
|
20
|
+
cert: "Certificate",
|
21
|
+
other_cert: "Certificate",
|
22
|
+
trust_chain: list["Certificate"],
|
23
|
+
) -> bool:
|
24
|
+
"""
|
25
|
+
Verifies if the other_cert is trusted based on this certificate's trust chain.
|
26
|
+
|
27
|
+
Args:
|
28
|
+
cert: The certificate doing the verification
|
29
|
+
other_cert: The certificate to verify
|
30
|
+
trust_chain: List of trusted certificates
|
31
|
+
|
32
|
+
Returns:
|
33
|
+
True if the certificate is trusted, False otherwise
|
34
|
+
"""
|
35
|
+
if other_cert is None:
|
36
|
+
raise CertificateError("Cannot verify trust: other_cert is None")
|
37
|
+
|
38
|
+
logger.debug(
|
39
|
+
f"📜🔍🚀 Verifying trust for cert S/N {other_cert.serial_number} "
|
40
|
+
f"against chain of S/N {cert.serial_number}"
|
41
|
+
)
|
42
|
+
|
43
|
+
if not other_cert.is_valid:
|
44
|
+
logger.debug(
|
45
|
+
"📜🔍⚠️ Trust verification failed: Other certificate is not valid"
|
46
|
+
)
|
47
|
+
return False
|
48
|
+
if not other_cert.public_key:
|
49
|
+
raise CertificateError(
|
50
|
+
"Cannot verify trust: Other certificate has no public key"
|
51
|
+
)
|
52
|
+
|
53
|
+
if cert == other_cert:
|
54
|
+
logger.debug(
|
55
|
+
"📜🔍✅ Trust verified: Certificates are identical (based on subject/serial)"
|
56
|
+
)
|
57
|
+
return True
|
58
|
+
|
59
|
+
if other_cert in trust_chain:
|
60
|
+
logger.debug(
|
61
|
+
"📜🔍✅ Trust verified: Other certificate found in trust chain"
|
62
|
+
)
|
63
|
+
return True
|
64
|
+
|
65
|
+
for trusted_cert in trust_chain:
|
66
|
+
logger.debug(
|
67
|
+
f"📜🔍🔁 Checking signature against trusted cert S/N {trusted_cert.serial_number}"
|
68
|
+
)
|
69
|
+
if validate_signature_wrapper(
|
70
|
+
signed_cert=other_cert, signing_cert=trusted_cert
|
71
|
+
):
|
72
|
+
logger.debug(
|
73
|
+
f"📜🔍✅ Trust verified: Other cert signed by trusted cert S/N "
|
74
|
+
f"{trusted_cert.serial_number}"
|
75
|
+
)
|
76
|
+
return True
|
77
|
+
|
78
|
+
logger.debug(
|
79
|
+
"📜🔍❌ Trust verification failed: Other certificate not identical, "
|
80
|
+
"not in chain, and not signed by any cert in chain"
|
81
|
+
)
|
82
|
+
return False
|
83
|
+
|
84
|
+
|
85
|
+
def validate_signature_wrapper(
|
86
|
+
signed_cert: "Certificate",
|
87
|
+
signing_cert: "Certificate"
|
88
|
+
) -> bool:
|
89
|
+
"""
|
90
|
+
Internal helper: Validates signature and issuer/subject match.
|
91
|
+
|
92
|
+
Args:
|
93
|
+
signed_cert: The certificate that was signed
|
94
|
+
signing_cert: The certificate that did the signing
|
95
|
+
|
96
|
+
Returns:
|
97
|
+
True if signature is valid, False otherwise
|
98
|
+
"""
|
99
|
+
if not hasattr(signed_cert, "_cert") or not hasattr(signing_cert, "_cert"):
|
100
|
+
logger.error(
|
101
|
+
"📜🔍❌ Cannot validate signature: Certificate object(s) not initialized"
|
102
|
+
)
|
103
|
+
return False
|
104
|
+
|
105
|
+
return validate_signature(
|
106
|
+
signed_cert._cert, signing_cert._cert, signing_cert.public_key
|
107
|
+
)
|
File without changes
|
@@ -0,0 +1,84 @@
|
|
1
|
+
"""
|
2
|
+
Event set display utilities for Foundation.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from provide.foundation.logger import get_logger
|
6
|
+
|
7
|
+
from provide.foundation.eventsets.registry import get_registry, discover_event_sets
|
8
|
+
from provide.foundation.eventsets.resolver import get_resolver
|
9
|
+
|
10
|
+
logger = get_logger(__name__)
|
11
|
+
|
12
|
+
|
13
|
+
def show_event_matrix() -> None:
|
14
|
+
"""
|
15
|
+
Display the active event set configuration to the console.
|
16
|
+
Shows all registered event sets and their field mappings.
|
17
|
+
"""
|
18
|
+
# Ensure event sets are discovered
|
19
|
+
discover_event_sets()
|
20
|
+
|
21
|
+
registry = get_registry()
|
22
|
+
resolver = get_resolver()
|
23
|
+
|
24
|
+
# Force resolution to ensure everything is loaded
|
25
|
+
resolver.resolve()
|
26
|
+
|
27
|
+
lines: list[str] = ["Foundation Event Sets: Active Configuration"]
|
28
|
+
lines.append("=" * 70)
|
29
|
+
|
30
|
+
# Show registered event sets
|
31
|
+
event_sets = registry.list_event_sets()
|
32
|
+
if event_sets:
|
33
|
+
lines.append(f"\nRegistered Event Sets ({len(event_sets)}):")
|
34
|
+
for config in event_sets:
|
35
|
+
lines.append(f"\n {config.name} (priority: {config.priority})")
|
36
|
+
if config.description:
|
37
|
+
lines.append(f" {config.description}")
|
38
|
+
|
39
|
+
# Show field mappings
|
40
|
+
if config.field_mappings:
|
41
|
+
lines.append(f" Field Mappings ({len(config.field_mappings)}):")
|
42
|
+
for mapping in config.field_mappings[:5]: # Show first 5
|
43
|
+
lines.append(f" - {mapping.log_key}")
|
44
|
+
if len(config.field_mappings) > 5:
|
45
|
+
lines.append(f" ... and {len(config.field_mappings) - 5} more")
|
46
|
+
|
47
|
+
# Show event sets
|
48
|
+
if config.event_sets:
|
49
|
+
lines.append(f" Event Sets ({len(config.event_sets)}):")
|
50
|
+
for event_set in config.event_sets:
|
51
|
+
marker_count = len(event_set.visual_markers)
|
52
|
+
metadata_count = len(event_set.metadata_fields)
|
53
|
+
transform_count = len(event_set.transformations)
|
54
|
+
lines.append(
|
55
|
+
f" - {event_set.name}: "
|
56
|
+
f"{marker_count} markers, "
|
57
|
+
f"{metadata_count} metadata, "
|
58
|
+
f"{transform_count} transforms"
|
59
|
+
)
|
60
|
+
else:
|
61
|
+
lines.append("\n (No event sets registered)")
|
62
|
+
|
63
|
+
lines.append("\n" + "=" * 70)
|
64
|
+
|
65
|
+
# Show resolved state
|
66
|
+
if resolver._resolved:
|
67
|
+
lines.append("\nResolver State:")
|
68
|
+
lines.append(f" Total Field Mappings: {len(resolver._field_mappings)}")
|
69
|
+
lines.append(f" Total Event Sets: {len(resolver._event_sets)}")
|
70
|
+
|
71
|
+
# Show sample visual markers
|
72
|
+
if resolver._event_sets:
|
73
|
+
lines.append("\n Sample Visual Markers:")
|
74
|
+
for name, event_set in list(resolver._event_sets.items())[:3]:
|
75
|
+
if event_set.visual_markers:
|
76
|
+
sample_markers = list(event_set.visual_markers.items())[:3]
|
77
|
+
lines.append(f" {name}:")
|
78
|
+
for key, marker in sample_markers:
|
79
|
+
lines.append(f" {marker} -> {key}")
|
80
|
+
else:
|
81
|
+
lines.append("\n (Resolver not yet initialized)")
|
82
|
+
|
83
|
+
# Log the complete display
|
84
|
+
logger.info("\n".join(lines))
|
@@ -0,0 +1,160 @@
|
|
1
|
+
"""
|
2
|
+
Event set registry and discovery.
|
3
|
+
"""
|
4
|
+
|
5
|
+
import importlib
|
6
|
+
import pkgutil
|
7
|
+
from pathlib import Path
|
8
|
+
|
9
|
+
from provide.foundation.errors.resources import AlreadyExistsError, NotFoundError
|
10
|
+
from provide.foundation.hub.registry import Registry
|
11
|
+
from provide.foundation.logger.setup.coordinator import create_foundation_internal_logger
|
12
|
+
|
13
|
+
from provide.foundation.eventsets.types import EventSet
|
14
|
+
|
15
|
+
# Bootstrap logger that doesn't trigger full logger setup
|
16
|
+
logger = create_foundation_internal_logger()
|
17
|
+
|
18
|
+
|
19
|
+
class EventSetRegistry(Registry):
|
20
|
+
"""
|
21
|
+
Registry for event set definitions using foundation Registry.
|
22
|
+
|
23
|
+
Extends the foundation Registry to provide specialized
|
24
|
+
methods for event set registration and discovery.
|
25
|
+
"""
|
26
|
+
|
27
|
+
def register_event_set(self, event_set: EventSet) -> None:
|
28
|
+
"""
|
29
|
+
Register an event set definition.
|
30
|
+
|
31
|
+
Args:
|
32
|
+
event_set: The EventSet to register
|
33
|
+
|
34
|
+
Raises:
|
35
|
+
AlreadyExistsError: If an event set with this name already exists
|
36
|
+
"""
|
37
|
+
try:
|
38
|
+
self.register(event_set.name, event_set, "eventset", metadata={"priority": event_set.priority})
|
39
|
+
logger.debug(
|
40
|
+
"Registered event set",
|
41
|
+
name=event_set.name,
|
42
|
+
priority=event_set.priority,
|
43
|
+
field_count=len(event_set.field_mappings),
|
44
|
+
mapping_count=len(event_set.mappings)
|
45
|
+
)
|
46
|
+
except AlreadyExistsError:
|
47
|
+
logger.warning("Event set already registered", name=event_set.name)
|
48
|
+
raise
|
49
|
+
|
50
|
+
def get_event_set(self, name: str) -> EventSet:
|
51
|
+
"""
|
52
|
+
Retrieve an event set by name.
|
53
|
+
|
54
|
+
Args:
|
55
|
+
name: The name of the event set
|
56
|
+
|
57
|
+
Returns:
|
58
|
+
The EventSet
|
59
|
+
|
60
|
+
Raises:
|
61
|
+
NotFoundError: If no event set with this name exists
|
62
|
+
"""
|
63
|
+
event_set = self.get(name, "eventset")
|
64
|
+
if event_set is None:
|
65
|
+
raise NotFoundError(f"Event set '{name}' not found")
|
66
|
+
return event_set
|
67
|
+
|
68
|
+
def list_event_sets(self) -> list[EventSet]:
|
69
|
+
"""
|
70
|
+
List all registered event sets sorted by priority.
|
71
|
+
|
72
|
+
Returns:
|
73
|
+
List of EventSet objects sorted by descending priority
|
74
|
+
"""
|
75
|
+
names = self.list_dimension("eventset")
|
76
|
+
entries = [self.get_entry(name, "eventset") for name in names]
|
77
|
+
entries = [entry for entry in entries if entry is not None]
|
78
|
+
entries.sort(key=lambda e: e.metadata.get("priority", 0), reverse=True)
|
79
|
+
return [entry.value for entry in entries]
|
80
|
+
|
81
|
+
def discover_sets(self) -> None:
|
82
|
+
"""
|
83
|
+
Auto-discover and register event sets from the sets/ directory.
|
84
|
+
|
85
|
+
Imports all modules in the sets/ subdirectory and registers
|
86
|
+
any EVENT_SET constants found.
|
87
|
+
"""
|
88
|
+
sets_path = Path(__file__).parent / "sets"
|
89
|
+
if not sets_path.exists():
|
90
|
+
logger.debug("No sets directory found for auto-discovery")
|
91
|
+
return
|
92
|
+
|
93
|
+
for module_info in pkgutil.iter_modules([str(sets_path)]):
|
94
|
+
if module_info.ispkg:
|
95
|
+
continue
|
96
|
+
|
97
|
+
module_name = f"provide.foundation.eventsets.sets.{module_info.name}"
|
98
|
+
try:
|
99
|
+
module = importlib.import_module(module_name)
|
100
|
+
|
101
|
+
if hasattr(module, "EVENT_SET"):
|
102
|
+
event_set = getattr(module, "EVENT_SET")
|
103
|
+
if isinstance(event_set, EventSet):
|
104
|
+
try:
|
105
|
+
self.register_event_set(event_set)
|
106
|
+
logger.debug(
|
107
|
+
"Auto-discovered event set",
|
108
|
+
module=module_name,
|
109
|
+
name=event_set.name
|
110
|
+
)
|
111
|
+
except AlreadyExistsError:
|
112
|
+
logger.debug(
|
113
|
+
"Event set already registered during discovery",
|
114
|
+
module=module_name,
|
115
|
+
name=event_set.name
|
116
|
+
)
|
117
|
+
else:
|
118
|
+
logger.warning(
|
119
|
+
"EVENT_SET is not an EventSet",
|
120
|
+
module=module_name,
|
121
|
+
type=type(event_set).__name__
|
122
|
+
)
|
123
|
+
|
124
|
+
except ImportError as e:
|
125
|
+
logger.debug(
|
126
|
+
"Failed to import event set module",
|
127
|
+
module=module_name,
|
128
|
+
error=str(e)
|
129
|
+
)
|
130
|
+
except Exception as e:
|
131
|
+
logger.warning(
|
132
|
+
"Error during event set discovery",
|
133
|
+
module=module_name,
|
134
|
+
error=str(e),
|
135
|
+
error_type=type(e).__name__
|
136
|
+
)
|
137
|
+
|
138
|
+
|
139
|
+
# Global registry instance
|
140
|
+
_registry = EventSetRegistry()
|
141
|
+
|
142
|
+
|
143
|
+
def get_registry() -> EventSetRegistry:
|
144
|
+
"""Get the global event set registry instance."""
|
145
|
+
return _registry
|
146
|
+
|
147
|
+
|
148
|
+
def register_event_set(event_set: EventSet) -> None:
|
149
|
+
"""
|
150
|
+
Register an event set in the global registry.
|
151
|
+
|
152
|
+
Args:
|
153
|
+
event_set: The EventSet to register
|
154
|
+
"""
|
155
|
+
_registry.register_event_set(event_set)
|
156
|
+
|
157
|
+
|
158
|
+
def discover_event_sets() -> None:
|
159
|
+
"""Auto-discover and register all event sets."""
|
160
|
+
_registry.discover_sets()
|
@@ -0,0 +1,192 @@
|
|
1
|
+
"""
|
2
|
+
Event set resolution and enrichment logic.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from typing import Any
|
6
|
+
|
7
|
+
from provide.foundation.eventsets.registry import get_registry
|
8
|
+
from provide.foundation.eventsets.types import EventMapping, EventSet, FieldMapping
|
9
|
+
|
10
|
+
|
11
|
+
class EventSetResolver:
|
12
|
+
"""
|
13
|
+
Resolves and applies event set enrichments to log events.
|
14
|
+
"""
|
15
|
+
|
16
|
+
def __init__(self) -> None:
|
17
|
+
"""Initialize the resolver with cached configurations."""
|
18
|
+
self._field_mappings: list[FieldMapping] = []
|
19
|
+
self._event_mappings_by_set: dict[str, list[EventMapping]] = {}
|
20
|
+
self._resolved = False
|
21
|
+
|
22
|
+
def resolve(self) -> None:
|
23
|
+
"""
|
24
|
+
Resolve all registered event sets into a unified configuration.
|
25
|
+
|
26
|
+
This merges all registered event sets by priority, building
|
27
|
+
the field mapping and event mapping lookup tables.
|
28
|
+
"""
|
29
|
+
registry = get_registry()
|
30
|
+
event_sets = registry.list_event_sets() # Already sorted by priority
|
31
|
+
|
32
|
+
# Clear existing state
|
33
|
+
self._field_mappings.clear()
|
34
|
+
self._event_mappings_by_set.clear()
|
35
|
+
|
36
|
+
# Process each event set in priority order
|
37
|
+
for event_set in event_sets:
|
38
|
+
# Store event mappings by event set name
|
39
|
+
self._event_mappings_by_set[event_set.name] = event_set.mappings
|
40
|
+
|
41
|
+
# Add field mappings
|
42
|
+
self._field_mappings.extend(event_set.field_mappings)
|
43
|
+
|
44
|
+
self._resolved = True
|
45
|
+
|
46
|
+
def enrich_event(self, event_dict: dict[str, Any]) -> dict[str, Any]:
|
47
|
+
"""
|
48
|
+
Enrich a log event with event set data.
|
49
|
+
|
50
|
+
Args:
|
51
|
+
event_dict: The event dictionary to enrich
|
52
|
+
|
53
|
+
Returns:
|
54
|
+
The enriched event dictionary
|
55
|
+
"""
|
56
|
+
if not self._resolved:
|
57
|
+
self.resolve()
|
58
|
+
|
59
|
+
enrichments = []
|
60
|
+
|
61
|
+
# Process each field in the event
|
62
|
+
for field_key, field_value in list(event_dict.items()):
|
63
|
+
if field_key == "event" or field_value is None:
|
64
|
+
continue
|
65
|
+
|
66
|
+
# Find appropriate event mapping for this field
|
67
|
+
event_mapping = self._find_event_mapping_for_field(field_key, field_value)
|
68
|
+
if not event_mapping:
|
69
|
+
continue
|
70
|
+
|
71
|
+
value_str = str(field_value).lower()
|
72
|
+
|
73
|
+
# Apply transformations
|
74
|
+
if value_str in event_mapping.transformations:
|
75
|
+
field_value = event_mapping.transformations[value_str](field_value)
|
76
|
+
value_str = str(field_value).lower()
|
77
|
+
|
78
|
+
# Get visual marker
|
79
|
+
visual_marker = event_mapping.visual_markers.get(
|
80
|
+
value_str,
|
81
|
+
event_mapping.visual_markers.get(event_mapping.default_key, "")
|
82
|
+
)
|
83
|
+
|
84
|
+
if visual_marker:
|
85
|
+
enrichments.append(visual_marker)
|
86
|
+
|
87
|
+
# Apply metadata fields
|
88
|
+
if value_str in event_mapping.metadata_fields:
|
89
|
+
for meta_key, meta_value in event_mapping.metadata_fields[value_str].items():
|
90
|
+
if meta_key not in event_dict:
|
91
|
+
event_dict[meta_key] = meta_value
|
92
|
+
|
93
|
+
# Add visual enrichments to event message
|
94
|
+
if enrichments:
|
95
|
+
prefix = "".join(f"[{e}]" for e in enrichments)
|
96
|
+
event_msg = event_dict.get("event", "")
|
97
|
+
event_dict["event"] = f"{prefix} {event_msg}" if event_msg else prefix
|
98
|
+
|
99
|
+
return event_dict
|
100
|
+
|
101
|
+
def _find_event_mapping_for_field(self, field_key: str, field_value: Any) -> EventMapping | None:
|
102
|
+
"""
|
103
|
+
Find the appropriate EventMapping for a given field.
|
104
|
+
|
105
|
+
This method uses a heuristic approach to match field keys to EventMappings:
|
106
|
+
1. Direct field name mapping (e.g., "domain" -> "domain" mapping)
|
107
|
+
2. Field prefix mapping (e.g., "http.method" -> "http_method" mapping)
|
108
|
+
3. Field pattern matching
|
109
|
+
"""
|
110
|
+
# First check for direct field name matches
|
111
|
+
simple_key = field_key.split('.')[-1] # Get last part of dotted key
|
112
|
+
|
113
|
+
for event_set_name, mappings in self._event_mappings_by_set.items():
|
114
|
+
for mapping in mappings:
|
115
|
+
# Direct name match
|
116
|
+
if mapping.name == simple_key or mapping.name == field_key:
|
117
|
+
return mapping
|
118
|
+
|
119
|
+
# Pattern matching for common cases
|
120
|
+
if field_key.startswith("http.") and mapping.name.startswith("http_"):
|
121
|
+
if field_key.replace(".", "_") == mapping.name:
|
122
|
+
return mapping
|
123
|
+
|
124
|
+
if field_key.startswith("llm.") and mapping.name.startswith("llm_"):
|
125
|
+
if field_key.replace(".", "_") == mapping.name:
|
126
|
+
return mapping
|
127
|
+
|
128
|
+
if field_key.startswith("db.") and mapping.name.startswith("db_"):
|
129
|
+
if field_key.replace(".", "_") == mapping.name:
|
130
|
+
return mapping
|
131
|
+
|
132
|
+
if field_key.startswith("task.") and mapping.name.startswith("task_"):
|
133
|
+
if field_key.replace(".", "_") == mapping.name:
|
134
|
+
return mapping
|
135
|
+
|
136
|
+
return None
|
137
|
+
|
138
|
+
def get_visual_markers(self, event_dict: dict[str, Any]) -> list[str]:
|
139
|
+
"""
|
140
|
+
Extract visual markers for an event without modifying it.
|
141
|
+
|
142
|
+
Args:
|
143
|
+
event_dict: The event dictionary to analyze
|
144
|
+
|
145
|
+
Returns:
|
146
|
+
List of visual markers that would be applied
|
147
|
+
"""
|
148
|
+
if not self._resolved:
|
149
|
+
self.resolve()
|
150
|
+
|
151
|
+
markers = []
|
152
|
+
|
153
|
+
for field_key, field_value in event_dict.items():
|
154
|
+
if field_key == "event" or field_value is None:
|
155
|
+
continue
|
156
|
+
|
157
|
+
event_mapping = self._find_event_mapping_for_field(field_key, field_value)
|
158
|
+
if not event_mapping:
|
159
|
+
continue
|
160
|
+
|
161
|
+
value_str = str(field_value).lower()
|
162
|
+
marker = event_mapping.visual_markers.get(
|
163
|
+
value_str,
|
164
|
+
event_mapping.visual_markers.get(event_mapping.default_key, "")
|
165
|
+
)
|
166
|
+
|
167
|
+
if marker:
|
168
|
+
markers.append(marker)
|
169
|
+
|
170
|
+
return markers
|
171
|
+
|
172
|
+
|
173
|
+
# Global resolver instance
|
174
|
+
_resolver = EventSetResolver()
|
175
|
+
|
176
|
+
|
177
|
+
def get_resolver() -> EventSetResolver:
|
178
|
+
"""Get the global event set resolver instance."""
|
179
|
+
return _resolver
|
180
|
+
|
181
|
+
|
182
|
+
def enrich_event(event_dict: dict[str, Any]) -> dict[str, Any]:
|
183
|
+
"""
|
184
|
+
Enrich a log event with event set data.
|
185
|
+
|
186
|
+
Args:
|
187
|
+
event_dict: The event dictionary to enrich
|
188
|
+
|
189
|
+
Returns:
|
190
|
+
The enriched event dictionary
|
191
|
+
"""
|
192
|
+
return _resolver.enrich_event(event_dict)
|
@@ -0,0 +1,128 @@
|
|
1
|
+
"""
|
2
|
+
Domain-Action-Status (DAS) event set.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from provide.foundation.eventsets.types import EventMapping, EventSet, FieldMapping
|
6
|
+
|
7
|
+
EVENT_SET = EventSet(
|
8
|
+
name="default",
|
9
|
+
description="Core Domain-Action-Status event enrichment",
|
10
|
+
mappings=[
|
11
|
+
EventMapping(
|
12
|
+
name="domain",
|
13
|
+
visual_markers={
|
14
|
+
"system": "⚙️",
|
15
|
+
"server": "🛎️",
|
16
|
+
"client": "🙋",
|
17
|
+
"network": "🌐",
|
18
|
+
"security": "🔐",
|
19
|
+
"config": "🔩",
|
20
|
+
"database": "🗄️",
|
21
|
+
"cache": "💾",
|
22
|
+
"task": "🔄",
|
23
|
+
"plugin": "🔌",
|
24
|
+
"telemetry": "🛰️",
|
25
|
+
"di": "💉",
|
26
|
+
"protocol": "📡",
|
27
|
+
"file": "📄",
|
28
|
+
"user": "👤",
|
29
|
+
"test": "🧪",
|
30
|
+
"utils": "🧰",
|
31
|
+
"core": "🌟",
|
32
|
+
"auth": "🔑",
|
33
|
+
"entity": "🦎",
|
34
|
+
"report": "📈",
|
35
|
+
"payment": "💳",
|
36
|
+
"default": "❓",
|
37
|
+
},
|
38
|
+
default_key="default"
|
39
|
+
),
|
40
|
+
EventMapping(
|
41
|
+
name="action",
|
42
|
+
visual_markers={
|
43
|
+
"init": "🌱",
|
44
|
+
"start": "🚀",
|
45
|
+
"stop": "🛑",
|
46
|
+
"connect": "🔗",
|
47
|
+
"disconnect": "💔",
|
48
|
+
"listen": "👂",
|
49
|
+
"send": "📤",
|
50
|
+
"receive": "📥",
|
51
|
+
"read": "📖",
|
52
|
+
"write": "📝",
|
53
|
+
"process": "⚙️",
|
54
|
+
"validate": "🛡️",
|
55
|
+
"execute": "▶️",
|
56
|
+
"query": "🔍",
|
57
|
+
"update": "🔄",
|
58
|
+
"delete": "🗑️",
|
59
|
+
"login": "➡️",
|
60
|
+
"logout": "⬅️",
|
61
|
+
"auth": "🔑",
|
62
|
+
"error": "🔥",
|
63
|
+
"encrypt": "🛡️",
|
64
|
+
"decrypt": "🔓",
|
65
|
+
"parse": "🧩",
|
66
|
+
"transmit": "📡",
|
67
|
+
"build": "🏗️",
|
68
|
+
"schedule": "📅",
|
69
|
+
"emit": "📢",
|
70
|
+
"load": "💡",
|
71
|
+
"observe": "🧐",
|
72
|
+
"request": "🗣️",
|
73
|
+
"interrupt": "🚦",
|
74
|
+
"register": "⚙️",
|
75
|
+
"default": "❓",
|
76
|
+
},
|
77
|
+
default_key="default"
|
78
|
+
),
|
79
|
+
EventMapping(
|
80
|
+
name="status",
|
81
|
+
visual_markers={
|
82
|
+
"success": "✅",
|
83
|
+
"failure": "❌",
|
84
|
+
"error": "🔥",
|
85
|
+
"warning": "⚠️",
|
86
|
+
"info": "ℹ️",
|
87
|
+
"debug": "🐞",
|
88
|
+
"trace": "👣",
|
89
|
+
"attempt": "⏳",
|
90
|
+
"retry": "🔁",
|
91
|
+
"skip": "⏭️",
|
92
|
+
"complete": "🏁",
|
93
|
+
"timeout": "⏱️",
|
94
|
+
"notfound": "❓",
|
95
|
+
"unauthorized": "🚫",
|
96
|
+
"invalid": "💢",
|
97
|
+
"cached": "🎯",
|
98
|
+
"ongoing": "🏃",
|
99
|
+
"idle": "💤",
|
100
|
+
"ready": "👍",
|
101
|
+
"default": "➡️",
|
102
|
+
},
|
103
|
+
default_key="default"
|
104
|
+
),
|
105
|
+
],
|
106
|
+
field_mappings=[
|
107
|
+
FieldMapping(
|
108
|
+
log_key="domain",
|
109
|
+
event_set_name="default",
|
110
|
+
description="System domain or component"
|
111
|
+
),
|
112
|
+
FieldMapping(
|
113
|
+
log_key="action",
|
114
|
+
event_set_name="default",
|
115
|
+
description="Action being performed"
|
116
|
+
),
|
117
|
+
FieldMapping(
|
118
|
+
log_key="status",
|
119
|
+
event_set_name="default",
|
120
|
+
description="Status or outcome of the action"
|
121
|
+
),
|
122
|
+
],
|
123
|
+
priority=0
|
124
|
+
)
|
125
|
+
|
126
|
+
# Alias for backward compatibility
|
127
|
+
das_event_set = EVENT_SET
|
128
|
+
default_event_set = EVENT_SET
|