provide-foundation 0.0.0.dev1__py3-none-any.whl → 0.0.0.dev3__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 +36 -10
- provide/foundation/archive/__init__.py +1 -1
- provide/foundation/archive/base.py +15 -14
- provide/foundation/archive/bzip2.py +40 -40
- provide/foundation/archive/gzip.py +42 -42
- provide/foundation/archive/operations.py +93 -96
- provide/foundation/archive/tar.py +33 -31
- provide/foundation/archive/zip.py +52 -50
- provide/foundation/asynctools/__init__.py +20 -0
- provide/foundation/asynctools/core.py +126 -0
- provide/foundation/cli/__init__.py +2 -2
- provide/foundation/cli/commands/deps.py +15 -9
- provide/foundation/cli/commands/logs/__init__.py +3 -3
- provide/foundation/cli/commands/logs/generate.py +2 -2
- provide/foundation/cli/commands/logs/query.py +4 -4
- provide/foundation/cli/commands/logs/send.py +3 -3
- provide/foundation/cli/commands/logs/tail.py +3 -3
- provide/foundation/cli/decorators.py +11 -11
- provide/foundation/cli/main.py +1 -1
- provide/foundation/cli/testing.py +2 -40
- provide/foundation/cli/utils.py +21 -18
- provide/foundation/config/__init__.py +35 -2
- provide/foundation/config/base.py +2 -2
- provide/foundation/config/converters.py +477 -0
- provide/foundation/config/defaults.py +67 -0
- provide/foundation/config/env.py +6 -20
- provide/foundation/config/loader.py +10 -4
- provide/foundation/config/sync.py +8 -6
- provide/foundation/config/types.py +5 -5
- provide/foundation/config/validators.py +4 -4
- provide/foundation/console/input.py +5 -5
- provide/foundation/console/output.py +36 -14
- provide/foundation/context/__init__.py +8 -4
- provide/foundation/context/core.py +88 -110
- provide/foundation/crypto/certificates/__init__.py +9 -5
- provide/foundation/crypto/certificates/base.py +2 -2
- provide/foundation/crypto/certificates/certificate.py +48 -19
- provide/foundation/crypto/certificates/factory.py +26 -18
- provide/foundation/crypto/certificates/generator.py +24 -23
- provide/foundation/crypto/certificates/loader.py +24 -16
- provide/foundation/crypto/certificates/operations.py +17 -10
- provide/foundation/crypto/certificates/trust.py +21 -21
- provide/foundation/env/__init__.py +28 -0
- provide/foundation/env/core.py +218 -0
- provide/foundation/errors/__init__.py +3 -3
- provide/foundation/errors/decorators.py +0 -234
- provide/foundation/errors/types.py +0 -98
- provide/foundation/eventsets/display.py +13 -14
- provide/foundation/eventsets/registry.py +61 -31
- provide/foundation/eventsets/resolver.py +50 -46
- provide/foundation/eventsets/sets/das.py +8 -8
- provide/foundation/eventsets/sets/database.py +14 -14
- provide/foundation/eventsets/sets/http.py +21 -21
- provide/foundation/eventsets/sets/llm.py +16 -16
- provide/foundation/eventsets/sets/task_queue.py +13 -13
- provide/foundation/eventsets/types.py +7 -7
- provide/foundation/file/directory.py +14 -23
- provide/foundation/file/lock.py +4 -3
- provide/foundation/hub/components.py +75 -389
- provide/foundation/hub/config.py +157 -0
- provide/foundation/hub/discovery.py +63 -0
- provide/foundation/hub/handlers.py +89 -0
- provide/foundation/hub/lifecycle.py +195 -0
- provide/foundation/hub/manager.py +7 -4
- provide/foundation/hub/processors.py +49 -0
- provide/foundation/integrations/__init__.py +11 -0
- provide/foundation/{observability → integrations}/openobserve/__init__.py +10 -7
- provide/foundation/{observability → integrations}/openobserve/auth.py +1 -1
- provide/foundation/{observability → integrations}/openobserve/client.py +14 -14
- provide/foundation/{observability → integrations}/openobserve/commands.py +12 -12
- provide/foundation/integrations/openobserve/config.py +37 -0
- provide/foundation/{observability → integrations}/openobserve/formatters.py +1 -1
- provide/foundation/{observability → integrations}/openobserve/otlp.py +2 -2
- provide/foundation/{observability → integrations}/openobserve/search.py +2 -3
- provide/foundation/{observability → integrations}/openobserve/streaming.py +5 -5
- provide/foundation/logger/__init__.py +0 -1
- provide/foundation/logger/config/base.py +1 -1
- provide/foundation/logger/config/logging.py +69 -299
- provide/foundation/logger/config/telemetry.py +39 -121
- provide/foundation/logger/factories.py +2 -2
- provide/foundation/logger/processors/main.py +12 -10
- provide/foundation/logger/ratelimit/limiters.py +4 -4
- provide/foundation/logger/ratelimit/processor.py +1 -1
- provide/foundation/logger/setup/coordinator.py +39 -25
- provide/foundation/logger/setup/processors.py +3 -3
- provide/foundation/logger/setup/testing.py +14 -0
- provide/foundation/logger/trace.py +5 -5
- provide/foundation/metrics/__init__.py +1 -1
- provide/foundation/metrics/otel.py +3 -1
- provide/foundation/observability/__init__.py +3 -3
- provide/foundation/process/__init__.py +9 -0
- provide/foundation/process/exit.py +48 -0
- provide/foundation/process/lifecycle.py +69 -46
- provide/foundation/resilience/__init__.py +36 -0
- provide/foundation/resilience/circuit.py +166 -0
- provide/foundation/resilience/decorators.py +236 -0
- provide/foundation/resilience/fallback.py +208 -0
- provide/foundation/resilience/retry.py +327 -0
- provide/foundation/serialization/__init__.py +16 -0
- provide/foundation/serialization/core.py +70 -0
- provide/foundation/streams/config.py +78 -0
- provide/foundation/streams/console.py +4 -5
- provide/foundation/streams/core.py +5 -2
- provide/foundation/streams/file.py +12 -2
- provide/foundation/testing/__init__.py +29 -9
- provide/foundation/testing/archive/__init__.py +7 -7
- provide/foundation/testing/archive/fixtures.py +58 -54
- provide/foundation/testing/cli.py +30 -20
- provide/foundation/testing/common/__init__.py +13 -15
- provide/foundation/testing/common/fixtures.py +27 -57
- provide/foundation/testing/file/__init__.py +15 -15
- provide/foundation/testing/file/content_fixtures.py +289 -0
- provide/foundation/testing/file/directory_fixtures.py +107 -0
- provide/foundation/testing/file/fixtures.py +42 -516
- provide/foundation/testing/file/special_fixtures.py +145 -0
- provide/foundation/testing/logger.py +89 -8
- provide/foundation/testing/mocking/__init__.py +21 -21
- provide/foundation/testing/mocking/fixtures.py +80 -67
- provide/foundation/testing/process/__init__.py +23 -23
- provide/foundation/testing/process/async_fixtures.py +414 -0
- provide/foundation/testing/process/fixtures.py +48 -571
- provide/foundation/testing/process/subprocess_fixtures.py +210 -0
- provide/foundation/testing/threading/__init__.py +17 -17
- provide/foundation/testing/threading/basic_fixtures.py +105 -0
- provide/foundation/testing/threading/data_fixtures.py +101 -0
- provide/foundation/testing/threading/execution_fixtures.py +278 -0
- provide/foundation/testing/threading/fixtures.py +32 -502
- provide/foundation/testing/threading/sync_fixtures.py +100 -0
- provide/foundation/testing/time/__init__.py +11 -11
- provide/foundation/testing/time/fixtures.py +95 -83
- provide/foundation/testing/transport/__init__.py +9 -9
- provide/foundation/testing/transport/fixtures.py +54 -54
- provide/foundation/time/__init__.py +18 -0
- provide/foundation/time/core.py +63 -0
- provide/foundation/tools/__init__.py +2 -2
- provide/foundation/tools/base.py +68 -67
- provide/foundation/tools/cache.py +69 -74
- provide/foundation/tools/downloader.py +68 -62
- provide/foundation/tools/installer.py +51 -57
- provide/foundation/tools/registry.py +38 -45
- provide/foundation/tools/resolver.py +70 -68
- provide/foundation/tools/verifier.py +39 -50
- provide/foundation/tracer/spans.py +2 -14
- provide/foundation/transport/__init__.py +26 -33
- provide/foundation/transport/base.py +32 -30
- provide/foundation/transport/client.py +44 -49
- provide/foundation/transport/config.py +36 -107
- provide/foundation/transport/errors.py +13 -27
- provide/foundation/transport/http.py +69 -55
- provide/foundation/transport/middleware.py +113 -114
- provide/foundation/transport/registry.py +29 -27
- provide/foundation/transport/types.py +6 -6
- provide/foundation/utils/deps.py +17 -14
- provide/foundation/utils/parsing.py +49 -4
- {provide_foundation-0.0.0.dev1.dist-info → provide_foundation-0.0.0.dev3.dist-info}/METADATA +2 -2
- provide_foundation-0.0.0.dev3.dist-info/RECORD +233 -0
- provide_foundation-0.0.0.dev1.dist-info/RECORD +0 -200
- /provide/foundation/{observability → integrations}/openobserve/exceptions.py +0 -0
- /provide/foundation/{observability → integrations}/openobserve/models.py +0 -0
- {provide_foundation-0.0.0.dev1.dist-info → provide_foundation-0.0.0.dev3.dist-info}/WHEEL +0 -0
- {provide_foundation-0.0.0.dev1.dist-info → provide_foundation-0.0.0.dev3.dist-info}/entry_points.txt +0 -0
- {provide_foundation-0.0.0.dev1.dist-info → provide_foundation-0.0.0.dev3.dist-info}/licenses/LICENSE +0 -0
- {provide_foundation-0.0.0.dev1.dist-info → provide_foundation-0.0.0.dev3.dist-info}/top_level.txt +0 -0
@@ -13,7 +13,7 @@ from provide.foundation.logger.core import logger
|
|
13
13
|
def get_logger(
|
14
14
|
name: str | None = None,
|
15
15
|
emoji: str | None = None,
|
16
|
-
emoji_hierarchy: dict[str, str] | None = None
|
16
|
+
emoji_hierarchy: dict[str, str] | None = None,
|
17
17
|
) -> Any:
|
18
18
|
"""
|
19
19
|
Get a logger instance with the given name and optional emoji customization.
|
@@ -30,7 +30,7 @@ def get_logger(
|
|
30
30
|
"""
|
31
31
|
# Emoji hierarchy removed - using event sets now
|
32
32
|
# emoji and emoji_hierarchy parameters are deprecated
|
33
|
-
|
33
|
+
|
34
34
|
return logger.get_logger(name)
|
35
35
|
|
36
36
|
|
@@ -7,7 +7,7 @@ Structlog processors for Foundation Telemetry.
|
|
7
7
|
|
8
8
|
import json
|
9
9
|
import logging as stdlib_logging
|
10
|
-
from typing import
|
10
|
+
from typing import Any, TextIO, cast
|
11
11
|
|
12
12
|
import structlog
|
13
13
|
|
@@ -26,7 +26,6 @@ from provide.foundation.types import (
|
|
26
26
|
LogLevelStr,
|
27
27
|
)
|
28
28
|
|
29
|
-
|
30
29
|
_LEVEL_TO_NUMERIC: dict[LogLevelStr, int] = {
|
31
30
|
"CRITICAL": stdlib_logging.CRITICAL,
|
32
31
|
"ERROR": stdlib_logging.ERROR,
|
@@ -70,24 +69,29 @@ def _config_create_timestamp_processors(
|
|
70
69
|
|
71
70
|
|
72
71
|
def _config_create_event_enrichment_processors(
|
73
|
-
logging_config: LoggingConfig
|
72
|
+
logging_config: LoggingConfig,
|
74
73
|
) -> list[StructlogProcessor]:
|
75
74
|
processors: list[StructlogProcessor] = []
|
76
75
|
if logging_config.logger_name_emoji_prefix_enabled:
|
77
76
|
processors.append(cast(StructlogProcessor, add_logger_name_emoji_prefix))
|
78
77
|
if logging_config.das_emoji_prefix_enabled:
|
78
|
+
|
79
79
|
def add_event_enrichment_processor(
|
80
80
|
_logger: Any, _method_name: str, event_dict: structlog.types.EventDict
|
81
81
|
) -> structlog.types.EventDict:
|
82
82
|
# Lazy import to avoid circular dependency
|
83
|
-
from provide.foundation.eventsets.resolver import get_resolver
|
84
83
|
from provide.foundation.eventsets.registry import discover_event_sets
|
85
|
-
|
84
|
+
from provide.foundation.eventsets.resolver import get_resolver
|
85
|
+
|
86
86
|
# Initialize on first use
|
87
|
-
if not hasattr(add_event_enrichment_processor,
|
87
|
+
if not hasattr(add_event_enrichment_processor, "_initialized"):
|
88
|
+
from provide.foundation.logger.setup.coordinator import create_foundation_internal_logger
|
89
|
+
setup_logger = create_foundation_internal_logger()
|
90
|
+
setup_logger.trace("Initializing event enrichment processor")
|
88
91
|
discover_event_sets()
|
89
92
|
add_event_enrichment_processor._initialized = True
|
90
|
-
|
93
|
+
setup_logger.trace("Event enrichment processor initialized")
|
94
|
+
|
91
95
|
resolver = get_resolver()
|
92
96
|
return resolver.enrich_event(event_dict)
|
93
97
|
|
@@ -95,9 +99,7 @@ def _config_create_event_enrichment_processors(
|
|
95
99
|
return processors
|
96
100
|
|
97
101
|
|
98
|
-
def _build_core_processors_list(
|
99
|
-
config: TelemetryConfig
|
100
|
-
) -> list[StructlogProcessor]:
|
102
|
+
def _build_core_processors_list(config: TelemetryConfig) -> list[StructlogProcessor]:
|
101
103
|
log_cfg = config.logging
|
102
104
|
processors: list[StructlogProcessor] = [
|
103
105
|
structlog.contextvars.merge_contextvars,
|
@@ -17,7 +17,7 @@ class SyncRateLimiter:
|
|
17
17
|
Thread-safe implementation suitable for synchronous logging operations.
|
18
18
|
"""
|
19
19
|
|
20
|
-
def __init__(self, capacity: float, refill_rate: float):
|
20
|
+
def __init__(self, capacity: float, refill_rate: float) -> None:
|
21
21
|
"""
|
22
22
|
Initialize the rate limiter.
|
23
23
|
|
@@ -87,7 +87,7 @@ class AsyncRateLimiter:
|
|
87
87
|
Uses asyncio.Lock for thread safety in async contexts.
|
88
88
|
"""
|
89
89
|
|
90
|
-
def __init__(self, capacity: float, refill_rate: float):
|
90
|
+
def __init__(self, capacity: float, refill_rate: float) -> None:
|
91
91
|
"""
|
92
92
|
Initialize the async rate limiter.
|
93
93
|
|
@@ -160,7 +160,7 @@ class GlobalRateLimiter:
|
|
160
160
|
_instance = None
|
161
161
|
_lock = threading.Lock()
|
162
162
|
|
163
|
-
def __new__(cls):
|
163
|
+
def __new__(cls) -> "GlobalRateLimiter":
|
164
164
|
if cls._instance is None:
|
165
165
|
with cls._lock:
|
166
166
|
if cls._instance is None:
|
@@ -168,7 +168,7 @@ class GlobalRateLimiter:
|
|
168
168
|
cls._instance._initialized = False
|
169
169
|
return cls._instance
|
170
170
|
|
171
|
-
def __init__(self):
|
171
|
+
def __init__(self) -> None:
|
172
172
|
if self._initialized:
|
173
173
|
return
|
174
174
|
|
@@ -107,7 +107,7 @@ class RateLimiterProcessor:
|
|
107
107
|
stats = self.rate_limiter.get_stats()
|
108
108
|
|
109
109
|
# Check if there's been any rate limiting activity
|
110
|
-
global_stats = stats.get("global"
|
110
|
+
global_stats = stats.get("global") or {}
|
111
111
|
total_denied = global_stats.get("total_denied", 0)
|
112
112
|
|
113
113
|
if not self.suppressed_counts and total_denied == 0:
|
@@ -28,6 +28,7 @@ _PROVIDE_SETUP_LOCK = threading.Lock()
|
|
28
28
|
_CORE_SETUP_LOGGER_NAME = "provide.foundation.core_setup"
|
29
29
|
_EXPLICIT_SETUP_DONE = False
|
30
30
|
_FOUNDATION_LOG_LEVEL: int | None = None
|
31
|
+
_CACHED_SETUP_LOGGER: Any | None = None
|
31
32
|
|
32
33
|
|
33
34
|
def get_foundation_log_level() -> int:
|
@@ -35,10 +36,10 @@ def get_foundation_log_level() -> int:
|
|
35
36
|
global _FOUNDATION_LOG_LEVEL
|
36
37
|
if _FOUNDATION_LOG_LEVEL is None:
|
37
38
|
import os
|
38
|
-
|
39
|
+
|
39
40
|
# Direct env read - avoid config imports that cause circular deps
|
40
41
|
level_str = os.environ.get("FOUNDATION_LOG_LEVEL", "INFO").upper()
|
41
|
-
|
42
|
+
|
42
43
|
# Validate and map to numeric level
|
43
44
|
valid_levels = {
|
44
45
|
"CRITICAL": stdlib_logging.CRITICAL,
|
@@ -48,7 +49,7 @@ def get_foundation_log_level() -> int:
|
|
48
49
|
"DEBUG": stdlib_logging.DEBUG,
|
49
50
|
"NOTSET": stdlib_logging.NOTSET,
|
50
51
|
}
|
51
|
-
|
52
|
+
|
52
53
|
_FOUNDATION_LOG_LEVEL = valid_levels.get(level_str, stdlib_logging.INFO)
|
53
54
|
return _FOUNDATION_LOG_LEVEL
|
54
55
|
|
@@ -56,10 +57,17 @@ def get_foundation_log_level() -> int:
|
|
56
57
|
def create_foundation_internal_logger(globally_disabled: bool = False) -> Any:
|
57
58
|
"""
|
58
59
|
Create Foundation's internal setup logger (structlog).
|
59
|
-
|
60
|
+
|
60
61
|
This is used internally by Foundation during its own initialization.
|
61
62
|
Components should use get_vanilla_logger() instead.
|
63
|
+
|
64
|
+
Returns the same logger instance when called multiple times (singleton pattern).
|
62
65
|
"""
|
66
|
+
global _CACHED_SETUP_LOGGER
|
67
|
+
|
68
|
+
# Return cached logger if already created
|
69
|
+
if _CACHED_SETUP_LOGGER is not None:
|
70
|
+
return _CACHED_SETUP_LOGGER
|
63
71
|
if globally_disabled:
|
64
72
|
# Configure structlog to be a no-op for core setup logger
|
65
73
|
structlog.configure(
|
@@ -68,11 +76,12 @@ def create_foundation_internal_logger(globally_disabled: bool = False) -> Any:
|
|
68
76
|
wrapper_class=structlog.BoundLogger,
|
69
77
|
cache_logger_on_first_use=True,
|
70
78
|
)
|
71
|
-
|
79
|
+
_CACHED_SETUP_LOGGER = structlog.get_logger(_CORE_SETUP_LOGGER_NAME)
|
80
|
+
return _CACHED_SETUP_LOGGER
|
72
81
|
else:
|
73
82
|
# Get the foundation log output stream
|
74
83
|
try:
|
75
|
-
logging_config = LoggingConfig.from_env(
|
84
|
+
logging_config = LoggingConfig.from_env()
|
76
85
|
foundation_stream = get_foundation_log_stream(
|
77
86
|
logging_config.foundation_log_output
|
78
87
|
)
|
@@ -92,55 +101,61 @@ def create_foundation_internal_logger(globally_disabled: bool = False) -> Any:
|
|
92
101
|
cache_logger_on_first_use=True,
|
93
102
|
)
|
94
103
|
|
95
|
-
|
104
|
+
_CACHED_SETUP_LOGGER = structlog.get_logger(_CORE_SETUP_LOGGER_NAME)
|
105
|
+
return _CACHED_SETUP_LOGGER
|
106
|
+
|
96
107
|
|
108
|
+
def reset_setup_logger_cache() -> None:
|
109
|
+
"""Reset the cached setup logger for testing."""
|
110
|
+
global _CACHED_SETUP_LOGGER
|
111
|
+
_CACHED_SETUP_LOGGER = None
|
97
112
|
|
98
|
-
|
113
|
+
|
114
|
+
def get_vanilla_logger(name: str) -> object:
|
99
115
|
"""
|
100
116
|
Get a vanilla Python logger without Foundation enhancements.
|
101
|
-
|
117
|
+
|
102
118
|
This provides a plain Python logger that respects FOUNDATION_LOG_LEVEL
|
103
119
|
but doesn't trigger Foundation's initialization. Use this for logging
|
104
120
|
during Foundation's setup phase or when you need to avoid circular
|
105
121
|
dependencies.
|
106
|
-
|
122
|
+
|
107
123
|
Args:
|
108
124
|
name: Logger name (e.g., "provide.foundation.otel.setup")
|
109
|
-
|
125
|
+
|
110
126
|
Returns:
|
111
127
|
A standard Python logging.Logger instance
|
112
|
-
|
128
|
+
|
113
129
|
Note:
|
114
130
|
"Vanilla" means plain/unmodified Python logging, without
|
115
131
|
Foundation's features like emoji prefixes or structured logging.
|
116
132
|
"""
|
117
133
|
import logging
|
118
|
-
import sys
|
119
134
|
import os
|
120
|
-
|
135
|
+
import sys
|
136
|
+
|
121
137
|
slog = logging.getLogger(name)
|
122
|
-
|
138
|
+
|
123
139
|
# Configure only once per logger
|
124
140
|
if not slog.handlers:
|
125
141
|
log_level = get_foundation_log_level()
|
126
142
|
slog.setLevel(log_level)
|
127
|
-
|
143
|
+
|
128
144
|
# Respect FOUNDATION_LOG_OUTPUT setting
|
129
145
|
output = os.environ.get("FOUNDATION_LOG_OUTPUT", "stderr").lower()
|
130
146
|
stream = sys.stderr if output != "stdout" else sys.stdout
|
131
|
-
|
147
|
+
|
132
148
|
handler = logging.StreamHandler(stream)
|
133
149
|
handler.setLevel(log_level)
|
134
150
|
formatter = logging.Formatter(
|
135
|
-
|
136
|
-
datefmt='%Y-%m-%dT%H:%M:%S'
|
151
|
+
"%(asctime)s [%(levelname)-5s] %(message)s", datefmt="%Y-%m-%dT%H:%M:%S"
|
137
152
|
)
|
138
153
|
handler.setFormatter(formatter)
|
139
154
|
slog.addHandler(handler)
|
140
|
-
|
155
|
+
|
141
156
|
# Don't propagate to avoid duplicate messages
|
142
157
|
slog.propagate = False
|
143
|
-
|
158
|
+
|
144
159
|
return slog
|
145
160
|
|
146
161
|
|
@@ -170,13 +185,12 @@ def internal_setup(
|
|
170
185
|
formatter=current_config.logging.console_formatter,
|
171
186
|
)
|
172
187
|
|
173
|
-
|
174
188
|
if current_config.globally_disabled:
|
189
|
+
core_setup_logger.trace("Setting up globally disabled telemetry")
|
175
190
|
handle_globally_disabled_setup()
|
176
191
|
else:
|
177
|
-
|
178
|
-
|
179
|
-
)
|
192
|
+
core_setup_logger.trace("Configuring structlog output processors")
|
193
|
+
configure_structlog_output(current_config, get_log_stream())
|
180
194
|
|
181
195
|
foundation_logger._is_configured_by_setup = is_explicit_call
|
182
196
|
foundation_logger._active_config = current_config
|
@@ -15,6 +15,8 @@ from provide.foundation.logger.processors import (
|
|
15
15
|
_build_core_processors_list,
|
16
16
|
_build_formatter_processors_list,
|
17
17
|
)
|
18
|
+
|
19
|
+
|
18
20
|
def build_complete_processor_chain(
|
19
21
|
config: TelemetryConfig,
|
20
22
|
log_stream: TextIO,
|
@@ -61,9 +63,7 @@ def configure_structlog_output(
|
|
61
63
|
config: Telemetry configuration
|
62
64
|
log_stream: Output stream for logging
|
63
65
|
"""
|
64
|
-
processors = build_complete_processor_chain(
|
65
|
-
config, log_stream
|
66
|
-
)
|
66
|
+
processors = build_complete_processor_chain(config, log_stream)
|
67
67
|
apply_structlog_configuration(processors, log_stream)
|
68
68
|
|
69
69
|
|
@@ -30,6 +30,20 @@ def reset_foundation_state() -> None:
|
|
30
30
|
foundation_logger._active_config = None
|
31
31
|
foundation_logger._active_resolved_emoji_config = None
|
32
32
|
_LAZY_SETUP_STATE.update({"done": False, "error": None, "in_progress": False})
|
33
|
+
|
34
|
+
# Reset event set registry and discovery state
|
35
|
+
try:
|
36
|
+
from provide.foundation.eventsets.registry import clear_registry
|
37
|
+
clear_registry()
|
38
|
+
except ImportError:
|
39
|
+
pass # Event sets may not be available in all test environments
|
40
|
+
|
41
|
+
# Reset setup logger cache
|
42
|
+
try:
|
43
|
+
from provide.foundation.logger.setup.coordinator import reset_setup_logger_cache
|
44
|
+
reset_setup_logger_cache()
|
45
|
+
except ImportError:
|
46
|
+
pass
|
33
47
|
|
34
48
|
|
35
49
|
def reset_foundation_setup_for_testing() -> None:
|
@@ -39,10 +39,10 @@ if not hasattr(stdlib_logging, TRACE_LEVEL_NAME): # pragma: no cover
|
|
39
39
|
|
40
40
|
# Also patch PrintLogger from structlog to support trace method
|
41
41
|
try:
|
42
|
-
import structlog
|
43
42
|
from structlog import PrintLogger
|
44
|
-
|
43
|
+
|
45
44
|
if not hasattr(PrintLogger, "trace"): # pragma: no cover
|
45
|
+
|
46
46
|
def trace_for_print_logger(
|
47
47
|
self: PrintLogger, msg: object, *args: object, **kwargs: object
|
48
48
|
) -> None: # pragma: no cover
|
@@ -54,12 +54,12 @@ try:
|
|
54
54
|
formatted_msg = f"{msg} {args}"
|
55
55
|
else:
|
56
56
|
formatted_msg = str(msg)
|
57
|
-
|
57
|
+
|
58
58
|
# Use the same output mechanism as other PrintLogger methods
|
59
59
|
self._file.write(formatted_msg + "\n")
|
60
60
|
self._file.flush()
|
61
|
-
|
61
|
+
|
62
62
|
PrintLogger.trace = trace_for_print_logger # type: ignore[attr-defined]
|
63
|
-
|
63
|
+
|
64
64
|
except ImportError: # pragma: no cover
|
65
65
|
pass
|
@@ -113,7 +113,7 @@ def histogram(name: str, description: str = "", unit: str = "") -> "SimpleHistog
|
|
113
113
|
return SimpleHistogram(name)
|
114
114
|
|
115
115
|
|
116
|
-
def _set_meter(meter) -> None:
|
116
|
+
def _set_meter(meter: object) -> None:
|
117
117
|
"""Set the global meter instance (internal use only)."""
|
118
118
|
global _meter
|
119
119
|
_meter = meter
|
@@ -52,7 +52,9 @@ def setup_opentelemetry_metrics(config: TelemetryConfig) -> None:
|
|
52
52
|
|
53
53
|
# Check if OpenTelemetry metrics are available
|
54
54
|
if not _HAS_OTEL_METRICS:
|
55
|
-
slog.debug(
|
55
|
+
slog.debug(
|
56
|
+
"📊 OpenTelemetry metrics not available (dependencies not installed)"
|
57
|
+
)
|
56
58
|
return
|
57
59
|
|
58
60
|
slog.debug("📊🚀 Setting up OpenTelemetry metrics")
|
@@ -17,7 +17,7 @@ except ImportError:
|
|
17
17
|
# Only import OpenObserve if OpenTelemetry is available
|
18
18
|
if _HAS_OTEL:
|
19
19
|
try:
|
20
|
-
from provide.foundation.
|
20
|
+
from provide.foundation.integrations.openobserve import (
|
21
21
|
OpenObserveClient,
|
22
22
|
search_logs,
|
23
23
|
stream_logs,
|
@@ -25,9 +25,9 @@ if _HAS_OTEL:
|
|
25
25
|
|
26
26
|
# Commands will auto-register if click is available
|
27
27
|
try:
|
28
|
-
from provide.foundation.
|
28
|
+
from provide.foundation.integrations.openobserve.commands import (
|
29
29
|
openobserve_group,
|
30
|
-
)
|
30
|
+
) # noqa: F401
|
31
31
|
except ImportError:
|
32
32
|
# Click not available, skip command registration
|
33
33
|
pass
|
@@ -10,6 +10,11 @@ from provide.foundation.process.async_runner import (
|
|
10
10
|
async_run_shell,
|
11
11
|
async_stream_command,
|
12
12
|
)
|
13
|
+
from provide.foundation.process.exit import (
|
14
|
+
exit_error,
|
15
|
+
exit_interrupted,
|
16
|
+
exit_success,
|
17
|
+
)
|
13
18
|
from provide.foundation.process.lifecycle import (
|
14
19
|
ManagedProcess,
|
15
20
|
wait_for_process_output,
|
@@ -36,4 +41,8 @@ __all__ = [
|
|
36
41
|
# Process lifecycle management
|
37
42
|
"ManagedProcess",
|
38
43
|
"wait_for_process_output",
|
44
|
+
# Exit utilities
|
45
|
+
"exit_success",
|
46
|
+
"exit_error",
|
47
|
+
"exit_interrupted",
|
39
48
|
]
|
@@ -0,0 +1,48 @@
|
|
1
|
+
"""Process exit utilities for standardized exit handling."""
|
2
|
+
|
3
|
+
import sys
|
4
|
+
|
5
|
+
from provide.foundation.config.defaults import EXIT_ERROR, EXIT_SIGINT, EXIT_SUCCESS
|
6
|
+
|
7
|
+
|
8
|
+
def _get_logger():
|
9
|
+
"""Get logger instance lazily to avoid circular imports."""
|
10
|
+
from provide.foundation.logger import logger
|
11
|
+
|
12
|
+
return logger
|
13
|
+
|
14
|
+
|
15
|
+
def exit_success(message: str | None = None) -> None:
|
16
|
+
"""Exit with success status.
|
17
|
+
|
18
|
+
Args:
|
19
|
+
message: Optional message to log before exiting
|
20
|
+
"""
|
21
|
+
if message:
|
22
|
+
logger = _get_logger()
|
23
|
+
logger.info(f"Exiting successfully: {message}")
|
24
|
+
sys.exit(EXIT_SUCCESS)
|
25
|
+
|
26
|
+
|
27
|
+
def exit_error(message: str | None = None, code: int = EXIT_ERROR) -> None:
|
28
|
+
"""Exit with error status.
|
29
|
+
|
30
|
+
Args:
|
31
|
+
message: Optional error message to log before exiting
|
32
|
+
code: Exit code to use (defaults to EXIT_ERROR)
|
33
|
+
"""
|
34
|
+
if message:
|
35
|
+
logger = _get_logger()
|
36
|
+
logger.error(f"Exiting with error: {message}", exit_code=code)
|
37
|
+
sys.exit(code)
|
38
|
+
|
39
|
+
|
40
|
+
def exit_interrupted(message: str = "Process interrupted") -> None:
|
41
|
+
"""Exit due to interrupt signal (SIGINT).
|
42
|
+
|
43
|
+
Args:
|
44
|
+
message: Message to log before exiting
|
45
|
+
"""
|
46
|
+
logger = _get_logger()
|
47
|
+
logger.warning(f"Exiting due to interrupt: {message}")
|
48
|
+
sys.exit(EXIT_SIGINT)
|