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
@@ -16,7 +16,7 @@ from attrs import define, field
|
|
16
16
|
|
17
17
|
from provide.foundation.hub.registry import Registry, RegistryEntry
|
18
18
|
from provide.foundation.logger import get_logger
|
19
|
-
from provide.foundation.
|
19
|
+
from provide.foundation.eventsets.types import EventMapping
|
20
20
|
|
21
21
|
log = get_logger(__name__)
|
22
22
|
|
@@ -40,12 +40,16 @@ class ComponentInfo:
|
|
40
40
|
class ComponentCategory(Enum):
|
41
41
|
"""Predefined component categories for Foundation."""
|
42
42
|
|
43
|
-
EMOJI_SET = "emoji_set"
|
44
43
|
CONFIG_SOURCE = "config_source"
|
45
44
|
PROCESSOR = "processor"
|
46
45
|
ERROR_HANDLER = "error_handler"
|
47
46
|
FORMATTER = "formatter"
|
48
47
|
FILTER = "filter"
|
48
|
+
TRANSPORT = "transport"
|
49
|
+
TRANSPORT_MIDDLEWARE = "transport.middleware"
|
50
|
+
TRANSPORT_AUTH = "transport.auth"
|
51
|
+
TRANSPORT_CACHE = "transport.cache"
|
52
|
+
EVENT_SET = "eventset"
|
49
53
|
|
50
54
|
|
51
55
|
class ComponentLifecycle(Protocol):
|
@@ -71,119 +75,6 @@ def get_component_registry() -> Registry:
|
|
71
75
|
return _component_registry
|
72
76
|
|
73
77
|
|
74
|
-
def find_emoji_set_for_domain(domain: str) -> EmojiSet | None:
|
75
|
-
"""Find the best emoji set for a given domain."""
|
76
|
-
with _registry_lock:
|
77
|
-
registry = get_component_registry()
|
78
|
-
|
79
|
-
# Get all emoji sets for this domain
|
80
|
-
all_entries = list(registry)
|
81
|
-
domain_sets = [
|
82
|
-
entry
|
83
|
-
for entry in all_entries
|
84
|
-
if (
|
85
|
-
entry.dimension == ComponentCategory.EMOJI_SET.value
|
86
|
-
and entry.metadata.get("domain") == domain
|
87
|
-
)
|
88
|
-
]
|
89
|
-
|
90
|
-
if not domain_sets:
|
91
|
-
return get_default_emoji_set()
|
92
|
-
|
93
|
-
# Sort by priority (highest first)
|
94
|
-
domain_sets.sort(key=lambda e: e.metadata.get("priority", 0), reverse=True)
|
95
|
-
return domain_sets[0].value
|
96
|
-
|
97
|
-
|
98
|
-
def get_default_emoji_set() -> EmojiSet:
|
99
|
-
"""Get the default emoji set for unknown domains."""
|
100
|
-
with _registry_lock:
|
101
|
-
registry = get_component_registry()
|
102
|
-
|
103
|
-
# Look for default emoji set
|
104
|
-
all_entries = list(registry)
|
105
|
-
default_sets = [
|
106
|
-
entry
|
107
|
-
for entry in all_entries
|
108
|
-
if (
|
109
|
-
entry.dimension == ComponentCategory.EMOJI_SET.value
|
110
|
-
and entry.metadata.get("default", False)
|
111
|
-
)
|
112
|
-
]
|
113
|
-
|
114
|
-
if default_sets:
|
115
|
-
return default_sets[0].value
|
116
|
-
|
117
|
-
# Return built-in default
|
118
|
-
return EmojiSet(
|
119
|
-
name="default",
|
120
|
-
emojis={
|
121
|
-
"success": "✅",
|
122
|
-
"error": "❌",
|
123
|
-
"info": "ℹ️",
|
124
|
-
"warning": "⚠️",
|
125
|
-
"debug": "🔍",
|
126
|
-
},
|
127
|
-
)
|
128
|
-
|
129
|
-
|
130
|
-
def resolve_emoji_for_domain(domain: str, action: str) -> str:
|
131
|
-
"""Resolve emoji for domain and action with priority ordering."""
|
132
|
-
with _registry_lock:
|
133
|
-
registry = get_component_registry()
|
134
|
-
|
135
|
-
# Get all emoji sets for this domain
|
136
|
-
all_entries = list(registry)
|
137
|
-
domain_sets = [
|
138
|
-
entry
|
139
|
-
for entry in all_entries
|
140
|
-
if (
|
141
|
-
entry.dimension == ComponentCategory.EMOJI_SET.value
|
142
|
-
and entry.metadata.get("domain") == domain
|
143
|
-
)
|
144
|
-
]
|
145
|
-
|
146
|
-
# Sort by priority (highest first)
|
147
|
-
domain_sets.sort(key=lambda e: e.metadata.get("priority", 0), reverse=True)
|
148
|
-
|
149
|
-
# Try each set in priority order
|
150
|
-
for entry in domain_sets:
|
151
|
-
emoji_set = entry.value
|
152
|
-
if action in emoji_set.emojis:
|
153
|
-
return emoji_set.emojis[action]
|
154
|
-
|
155
|
-
# Fall back to default
|
156
|
-
default_set = get_default_emoji_set()
|
157
|
-
return default_set.emojis.get(action, "📝")
|
158
|
-
|
159
|
-
|
160
|
-
def get_composed_emoji_set(domain: str) -> EmojiSet:
|
161
|
-
"""Get composed emoji set combining all sets for a domain."""
|
162
|
-
with _registry_lock:
|
163
|
-
registry = get_component_registry()
|
164
|
-
|
165
|
-
# Get all emoji sets for this domain
|
166
|
-
all_entries = list(registry)
|
167
|
-
domain_sets = [
|
168
|
-
entry
|
169
|
-
for entry in all_entries
|
170
|
-
if (
|
171
|
-
entry.dimension == ComponentCategory.EMOJI_SET.value
|
172
|
-
and entry.metadata.get("domain") == domain
|
173
|
-
)
|
174
|
-
]
|
175
|
-
|
176
|
-
# Sort by priority (lowest first for composition)
|
177
|
-
domain_sets.sort(key=lambda e: e.metadata.get("priority", 0))
|
178
|
-
|
179
|
-
# Compose emojis
|
180
|
-
composed_emojis = {}
|
181
|
-
for entry in domain_sets:
|
182
|
-
emoji_set = entry.value
|
183
|
-
composed_emojis.update(emoji_set.emojis)
|
184
|
-
|
185
|
-
return EmojiSet(name=f"composed_{domain}", emojis=composed_emojis)
|
186
|
-
|
187
78
|
|
188
79
|
def resolve_config_value(key: str) -> Any:
|
189
80
|
"""Resolve configuration value using priority-ordered sources."""
|
@@ -532,24 +423,7 @@ def bootstrap_foundation() -> None:
|
|
532
423
|
"""Bootstrap Foundation with core registry components."""
|
533
424
|
registry = get_component_registry()
|
534
425
|
|
535
|
-
#
|
536
|
-
default_emoji_set = EmojiSet(
|
537
|
-
name="foundation_default",
|
538
|
-
emojis={
|
539
|
-
"success": "✅",
|
540
|
-
"error": "❌",
|
541
|
-
"info": "ℹ️",
|
542
|
-
"warning": "⚠️",
|
543
|
-
"debug": "🔍",
|
544
|
-
},
|
545
|
-
)
|
546
|
-
|
547
|
-
registry.register(
|
548
|
-
name="default",
|
549
|
-
value=default_emoji_set,
|
550
|
-
dimension=ComponentCategory.EMOJI_SET.value,
|
551
|
-
metadata={"default": True, "priority": 1},
|
552
|
-
)
|
426
|
+
# Event sets are now managed by the eventsets module
|
553
427
|
|
554
428
|
# Config sources would be registered here when implemented
|
555
429
|
|
@@ -6,6 +6,9 @@ Foundation Telemetry Logger Sub-package.
|
|
6
6
|
Re-exports key components related to logging functionality.
|
7
7
|
"""
|
8
8
|
|
9
|
+
# Import trace module early to ensure PrintLogger gets patched
|
10
|
+
from provide.foundation.logger import trace # noqa: F401
|
11
|
+
|
9
12
|
from provide.foundation.logger.base import (
|
10
13
|
FoundationLogger, # Class definition
|
11
14
|
get_logger, # Factory function
|
@@ -17,17 +20,8 @@ from provide.foundation.logger.config import (
|
|
17
20
|
LoggingConfig,
|
18
21
|
TelemetryConfig,
|
19
22
|
)
|
20
|
-
from provide.foundation.logger.emoji.matrix import (
|
21
|
-
PRIMARY_EMOJI, # Core domain emojis
|
22
|
-
SECONDARY_EMOJI, # Core action emojis
|
23
|
-
TERTIARY_EMOJI, # Core status emojis
|
24
|
-
show_emoji_matrix, # Utility to display emoji configurations
|
25
|
-
)
|
26
23
|
|
27
24
|
__all__ = [
|
28
|
-
"PRIMARY_EMOJI",
|
29
|
-
"SECONDARY_EMOJI",
|
30
|
-
"TERTIARY_EMOJI",
|
31
25
|
"FoundationLogger",
|
32
26
|
"LoggingConfig",
|
33
27
|
"TelemetryConfig",
|
@@ -35,7 +29,6 @@ __all__ = [
|
|
35
29
|
"logger",
|
36
30
|
"setup_logger", # Consistent naming
|
37
31
|
"setup_logging", # Backward compatibility
|
38
|
-
"show_emoji_matrix",
|
39
32
|
]
|
40
33
|
|
41
34
|
# 🐍📝
|
@@ -14,7 +14,7 @@ from attrs import define
|
|
14
14
|
from provide.foundation.config import BaseConfig, field
|
15
15
|
from provide.foundation.config.types import ConfigSource
|
16
16
|
from provide.foundation.logger.config.base import get_config_logger
|
17
|
-
from provide.foundation.
|
17
|
+
from provide.foundation.eventsets.types import EventMapping, EventSet
|
18
18
|
from provide.foundation.types import (
|
19
19
|
_VALID_FORMATTER_TUPLE,
|
20
20
|
_VALID_LOG_LEVEL_TUPLE,
|
@@ -28,7 +28,7 @@ class LoggingConfig(BaseConfig):
|
|
28
28
|
"""Configuration specific to logging behavior within Foundation Telemetry."""
|
29
29
|
|
30
30
|
default_level: LogLevelStr = field(
|
31
|
-
default="
|
31
|
+
default="WARNING",
|
32
32
|
env_var="PROVIDE_LOG_LEVEL",
|
33
33
|
description="Default logging level",
|
34
34
|
)
|
@@ -62,12 +62,12 @@ class LoggingConfig(BaseConfig):
|
|
62
62
|
env_var="PROVIDE_LOG_ENABLED_EMOJI_SETS",
|
63
63
|
description="Comma-separated list of emoji sets to enable",
|
64
64
|
)
|
65
|
-
custom_emoji_sets: list[
|
65
|
+
custom_emoji_sets: list[EventSet] = field(
|
66
66
|
factory=lambda: [],
|
67
67
|
env_var="PROVIDE_LOG_CUSTOM_EMOJI_SETS",
|
68
68
|
description="JSON array of custom emoji set configurations",
|
69
69
|
)
|
70
|
-
user_defined_emoji_sets: list[
|
70
|
+
user_defined_emoji_sets: list[EventMapping] = field(
|
71
71
|
factory=lambda: [],
|
72
72
|
env_var="PROVIDE_LOG_USER_DEFINED_EMOJI_SETS",
|
73
73
|
description="JSON array of user-defined emoji sets",
|
@@ -342,7 +342,7 @@ class LoggingConfig(BaseConfig):
|
|
342
342
|
parsed = json.loads(custom_sets)
|
343
343
|
if isinstance(parsed, list):
|
344
344
|
config_dict["custom_emoji_sets"] = [
|
345
|
-
|
345
|
+
EventSet(**item) if isinstance(item, dict) else item
|
346
346
|
for item in parsed
|
347
347
|
]
|
348
348
|
except json.JSONDecodeError as e:
|
@@ -369,7 +369,7 @@ class LoggingConfig(BaseConfig):
|
|
369
369
|
parsed = json.loads(user_sets)
|
370
370
|
if isinstance(parsed, list):
|
371
371
|
config_dict["user_defined_emoji_sets"] = [
|
372
|
-
|
372
|
+
EventMapping(**item) if isinstance(item, dict) else item
|
373
373
|
for item in parsed
|
374
374
|
]
|
375
375
|
except json.JSONDecodeError as e:
|
@@ -16,7 +16,6 @@ from provide.foundation.types import TRACE_LEVEL_NAME
|
|
16
16
|
|
17
17
|
if TYPE_CHECKING:
|
18
18
|
from provide.foundation.logger.config import TelemetryConfig
|
19
|
-
from provide.foundation.logger.setup.emoji_resolver import ResolvedEmojiConfig
|
20
19
|
|
21
20
|
_LAZY_SETUP_LOCK = threading.Lock()
|
22
21
|
_LAZY_SETUP_STATE: dict[str, Any] = {"done": False, "error": None, "in_progress": False}
|
@@ -31,7 +30,6 @@ class FoundationLogger:
|
|
31
30
|
)
|
32
31
|
self._is_configured_by_setup: bool = False
|
33
32
|
self._active_config: TelemetryConfig | None = None
|
34
|
-
self._active_resolved_emoji_config: ResolvedEmojiConfig | None = None
|
35
33
|
|
36
34
|
def _check_structlog_already_disabled(self) -> bool:
|
37
35
|
try:
|
@@ -10,18 +10,27 @@ from typing import Any
|
|
10
10
|
from provide.foundation.logger.core import logger
|
11
11
|
|
12
12
|
|
13
|
-
def get_logger(
|
13
|
+
def get_logger(
|
14
|
+
name: str | None = None,
|
15
|
+
emoji: str | None = None,
|
16
|
+
emoji_hierarchy: dict[str, str] | None = None
|
17
|
+
) -> Any:
|
14
18
|
"""
|
15
|
-
Get a logger instance with the given name.
|
19
|
+
Get a logger instance with the given name and optional emoji customization.
|
16
20
|
|
17
21
|
This is a convenience function that uses the global FoundationLogger.
|
18
22
|
|
19
23
|
Args:
|
20
24
|
name: Logger name (e.g., __name__ from a module)
|
25
|
+
emoji: Override emoji for this specific logger instance
|
26
|
+
emoji_hierarchy: Define emoji mapping for module hierarchy patterns
|
21
27
|
|
22
28
|
Returns:
|
23
29
|
Configured structlog logger instance
|
24
30
|
"""
|
31
|
+
# Emoji hierarchy removed - using event sets now
|
32
|
+
# emoji and emoji_hierarchy parameters are deprecated
|
33
|
+
|
25
34
|
return logger.get_logger(name)
|
26
35
|
|
27
36
|
|
@@ -26,8 +26,6 @@ from provide.foundation.types import (
|
|
26
26
|
LogLevelStr,
|
27
27
|
)
|
28
28
|
|
29
|
-
if TYPE_CHECKING:
|
30
|
-
from provide.foundation.logger.setup.emoji_resolver import ResolvedEmojiConfig
|
31
29
|
|
32
30
|
_LEVEL_TO_NUMERIC: dict[LogLevelStr, int] = {
|
33
31
|
"CRITICAL": stdlib_logging.CRITICAL,
|
@@ -71,96 +69,34 @@ def _config_create_timestamp_processors(
|
|
71
69
|
return processors
|
72
70
|
|
73
71
|
|
74
|
-
def
|
75
|
-
logging_config: LoggingConfig
|
72
|
+
def _config_create_event_enrichment_processors(
|
73
|
+
logging_config: LoggingConfig
|
76
74
|
) -> list[StructlogProcessor]:
|
77
75
|
processors: list[StructlogProcessor] = []
|
78
76
|
if logging_config.logger_name_emoji_prefix_enabled:
|
79
77
|
processors.append(cast(StructlogProcessor, add_logger_name_emoji_prefix))
|
80
78
|
if logging_config.das_emoji_prefix_enabled:
|
81
|
-
|
82
|
-
resolved_field_definitions, resolved_emoji_sets_lookup = resolved_emoji_config
|
83
|
-
|
84
|
-
def add_das_emoji_prefix_closure(
|
79
|
+
def add_event_enrichment_processor(
|
85
80
|
_logger: Any, _method_name: str, event_dict: structlog.types.EventDict
|
86
81
|
) -> structlog.types.EventDict:
|
87
|
-
#
|
88
|
-
from provide.foundation.
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
)
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
event_dict.pop(field_def.log_key, None)
|
101
|
-
emoji_set = resolved_emoji_sets_lookup.get(
|
102
|
-
field_def.emoji_set_name
|
103
|
-
)
|
104
|
-
if emoji_set:
|
105
|
-
value_str_lower = str(value_from_event).lower()
|
106
|
-
specific_emoji = emoji_set.emojis.get(value_str_lower)
|
107
|
-
default_key = (
|
108
|
-
field_def.default_emoji_override_key
|
109
|
-
or emoji_set.default_emoji_key
|
110
|
-
)
|
111
|
-
default_emoji = emoji_set.emojis.get(default_key, "❓")
|
112
|
-
chosen_emoji = (
|
113
|
-
specific_emoji
|
114
|
-
if specific_emoji is not None
|
115
|
-
else default_emoji
|
116
|
-
)
|
117
|
-
final_das_prefix_parts.append(f"[{chosen_emoji}]")
|
118
|
-
else:
|
119
|
-
final_das_prefix_parts.append("[❓]")
|
120
|
-
else: # Fallback to Core DAS System
|
121
|
-
domain = event_dict.pop("domain", None)
|
122
|
-
action = event_dict.pop("action", None)
|
123
|
-
status = event_dict.pop("status", None)
|
124
|
-
if domain or action or status:
|
125
|
-
domain_emoji = (
|
126
|
-
PRIMARY_EMOJI.get(str(domain).lower(), PRIMARY_EMOJI["default"])
|
127
|
-
if domain
|
128
|
-
else PRIMARY_EMOJI["default"]
|
129
|
-
)
|
130
|
-
action_emoji = (
|
131
|
-
SECONDARY_EMOJI.get(
|
132
|
-
str(action).lower(), SECONDARY_EMOJI["default"]
|
133
|
-
)
|
134
|
-
if action
|
135
|
-
else SECONDARY_EMOJI["default"]
|
136
|
-
)
|
137
|
-
status_emoji = (
|
138
|
-
TERTIARY_EMOJI.get(
|
139
|
-
str(status).lower(), TERTIARY_EMOJI["default"]
|
140
|
-
)
|
141
|
-
if status
|
142
|
-
else TERTIARY_EMOJI["default"]
|
143
|
-
)
|
144
|
-
final_das_prefix_parts.extend(
|
145
|
-
[f"[{domain_emoji}]", f"[{action_emoji}]", f"[{status_emoji}]"]
|
146
|
-
)
|
147
|
-
|
148
|
-
if final_das_prefix_parts:
|
149
|
-
final_das_prefix_str = "".join(final_das_prefix_parts)
|
150
|
-
event_msg = event_dict.get("event")
|
151
|
-
event_dict["event"] = (
|
152
|
-
f"{final_das_prefix_str} {event_msg}"
|
153
|
-
if event_msg is not None
|
154
|
-
else final_das_prefix_str
|
155
|
-
)
|
156
|
-
return event_dict
|
157
|
-
|
158
|
-
processors.append(cast(StructlogProcessor, add_das_emoji_prefix_closure))
|
82
|
+
# Lazy import to avoid circular dependency
|
83
|
+
from provide.foundation.eventsets.resolver import get_resolver
|
84
|
+
from provide.foundation.eventsets.registry import discover_event_sets
|
85
|
+
|
86
|
+
# Initialize on first use
|
87
|
+
if not hasattr(add_event_enrichment_processor, '_initialized'):
|
88
|
+
discover_event_sets()
|
89
|
+
add_event_enrichment_processor._initialized = True
|
90
|
+
|
91
|
+
resolver = get_resolver()
|
92
|
+
return resolver.enrich_event(event_dict)
|
93
|
+
|
94
|
+
processors.append(cast(StructlogProcessor, add_event_enrichment_processor))
|
159
95
|
return processors
|
160
96
|
|
161
97
|
|
162
98
|
def _build_core_processors_list(
|
163
|
-
config: TelemetryConfig
|
99
|
+
config: TelemetryConfig
|
164
100
|
) -> list[StructlogProcessor]:
|
165
101
|
log_cfg = config.logging
|
166
102
|
processors: list[StructlogProcessor] = [
|
@@ -202,7 +138,7 @@ def _build_core_processors_list(
|
|
202
138
|
if config.tracing_enabled and not config.globally_disabled:
|
203
139
|
processors.append(cast(StructlogProcessor, inject_trace_context))
|
204
140
|
|
205
|
-
processors.extend(
|
141
|
+
processors.extend(_config_create_event_enrichment_processors(log_cfg))
|
206
142
|
return processors
|
207
143
|
|
208
144
|
|
@@ -243,10 +179,10 @@ def _build_formatter_processors_list(
|
|
243
179
|
# Unknown formatter, warn and default to key_value
|
244
180
|
# Use setup coordinator logger
|
245
181
|
from provide.foundation.logger.setup.coordinator import (
|
246
|
-
|
182
|
+
create_foundation_internal_logger,
|
247
183
|
)
|
248
184
|
|
249
|
-
setup_logger =
|
185
|
+
setup_logger = create_foundation_internal_logger()
|
250
186
|
setup_logger.warning(
|
251
187
|
f"Unknown formatter '{logging_config.console_formatter}', using default 'key_value'. "
|
252
188
|
f"Valid formatters: ['json', 'key_value']"
|
@@ -8,7 +8,10 @@ Handles structured logging configuration, processor setup, and emoji resolution.
|
|
8
8
|
Provides the core setup functionality for the Foundation logging system.
|
9
9
|
"""
|
10
10
|
|
11
|
-
from provide.foundation.logger.setup.coordinator import
|
11
|
+
from provide.foundation.logger.setup.coordinator import (
|
12
|
+
get_vanilla_logger,
|
13
|
+
internal_setup,
|
14
|
+
)
|
12
15
|
|
13
16
|
# Import testing utilities conditionally
|
14
17
|
try:
|
@@ -22,6 +25,7 @@ except ImportError:
|
|
22
25
|
reset_for_testing = None
|
23
26
|
|
24
27
|
__all__ = [
|
28
|
+
"get_vanilla_logger",
|
25
29
|
"internal_setup",
|
26
30
|
]
|
27
31
|
|
@@ -17,8 +17,6 @@ from provide.foundation.logger.core import (
|
|
17
17
|
_LAZY_SETUP_STATE,
|
18
18
|
logger as foundation_logger,
|
19
19
|
)
|
20
|
-
from provide.foundation.logger.emoji.sets import BUILTIN_EMOJI_SETS
|
21
|
-
from provide.foundation.logger.setup.emoji_resolver import resolve_active_emoji_config
|
22
20
|
from provide.foundation.logger.setup.processors import (
|
23
21
|
configure_structlog_output,
|
24
22
|
handle_globally_disabled_setup,
|
@@ -33,22 +31,35 @@ _FOUNDATION_LOG_LEVEL: int | None = None
|
|
33
31
|
|
34
32
|
|
35
33
|
def get_foundation_log_level() -> int:
|
36
|
-
"""Get
|
34
|
+
"""Get Foundation log level for setup phase, safely."""
|
37
35
|
global _FOUNDATION_LOG_LEVEL
|
38
36
|
if _FOUNDATION_LOG_LEVEL is None:
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
37
|
+
import os
|
38
|
+
|
39
|
+
# Direct env read - avoid config imports that cause circular deps
|
40
|
+
level_str = os.environ.get("FOUNDATION_LOG_LEVEL", "INFO").upper()
|
41
|
+
|
42
|
+
# Validate and map to numeric level
|
43
|
+
valid_levels = {
|
44
|
+
"CRITICAL": stdlib_logging.CRITICAL,
|
45
|
+
"ERROR": stdlib_logging.ERROR,
|
46
|
+
"WARNING": stdlib_logging.WARNING,
|
47
|
+
"INFO": stdlib_logging.INFO,
|
48
|
+
"DEBUG": stdlib_logging.DEBUG,
|
49
|
+
"NOTSET": stdlib_logging.NOTSET,
|
50
|
+
}
|
51
|
+
|
52
|
+
_FOUNDATION_LOG_LEVEL = valid_levels.get(level_str, stdlib_logging.INFO)
|
47
53
|
return _FOUNDATION_LOG_LEVEL
|
48
54
|
|
49
55
|
|
50
|
-
def
|
51
|
-
"""
|
56
|
+
def create_foundation_internal_logger(globally_disabled: bool = False) -> Any:
|
57
|
+
"""
|
58
|
+
Create Foundation's internal setup logger (structlog).
|
59
|
+
|
60
|
+
This is used internally by Foundation during its own initialization.
|
61
|
+
Components should use get_vanilla_logger() instead.
|
62
|
+
"""
|
52
63
|
if globally_disabled:
|
53
64
|
# Configure structlog to be a no-op for core setup logger
|
54
65
|
structlog.configure(
|
@@ -84,6 +95,55 @@ def create_core_setup_logger(globally_disabled: bool = False) -> Any:
|
|
84
95
|
return structlog.get_logger(_CORE_SETUP_LOGGER_NAME)
|
85
96
|
|
86
97
|
|
98
|
+
def get_vanilla_logger(name: str):
|
99
|
+
"""
|
100
|
+
Get a vanilla Python logger without Foundation enhancements.
|
101
|
+
|
102
|
+
This provides a plain Python logger that respects FOUNDATION_LOG_LEVEL
|
103
|
+
but doesn't trigger Foundation's initialization. Use this for logging
|
104
|
+
during Foundation's setup phase or when you need to avoid circular
|
105
|
+
dependencies.
|
106
|
+
|
107
|
+
Args:
|
108
|
+
name: Logger name (e.g., "provide.foundation.otel.setup")
|
109
|
+
|
110
|
+
Returns:
|
111
|
+
A standard Python logging.Logger instance
|
112
|
+
|
113
|
+
Note:
|
114
|
+
"Vanilla" means plain/unmodified Python logging, without
|
115
|
+
Foundation's features like emoji prefixes or structured logging.
|
116
|
+
"""
|
117
|
+
import logging
|
118
|
+
import sys
|
119
|
+
import os
|
120
|
+
|
121
|
+
slog = logging.getLogger(name)
|
122
|
+
|
123
|
+
# Configure only once per logger
|
124
|
+
if not slog.handlers:
|
125
|
+
log_level = get_foundation_log_level()
|
126
|
+
slog.setLevel(log_level)
|
127
|
+
|
128
|
+
# Respect FOUNDATION_LOG_OUTPUT setting
|
129
|
+
output = os.environ.get("FOUNDATION_LOG_OUTPUT", "stderr").lower()
|
130
|
+
stream = sys.stderr if output != "stdout" else sys.stdout
|
131
|
+
|
132
|
+
handler = logging.StreamHandler(stream)
|
133
|
+
handler.setLevel(log_level)
|
134
|
+
formatter = logging.Formatter(
|
135
|
+
'%(asctime)s [%(levelname)-5s] %(message)s',
|
136
|
+
datefmt='%Y-%m-%dT%H:%M:%S'
|
137
|
+
)
|
138
|
+
handler.setFormatter(formatter)
|
139
|
+
slog.addHandler(handler)
|
140
|
+
|
141
|
+
# Don't propagate to avoid duplicate messages
|
142
|
+
slog.propagate = False
|
143
|
+
|
144
|
+
return slog
|
145
|
+
|
146
|
+
|
87
147
|
def internal_setup(
|
88
148
|
config: TelemetryConfig | None = None, is_explicit_call: bool = False
|
89
149
|
) -> None:
|
@@ -95,11 +155,10 @@ def internal_setup(
|
|
95
155
|
structlog.reset_defaults()
|
96
156
|
foundation_logger._is_configured_by_setup = False
|
97
157
|
foundation_logger._active_config = None
|
98
|
-
foundation_logger._active_resolved_emoji_config = None
|
99
158
|
_LAZY_SETUP_STATE.update({"done": False, "error": None, "in_progress": False})
|
100
159
|
|
101
160
|
current_config = config if config is not None else TelemetryConfig.from_env()
|
102
|
-
core_setup_logger =
|
161
|
+
core_setup_logger = create_foundation_internal_logger(
|
103
162
|
globally_disabled=current_config.globally_disabled
|
104
163
|
)
|
105
164
|
|
@@ -111,28 +170,21 @@ def internal_setup(
|
|
111
170
|
formatter=current_config.logging.console_formatter,
|
112
171
|
)
|
113
172
|
|
114
|
-
resolved_emoji_config = resolve_active_emoji_config(
|
115
|
-
current_config.logging, BUILTIN_EMOJI_SETS
|
116
|
-
)
|
117
173
|
|
118
174
|
if current_config.globally_disabled:
|
119
175
|
handle_globally_disabled_setup()
|
120
176
|
else:
|
121
177
|
configure_structlog_output(
|
122
|
-
current_config,
|
178
|
+
current_config, get_log_stream()
|
123
179
|
)
|
124
180
|
|
125
181
|
foundation_logger._is_configured_by_setup = is_explicit_call
|
126
182
|
foundation_logger._active_config = current_config
|
127
|
-
foundation_logger._active_resolved_emoji_config = resolved_emoji_config
|
128
183
|
_LAZY_SETUP_STATE["done"] = True
|
129
184
|
|
130
185
|
if not current_config.globally_disabled:
|
131
|
-
field_definitions, emoji_sets = resolved_emoji_config
|
132
186
|
core_setup_logger.debug(
|
133
187
|
"⚙️➡️✅ Foundation (structlog) setup completed",
|
134
|
-
emoji_sets_enabled=len(field_definitions) > 0,
|
135
|
-
emoji_sets_count=len(emoji_sets),
|
136
188
|
processors_configured=True,
|
137
189
|
log_file_enabled=current_config.logging.log_file is not None,
|
138
190
|
)
|
@@ -15,12 +15,8 @@ from provide.foundation.logger.processors import (
|
|
15
15
|
_build_core_processors_list,
|
16
16
|
_build_formatter_processors_list,
|
17
17
|
)
|
18
|
-
from provide.foundation.logger.setup.emoji_resolver import ResolvedEmojiConfig
|
19
|
-
|
20
|
-
|
21
18
|
def build_complete_processor_chain(
|
22
19
|
config: TelemetryConfig,
|
23
|
-
resolved_emoji_config: ResolvedEmojiConfig,
|
24
20
|
log_stream: TextIO,
|
25
21
|
) -> list[Any]:
|
26
22
|
"""
|
@@ -28,13 +24,12 @@ def build_complete_processor_chain(
|
|
28
24
|
|
29
25
|
Args:
|
30
26
|
config: Telemetry configuration
|
31
|
-
resolved_emoji_config: Resolved emoji configuration
|
32
27
|
log_stream: Output stream for logging
|
33
28
|
|
34
29
|
Returns:
|
35
30
|
List of processors for structlog
|
36
31
|
"""
|
37
|
-
core_processors = _build_core_processors_list(config
|
32
|
+
core_processors = _build_core_processors_list(config)
|
38
33
|
formatter_processors = _build_formatter_processors_list(config.logging, log_stream)
|
39
34
|
return cast(list[Any], core_processors + formatter_processors)
|
40
35
|
|
@@ -57,7 +52,6 @@ def apply_structlog_configuration(processors: list[Any], log_stream: TextIO) ->
|
|
57
52
|
|
58
53
|
def configure_structlog_output(
|
59
54
|
config: TelemetryConfig,
|
60
|
-
resolved_emoji_config: ResolvedEmojiConfig,
|
61
55
|
log_stream: TextIO,
|
62
56
|
) -> None:
|
63
57
|
"""
|
@@ -65,11 +59,10 @@ def configure_structlog_output(
|
|
65
59
|
|
66
60
|
Args:
|
67
61
|
config: Telemetry configuration
|
68
|
-
resolved_emoji_config: Resolved emoji configuration
|
69
62
|
log_stream: Output stream for logging
|
70
63
|
"""
|
71
64
|
processors = build_complete_processor_chain(
|
72
|
-
config,
|
65
|
+
config, log_stream
|
73
66
|
)
|
74
67
|
apply_structlog_configuration(processors, log_stream)
|
75
68
|
|