provide-foundation 0.0.0.dev0__py3-none-any.whl → 0.0.0.dev2__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 +41 -23
- 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 +334 -0
- provide/foundation/archive/tar.py +164 -0
- provide/foundation/archive/zip.py +203 -0
- provide/foundation/cli/__init__.py +2 -2
- provide/foundation/cli/commands/deps.py +13 -7
- provide/foundation/cli/commands/logs/__init__.py +1 -1
- provide/foundation/cli/commands/logs/query.py +1 -1
- provide/foundation/cli/commands/logs/send.py +1 -1
- provide/foundation/cli/commands/logs/tail.py +1 -1
- provide/foundation/cli/decorators.py +11 -10
- provide/foundation/cli/main.py +1 -1
- provide/foundation/cli/testing.py +2 -35
- provide/foundation/cli/utils.py +21 -17
- provide/foundation/config/__init__.py +35 -2
- provide/foundation/config/base.py +2 -2
- provide/foundation/config/converters.py +479 -0
- provide/foundation/config/defaults.py +67 -0
- provide/foundation/config/env.py +4 -19
- provide/foundation/config/loader.py +9 -3
- provide/foundation/config/sync.py +19 -4
- provide/foundation/console/input.py +5 -5
- provide/foundation/console/output.py +35 -13
- provide/foundation/context/__init__.py +8 -4
- provide/foundation/context/core.py +85 -109
- 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/errors/__init__.py +2 -3
- provide/foundation/errors/decorators.py +0 -231
- provide/foundation/errors/types.py +0 -97
- 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/file/directory.py +13 -22
- provide/foundation/file/lock.py +3 -1
- provide/foundation/hub/components.py +77 -515
- provide/foundation/hub/config.py +151 -0
- provide/foundation/hub/discovery.py +62 -0
- provide/foundation/hub/handlers.py +81 -0
- provide/foundation/hub/lifecycle.py +194 -0
- provide/foundation/hub/manager.py +4 -4
- provide/foundation/hub/processors.py +44 -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 +12 -12
- provide/foundation/{observability → integrations}/openobserve/commands.py +3 -3
- provide/foundation/integrations/openobserve/config.py +37 -0
- provide/foundation/{observability → integrations}/openobserve/formatters.py +1 -1
- provide/foundation/{observability → integrations}/openobserve/otlp.py +1 -1
- provide/foundation/{observability → integrations}/openobserve/search.py +2 -2
- provide/foundation/{observability → integrations}/openobserve/streaming.py +4 -4
- provide/foundation/logger/__init__.py +3 -10
- provide/foundation/logger/config/logging.py +68 -298
- provide/foundation/logger/config/telemetry.py +41 -121
- 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 +76 -24
- provide/foundation/logger/setup/processors.py +2 -9
- provide/foundation/logger/trace.py +27 -0
- provide/foundation/metrics/otel.py +10 -10
- provide/foundation/observability/__init__.py +2 -2
- provide/foundation/process/__init__.py +9 -0
- provide/foundation/process/exit.py +47 -0
- provide/foundation/process/lifecycle.py +115 -59
- provide/foundation/resilience/__init__.py +35 -0
- provide/foundation/resilience/circuit.py +164 -0
- provide/foundation/resilience/decorators.py +220 -0
- provide/foundation/resilience/fallback.py +193 -0
- provide/foundation/resilience/retry.py +325 -0
- provide/foundation/streams/config.py +79 -0
- provide/foundation/streams/console.py +7 -8
- provide/foundation/streams/core.py +6 -3
- provide/foundation/streams/file.py +12 -2
- provide/foundation/testing/__init__.py +84 -2
- provide/foundation/testing/archive/__init__.py +24 -0
- provide/foundation/testing/archive/fixtures.py +217 -0
- provide/foundation/testing/cli.py +30 -17
- provide/foundation/testing/common/__init__.py +32 -0
- provide/foundation/testing/common/fixtures.py +236 -0
- provide/foundation/testing/file/__init__.py +40 -0
- provide/foundation/testing/file/content_fixtures.py +316 -0
- provide/foundation/testing/file/directory_fixtures.py +107 -0
- provide/foundation/testing/file/fixtures.py +52 -0
- provide/foundation/testing/file/special_fixtures.py +153 -0
- provide/foundation/testing/logger.py +117 -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/async_fixtures.py +405 -0
- provide/foundation/testing/process/fixtures.py +56 -0
- provide/foundation/testing/process/subprocess_fixtures.py +209 -0
- provide/foundation/testing/threading/__init__.py +38 -0
- provide/foundation/testing/threading/basic_fixtures.py +101 -0
- provide/foundation/testing/threading/data_fixtures.py +99 -0
- provide/foundation/testing/threading/execution_fixtures.py +263 -0
- provide/foundation/testing/threading/fixtures.py +54 -0
- provide/foundation/testing/threading/sync_fixtures.py +97 -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 +268 -0
- provide/foundation/tools/downloader.py +224 -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/tracer/spans.py +2 -2
- 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 +140 -0
- provide/foundation/transport/errors.py +79 -0
- provide/foundation/transport/http.py +232 -0
- provide/foundation/transport/middleware.py +360 -0
- provide/foundation/transport/registry.py +167 -0
- provide/foundation/transport/types.py +45 -0
- provide/foundation/utils/deps.py +14 -12
- provide/foundation/utils/parsing.py +49 -4
- {provide_foundation-0.0.0.dev0.dist-info → provide_foundation-0.0.0.dev2.dist-info}/METADATA +5 -28
- provide_foundation-0.0.0.dev2.dist-info/RECORD +225 -0
- 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/RECORD +0 -149
- /provide/foundation/{observability → integrations}/openobserve/exceptions.py +0 -0
- /provide/foundation/{observability → integrations}/openobserve/models.py +0 -0
- {provide_foundation-0.0.0.dev0.dist-info → provide_foundation-0.0.0.dev2.dist-info}/WHEEL +0 -0
- {provide_foundation-0.0.0.dev0.dist-info → provide_foundation-0.0.0.dev2.dist-info}/entry_points.txt +0 -0
- {provide_foundation-0.0.0.dev0.dist-info → provide_foundation-0.0.0.dev2.dist-info}/licenses/LICENSE +0 -0
- {provide_foundation-0.0.0.dev0.dist-info → provide_foundation-0.0.0.dev2.dist-info}/top_level.txt +0 -0
@@ -12,16 +12,16 @@ from requests.adapters import HTTPAdapter
|
|
12
12
|
from urllib3.util.retry import Retry
|
13
13
|
|
14
14
|
from provide.foundation.logger import get_logger
|
15
|
-
from provide.foundation.
|
15
|
+
from provide.foundation.integrations.openobserve.auth import (
|
16
16
|
get_auth_headers,
|
17
17
|
validate_credentials,
|
18
18
|
)
|
19
|
-
from provide.foundation.
|
19
|
+
from provide.foundation.integrations.openobserve.exceptions import (
|
20
20
|
OpenObserveConfigError,
|
21
21
|
OpenObserveConnectionError,
|
22
22
|
OpenObserveQueryError,
|
23
23
|
)
|
24
|
-
from provide.foundation.
|
24
|
+
from provide.foundation.integrations.openobserve.models import (
|
25
25
|
SearchQuery,
|
26
26
|
SearchResponse,
|
27
27
|
StreamInfo,
|
@@ -74,7 +74,7 @@ class OpenObserveClient:
|
|
74
74
|
|
75
75
|
@classmethod
|
76
76
|
def from_config(cls) -> "OpenObserveClient":
|
77
|
-
"""Create client from
|
77
|
+
"""Create client from OpenObserveConfig.
|
78
78
|
|
79
79
|
Returns:
|
80
80
|
Configured OpenObserveClient instance
|
@@ -82,26 +82,26 @@ class OpenObserveClient:
|
|
82
82
|
Raises:
|
83
83
|
OpenObserveConfigError: If configuration is missing
|
84
84
|
"""
|
85
|
-
from provide.foundation.
|
85
|
+
from provide.foundation.integrations.openobserve.config import OpenObserveConfig
|
86
86
|
|
87
|
-
config =
|
87
|
+
config = OpenObserveConfig.from_env()
|
88
88
|
|
89
|
-
if not config.
|
89
|
+
if not config.url:
|
90
90
|
raise OpenObserveConfigError(
|
91
91
|
"OpenObserve URL not configured. Set OPENOBSERVE_URL environment variable."
|
92
92
|
)
|
93
93
|
|
94
|
-
if not config.
|
94
|
+
if not config.user or not config.password:
|
95
95
|
raise OpenObserveConfigError(
|
96
96
|
"OpenObserve credentials not configured. "
|
97
97
|
"Set OPENOBSERVE_USER and OPENOBSERVE_PASSWORD environment variables."
|
98
98
|
)
|
99
99
|
|
100
100
|
return cls(
|
101
|
-
url=config.
|
102
|
-
username=config.
|
103
|
-
password=config.
|
104
|
-
organization=config.
|
101
|
+
url=config.url,
|
102
|
+
username=config.user,
|
103
|
+
password=config.password,
|
104
|
+
organization=config.org or "default",
|
105
105
|
)
|
106
106
|
|
107
107
|
def _make_request(
|
@@ -18,7 +18,7 @@ log = get_logger(__name__)
|
|
18
18
|
|
19
19
|
|
20
20
|
if _HAS_CLICK:
|
21
|
-
from provide.foundation.
|
21
|
+
from provide.foundation.integrations.openobserve import (
|
22
22
|
OpenObserveClient,
|
23
23
|
format_output,
|
24
24
|
search_logs,
|
@@ -198,7 +198,7 @@ if _HAS_CLICK:
|
|
198
198
|
return 1
|
199
199
|
|
200
200
|
try:
|
201
|
-
from provide.foundation.
|
201
|
+
from provide.foundation.integrations.openobserve import search_errors
|
202
202
|
|
203
203
|
response = search_errors(
|
204
204
|
stream=stream,
|
@@ -240,7 +240,7 @@ if _HAS_CLICK:
|
|
240
240
|
return 1
|
241
241
|
|
242
242
|
try:
|
243
|
-
from provide.foundation.
|
243
|
+
from provide.foundation.integrations.openobserve import search_by_trace_id
|
244
244
|
|
245
245
|
response = search_by_trace_id(
|
246
246
|
trace_id=trace_id,
|
@@ -0,0 +1,37 @@
|
|
1
|
+
"""OpenObserve integration configuration."""
|
2
|
+
|
3
|
+
from attrs import define
|
4
|
+
|
5
|
+
from provide.foundation.config.env import RuntimeConfig
|
6
|
+
from provide.foundation.config.base import field
|
7
|
+
|
8
|
+
|
9
|
+
@define(slots=True, repr=False)
|
10
|
+
class OpenObserveConfig(RuntimeConfig):
|
11
|
+
"""Configuration for OpenObserve integration."""
|
12
|
+
|
13
|
+
url: str | None = field(
|
14
|
+
default=None,
|
15
|
+
env_var="OPENOBSERVE_URL",
|
16
|
+
description="OpenObserve URL endpoint",
|
17
|
+
)
|
18
|
+
org: str | None = field(
|
19
|
+
default=None,
|
20
|
+
env_var="OPENOBSERVE_ORG",
|
21
|
+
description="OpenObserve organization",
|
22
|
+
)
|
23
|
+
user: str | None = field(
|
24
|
+
default=None,
|
25
|
+
env_var="OPENOBSERVE_USER",
|
26
|
+
description="OpenObserve username",
|
27
|
+
)
|
28
|
+
password: str | None = field(
|
29
|
+
default=None,
|
30
|
+
env_var="OPENOBSERVE_PASSWORD",
|
31
|
+
description="OpenObserve password",
|
32
|
+
)
|
33
|
+
stream: str | None = field(
|
34
|
+
default=None,
|
35
|
+
env_var="OPENOBSERVE_STREAM",
|
36
|
+
description="OpenObserve stream name",
|
37
|
+
)
|
@@ -8,7 +8,7 @@ import io
|
|
8
8
|
import json
|
9
9
|
from typing import Any
|
10
10
|
|
11
|
-
from provide.foundation.
|
11
|
+
from provide.foundation.integrations.openobserve.models import SearchResponse
|
12
12
|
|
13
13
|
|
14
14
|
def format_json(response: SearchResponse | dict[str, Any], pretty: bool = True) -> str:
|
@@ -7,7 +7,7 @@ import json
|
|
7
7
|
from typing import Any
|
8
8
|
|
9
9
|
from provide.foundation.logger import get_logger
|
10
|
-
from provide.foundation.
|
10
|
+
from provide.foundation.integrations.openobserve.client import OpenObserveClient
|
11
11
|
|
12
12
|
log = get_logger(__name__)
|
13
13
|
|
@@ -4,8 +4,8 @@ Search operations for OpenObserve.
|
|
4
4
|
|
5
5
|
|
6
6
|
from provide.foundation.logger import get_logger
|
7
|
-
from provide.foundation.
|
8
|
-
from provide.foundation.
|
7
|
+
from provide.foundation.integrations.openobserve.client import OpenObserveClient
|
8
|
+
from provide.foundation.integrations.openobserve.models import SearchResponse
|
9
9
|
|
10
10
|
log = get_logger(__name__)
|
11
11
|
|
@@ -10,12 +10,12 @@ from typing import Any
|
|
10
10
|
import requests
|
11
11
|
|
12
12
|
from provide.foundation.logger import get_logger
|
13
|
-
from provide.foundation.
|
14
|
-
from provide.foundation.
|
15
|
-
from provide.foundation.
|
13
|
+
from provide.foundation.integrations.openobserve.auth import get_auth_headers
|
14
|
+
from provide.foundation.integrations.openobserve.client import OpenObserveClient
|
15
|
+
from provide.foundation.integrations.openobserve.exceptions import (
|
16
16
|
OpenObserveStreamingError,
|
17
17
|
)
|
18
|
-
from provide.foundation.
|
18
|
+
from provide.foundation.integrations.openobserve.models import parse_relative_time
|
19
19
|
|
20
20
|
log = get_logger(__name__)
|
21
21
|
|
@@ -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
|
# 🐍📝
|
@@ -5,390 +5,160 @@
|
|
5
5
|
LoggingConfig class for Foundation logger configuration.
|
6
6
|
"""
|
7
7
|
|
8
|
-
import json
|
9
|
-
import os
|
10
8
|
from pathlib import Path
|
11
9
|
|
12
10
|
from attrs import define
|
13
11
|
|
14
|
-
from provide.foundation.config import
|
15
|
-
from provide.foundation.config.
|
16
|
-
from provide.foundation.
|
17
|
-
|
12
|
+
from provide.foundation.config.env import RuntimeConfig
|
13
|
+
from provide.foundation.config.base import field
|
14
|
+
from provide.foundation.config.defaults import (
|
15
|
+
DEFAULT_LOG_LEVEL,
|
16
|
+
DEFAULT_CONSOLE_FORMATTER,
|
17
|
+
DEFAULT_LOGGER_NAME_EMOJI_ENABLED,
|
18
|
+
DEFAULT_DAS_EMOJI_ENABLED,
|
19
|
+
DEFAULT_OMIT_TIMESTAMP,
|
20
|
+
DEFAULT_FOUNDATION_SETUP_LOG_LEVEL,
|
21
|
+
DEFAULT_FOUNDATION_LOG_OUTPUT,
|
22
|
+
DEFAULT_RATE_LIMIT_ENABLED,
|
23
|
+
DEFAULT_RATE_LIMIT_EMIT_WARNINGS,
|
24
|
+
DEFAULT_RATE_LIMIT_GLOBAL,
|
25
|
+
DEFAULT_RATE_LIMIT_GLOBAL_CAPACITY,
|
26
|
+
DEFAULT_RATE_LIMIT_OVERFLOW_POLICY,
|
27
|
+
)
|
28
|
+
from provide.foundation.config.converters import (
|
29
|
+
parse_bool_extended,
|
30
|
+
parse_console_formatter,
|
31
|
+
parse_float_with_validation,
|
32
|
+
parse_foundation_log_output,
|
33
|
+
parse_log_level,
|
34
|
+
parse_module_levels,
|
35
|
+
parse_rate_limits,
|
36
|
+
validate_log_level,
|
37
|
+
validate_non_negative,
|
38
|
+
validate_overflow_policy,
|
39
|
+
validate_positive,
|
40
|
+
)
|
18
41
|
from provide.foundation.types import (
|
19
|
-
_VALID_FORMATTER_TUPLE,
|
20
|
-
_VALID_LOG_LEVEL_TUPLE,
|
21
42
|
ConsoleFormatterStr,
|
22
43
|
LogLevelStr,
|
23
44
|
)
|
24
45
|
|
25
46
|
|
26
47
|
@define(slots=True, repr=False)
|
27
|
-
class LoggingConfig(
|
48
|
+
class LoggingConfig(RuntimeConfig):
|
28
49
|
"""Configuration specific to logging behavior within Foundation Telemetry."""
|
29
50
|
|
30
51
|
default_level: LogLevelStr = field(
|
31
|
-
default=
|
52
|
+
default=DEFAULT_LOG_LEVEL,
|
32
53
|
env_var="PROVIDE_LOG_LEVEL",
|
54
|
+
converter=parse_log_level,
|
55
|
+
validator=validate_log_level,
|
33
56
|
description="Default logging level",
|
34
57
|
)
|
35
58
|
module_levels: dict[str, LogLevelStr] = field(
|
36
59
|
factory=lambda: {},
|
37
60
|
env_var="PROVIDE_LOG_MODULE_LEVELS",
|
61
|
+
converter=parse_module_levels,
|
38
62
|
description="Per-module log levels (format: module1:LEVEL,module2:LEVEL)",
|
39
63
|
)
|
40
64
|
console_formatter: ConsoleFormatterStr = field(
|
41
|
-
default=
|
65
|
+
default=DEFAULT_CONSOLE_FORMATTER,
|
42
66
|
env_var="PROVIDE_LOG_CONSOLE_FORMATTER",
|
67
|
+
converter=parse_console_formatter,
|
43
68
|
description="Console output formatter (key_value or json)",
|
44
69
|
)
|
45
70
|
logger_name_emoji_prefix_enabled: bool = field(
|
46
|
-
default=
|
71
|
+
default=DEFAULT_LOGGER_NAME_EMOJI_ENABLED,
|
47
72
|
env_var="PROVIDE_LOG_LOGGER_NAME_EMOJI_ENABLED",
|
73
|
+
converter=parse_bool_extended,
|
48
74
|
description="Enable emoji prefixes based on logger names",
|
49
75
|
)
|
50
76
|
das_emoji_prefix_enabled: bool = field(
|
51
|
-
default=
|
77
|
+
default=DEFAULT_DAS_EMOJI_ENABLED,
|
52
78
|
env_var="PROVIDE_LOG_DAS_EMOJI_ENABLED",
|
79
|
+
converter=parse_bool_extended,
|
53
80
|
description="Enable Domain-Action-Status emoji prefixes",
|
54
81
|
)
|
55
82
|
omit_timestamp: bool = field(
|
56
|
-
default=
|
83
|
+
default=DEFAULT_OMIT_TIMESTAMP,
|
57
84
|
env_var="PROVIDE_LOG_OMIT_TIMESTAMP",
|
85
|
+
converter=parse_bool_extended,
|
58
86
|
description="Omit timestamps from console output",
|
59
87
|
)
|
60
|
-
|
61
|
-
factory=lambda: [],
|
62
|
-
env_var="PROVIDE_LOG_ENABLED_EMOJI_SETS",
|
63
|
-
description="Comma-separated list of emoji sets to enable",
|
64
|
-
)
|
65
|
-
custom_emoji_sets: list[EmojiSetConfig] = field(
|
66
|
-
factory=lambda: [],
|
67
|
-
env_var="PROVIDE_LOG_CUSTOM_EMOJI_SETS",
|
68
|
-
description="JSON array of custom emoji set configurations",
|
69
|
-
)
|
70
|
-
user_defined_emoji_sets: list[EmojiSet] = field(
|
71
|
-
factory=lambda: [],
|
72
|
-
env_var="PROVIDE_LOG_USER_DEFINED_EMOJI_SETS",
|
73
|
-
description="JSON array of user-defined emoji sets",
|
74
|
-
)
|
88
|
+
# Event sets have replaced emoji sets - these fields are deprecated
|
75
89
|
log_file: Path | None = field(
|
76
|
-
default=None,
|
90
|
+
default=None,
|
91
|
+
env_var="PROVIDE_LOG_FILE",
|
92
|
+
converter=lambda x: Path(x) if x else None,
|
93
|
+
description="Path to log file"
|
77
94
|
)
|
78
95
|
foundation_setup_log_level: LogLevelStr = field(
|
79
|
-
default=
|
96
|
+
default=DEFAULT_FOUNDATION_SETUP_LOG_LEVEL,
|
80
97
|
env_var="FOUNDATION_LOG_LEVEL",
|
98
|
+
converter=parse_log_level,
|
99
|
+
validator=validate_log_level,
|
81
100
|
description="Log level for Foundation internal setup messages",
|
82
101
|
)
|
83
102
|
foundation_log_output: str = field(
|
84
|
-
default=
|
103
|
+
default=DEFAULT_FOUNDATION_LOG_OUTPUT,
|
85
104
|
env_var="FOUNDATION_LOG_OUTPUT",
|
105
|
+
converter=parse_foundation_log_output,
|
86
106
|
description="Output destination for Foundation internal messages (stderr, stdout, main)",
|
87
107
|
)
|
88
|
-
show_emoji_matrix: bool = field(
|
89
|
-
default=False,
|
90
|
-
env_var="PROVIDE_SHOW_EMOJI_MATRIX",
|
91
|
-
description="Whether to display emoji matrix on startup",
|
92
|
-
)
|
93
108
|
rate_limit_enabled: bool = field(
|
94
|
-
default=
|
109
|
+
default=DEFAULT_RATE_LIMIT_ENABLED,
|
95
110
|
env_var="PROVIDE_LOG_RATE_LIMIT_ENABLED",
|
111
|
+
converter=parse_bool_extended,
|
96
112
|
description="Enable rate limiting for log output",
|
97
113
|
)
|
98
114
|
rate_limit_global: float | None = field(
|
99
115
|
default=None,
|
100
116
|
env_var="PROVIDE_LOG_RATE_LIMIT_GLOBAL",
|
117
|
+
converter=lambda x: parse_float_with_validation(x, min_val=0.0) if x else None,
|
101
118
|
description="Global rate limit (logs per second)",
|
102
119
|
)
|
103
120
|
rate_limit_global_capacity: float | None = field(
|
104
121
|
default=None,
|
105
122
|
env_var="PROVIDE_LOG_RATE_LIMIT_GLOBAL_CAPACITY",
|
123
|
+
converter=lambda x: parse_float_with_validation(x, min_val=0.0) if x else None,
|
106
124
|
description="Global rate limit burst capacity",
|
107
125
|
)
|
108
126
|
rate_limit_per_logger: dict[str, tuple[float, float]] = field(
|
109
127
|
factory=lambda: {},
|
110
128
|
env_var="PROVIDE_LOG_RATE_LIMIT_PER_LOGGER",
|
129
|
+
converter=parse_rate_limits,
|
111
130
|
description="Per-logger rate limits (format: logger1:rate:capacity,logger2:rate:capacity)",
|
112
131
|
)
|
113
132
|
rate_limit_emit_warnings: bool = field(
|
114
|
-
default=
|
133
|
+
default=DEFAULT_RATE_LIMIT_EMIT_WARNINGS,
|
115
134
|
env_var="PROVIDE_LOG_RATE_LIMIT_EMIT_WARNINGS",
|
135
|
+
converter=parse_bool_extended,
|
116
136
|
description="Emit warnings when logs are rate limited",
|
117
137
|
)
|
118
138
|
rate_limit_summary_interval: float = field(
|
119
|
-
default=
|
139
|
+
default=DEFAULT_RATE_LIMIT_GLOBAL,
|
120
140
|
env_var="PROVIDE_LOG_RATE_LIMIT_SUMMARY_INTERVAL",
|
141
|
+
converter=lambda x: parse_float_with_validation(x, min_val=0.0) if x else DEFAULT_RATE_LIMIT_GLOBAL,
|
142
|
+
validator=validate_positive,
|
121
143
|
description="Seconds between rate limit summary reports",
|
122
144
|
)
|
123
145
|
rate_limit_max_queue_size: int = field(
|
124
|
-
default=
|
146
|
+
default=DEFAULT_RATE_LIMIT_GLOBAL_CAPACITY,
|
125
147
|
env_var="PROVIDE_LOG_RATE_LIMIT_MAX_QUEUE_SIZE",
|
148
|
+
converter=int,
|
149
|
+
validator=validate_positive,
|
126
150
|
description="Maximum number of logs to queue when rate limited",
|
127
151
|
)
|
128
152
|
rate_limit_max_memory_mb: float | None = field(
|
129
153
|
default=None,
|
130
154
|
env_var="PROVIDE_LOG_RATE_LIMIT_MAX_MEMORY_MB",
|
155
|
+
converter=lambda x: parse_float_with_validation(x, min_val=0.0) if x else None,
|
131
156
|
description="Maximum memory (MB) for queued logs",
|
132
157
|
)
|
133
158
|
rate_limit_overflow_policy: str = field(
|
134
|
-
default=
|
159
|
+
default=DEFAULT_RATE_LIMIT_OVERFLOW_POLICY,
|
135
160
|
env_var="PROVIDE_LOG_RATE_LIMIT_OVERFLOW_POLICY",
|
161
|
+
validator=validate_overflow_policy,
|
136
162
|
description="Policy when queue is full: drop_oldest, drop_newest, or block",
|
137
163
|
)
|
138
164
|
|
139
|
-
@classmethod
|
140
|
-
def from_env(cls, strict: bool = True) -> "LoggingConfig":
|
141
|
-
"""Load LoggingConfig from environment variables."""
|
142
|
-
config_dict = {}
|
143
|
-
|
144
|
-
# Parse standard fields
|
145
|
-
if level := os.getenv("PROVIDE_LOG_LEVEL"):
|
146
|
-
level = level.upper()
|
147
|
-
if level in _VALID_LOG_LEVEL_TUPLE:
|
148
|
-
config_dict["default_level"] = level
|
149
|
-
elif strict:
|
150
|
-
get_config_logger().warning(
|
151
|
-
"[Foundation Config Warning] Invalid configuration value, using default",
|
152
|
-
config_key="PROVIDE_LOG_LEVEL",
|
153
|
-
invalid_value=level,
|
154
|
-
valid_options=list(_VALID_LOG_LEVEL_TUPLE),
|
155
|
-
default_value="DEBUG",
|
156
|
-
)
|
157
|
-
|
158
|
-
if formatter := os.getenv("PROVIDE_LOG_CONSOLE_FORMATTER"):
|
159
|
-
formatter = formatter.lower()
|
160
|
-
if formatter in _VALID_FORMATTER_TUPLE:
|
161
|
-
config_dict["console_formatter"] = formatter
|
162
|
-
elif strict:
|
163
|
-
get_config_logger().warning(
|
164
|
-
"[Foundation Config Warning] Invalid configuration value, using default",
|
165
|
-
config_key="PROVIDE_LOG_CONSOLE_FORMATTER",
|
166
|
-
invalid_value=formatter,
|
167
|
-
valid_options=list(_VALID_FORMATTER_TUPLE),
|
168
|
-
default_value="key_value",
|
169
|
-
)
|
170
|
-
|
171
|
-
if omit_ts := os.getenv("PROVIDE_LOG_OMIT_TIMESTAMP"):
|
172
|
-
config_dict["omit_timestamp"] = omit_ts.lower() == "true"
|
173
|
-
|
174
|
-
if logger_emoji := os.getenv("PROVIDE_LOG_LOGGER_NAME_EMOJI_ENABLED"):
|
175
|
-
config_dict["logger_name_emoji_prefix_enabled"] = (
|
176
|
-
logger_emoji.lower() == "true"
|
177
|
-
)
|
178
|
-
|
179
|
-
if das_emoji := os.getenv("PROVIDE_LOG_DAS_EMOJI_ENABLED"):
|
180
|
-
config_dict["das_emoji_prefix_enabled"] = das_emoji.lower() == "true"
|
181
|
-
|
182
|
-
if log_file := os.getenv("PROVIDE_LOG_FILE"):
|
183
|
-
config_dict["log_file"] = Path(log_file)
|
184
|
-
|
185
|
-
if foundation_level := os.getenv("FOUNDATION_LOG_LEVEL"):
|
186
|
-
foundation_level = foundation_level.upper()
|
187
|
-
if foundation_level in _VALID_LOG_LEVEL_TUPLE:
|
188
|
-
config_dict["foundation_setup_log_level"] = foundation_level
|
189
|
-
elif strict:
|
190
|
-
get_config_logger().warning(
|
191
|
-
"[Foundation Config Warning] Invalid configuration value, using default",
|
192
|
-
config_key="FOUNDATION_LOG_LEVEL",
|
193
|
-
invalid_value=foundation_level,
|
194
|
-
valid_options=list(_VALID_LOG_LEVEL_TUPLE),
|
195
|
-
default_value="INFO",
|
196
|
-
)
|
197
|
-
|
198
|
-
if foundation_output := os.getenv("FOUNDATION_LOG_OUTPUT"):
|
199
|
-
foundation_output = foundation_output.lower()
|
200
|
-
valid_outputs = ("stderr", "stdout", "main")
|
201
|
-
if foundation_output in valid_outputs:
|
202
|
-
config_dict["foundation_log_output"] = foundation_output
|
203
|
-
elif strict:
|
204
|
-
get_config_logger().warning(
|
205
|
-
"[Foundation Config Warning] Invalid configuration value, using default",
|
206
|
-
config_key="FOUNDATION_LOG_OUTPUT",
|
207
|
-
invalid_value=foundation_output,
|
208
|
-
valid_options=list(valid_outputs),
|
209
|
-
default_value="stderr",
|
210
|
-
)
|
211
|
-
|
212
|
-
if show_matrix := os.getenv("PROVIDE_SHOW_EMOJI_MATRIX"):
|
213
|
-
config_dict["show_emoji_matrix"] = show_matrix.strip().lower() in (
|
214
|
-
"true",
|
215
|
-
"1",
|
216
|
-
"yes",
|
217
|
-
)
|
218
|
-
|
219
|
-
# Parse rate limiting configuration
|
220
|
-
if rate_limit_enabled := os.getenv("PROVIDE_LOG_RATE_LIMIT_ENABLED"):
|
221
|
-
config_dict["rate_limit_enabled"] = rate_limit_enabled.lower() == "true"
|
222
|
-
|
223
|
-
if rate_limit_global := os.getenv("PROVIDE_LOG_RATE_LIMIT_GLOBAL"):
|
224
|
-
try:
|
225
|
-
config_dict["rate_limit_global"] = float(rate_limit_global)
|
226
|
-
except ValueError:
|
227
|
-
if strict:
|
228
|
-
get_config_logger().warning(
|
229
|
-
"[Foundation Config Warning] Invalid rate limit value",
|
230
|
-
config_key="PROVIDE_LOG_RATE_LIMIT_GLOBAL",
|
231
|
-
invalid_value=rate_limit_global,
|
232
|
-
)
|
233
|
-
|
234
|
-
if rate_limit_capacity := os.getenv("PROVIDE_LOG_RATE_LIMIT_GLOBAL_CAPACITY"):
|
235
|
-
try:
|
236
|
-
config_dict["rate_limit_global_capacity"] = float(rate_limit_capacity)
|
237
|
-
except ValueError:
|
238
|
-
if strict:
|
239
|
-
get_config_logger().warning(
|
240
|
-
"[Foundation Config Warning] Invalid rate limit capacity",
|
241
|
-
config_key="PROVIDE_LOG_RATE_LIMIT_GLOBAL_CAPACITY",
|
242
|
-
invalid_value=rate_limit_capacity,
|
243
|
-
)
|
244
|
-
|
245
|
-
if per_logger_limits := os.getenv("PROVIDE_LOG_RATE_LIMIT_PER_LOGGER"):
|
246
|
-
limits_dict = {}
|
247
|
-
for item in per_logger_limits.split(","):
|
248
|
-
parts = item.split(":")
|
249
|
-
if len(parts) == 3:
|
250
|
-
logger_name, rate, capacity = parts
|
251
|
-
try:
|
252
|
-
limits_dict[logger_name.strip()] = (
|
253
|
-
float(rate.strip()),
|
254
|
-
float(capacity.strip()),
|
255
|
-
)
|
256
|
-
except ValueError:
|
257
|
-
if strict:
|
258
|
-
get_config_logger().warning(
|
259
|
-
"[Foundation Config Warning] Invalid per-logger rate limit",
|
260
|
-
config_key="PROVIDE_LOG_RATE_LIMIT_PER_LOGGER",
|
261
|
-
invalid_item=item,
|
262
|
-
)
|
263
|
-
if limits_dict:
|
264
|
-
config_dict["rate_limit_per_logger"] = limits_dict
|
265
|
-
|
266
|
-
if emit_warnings := os.getenv("PROVIDE_LOG_RATE_LIMIT_EMIT_WARNINGS"):
|
267
|
-
config_dict["rate_limit_emit_warnings"] = emit_warnings.lower() == "true"
|
268
|
-
|
269
|
-
if summary_interval := os.getenv("PROVIDE_LOG_RATE_LIMIT_SUMMARY_INTERVAL"):
|
270
|
-
try:
|
271
|
-
config_dict["rate_limit_summary_interval"] = float(summary_interval)
|
272
|
-
except ValueError:
|
273
|
-
if strict:
|
274
|
-
get_config_logger().warning(
|
275
|
-
"[Foundation Config Warning] Invalid summary interval",
|
276
|
-
config_key="PROVIDE_LOG_RATE_LIMIT_SUMMARY_INTERVAL",
|
277
|
-
invalid_value=summary_interval,
|
278
|
-
)
|
279
|
-
|
280
|
-
if max_queue := os.getenv("PROVIDE_LOG_RATE_LIMIT_MAX_QUEUE_SIZE"):
|
281
|
-
try:
|
282
|
-
config_dict["rate_limit_max_queue_size"] = int(max_queue)
|
283
|
-
except ValueError:
|
284
|
-
if strict:
|
285
|
-
get_config_logger().warning(
|
286
|
-
"[Foundation Config Warning] Invalid max queue size",
|
287
|
-
config_key="PROVIDE_LOG_RATE_LIMIT_MAX_QUEUE_SIZE",
|
288
|
-
invalid_value=max_queue,
|
289
|
-
)
|
290
|
-
|
291
|
-
if max_memory := os.getenv("PROVIDE_LOG_RATE_LIMIT_MAX_MEMORY_MB"):
|
292
|
-
try:
|
293
|
-
config_dict["rate_limit_max_memory_mb"] = float(max_memory)
|
294
|
-
except ValueError:
|
295
|
-
if strict:
|
296
|
-
get_config_logger().warning(
|
297
|
-
"[Foundation Config Warning] Invalid max memory",
|
298
|
-
config_key="PROVIDE_LOG_RATE_LIMIT_MAX_MEMORY_MB",
|
299
|
-
invalid_value=max_memory,
|
300
|
-
)
|
301
|
-
|
302
|
-
if overflow_policy := os.getenv("PROVIDE_LOG_RATE_LIMIT_OVERFLOW_POLICY"):
|
303
|
-
valid_policies = ("drop_oldest", "drop_newest", "block")
|
304
|
-
if overflow_policy in valid_policies:
|
305
|
-
config_dict["rate_limit_overflow_policy"] = overflow_policy
|
306
|
-
elif strict:
|
307
|
-
get_config_logger().warning(
|
308
|
-
"[Foundation Config Warning] Invalid overflow policy",
|
309
|
-
config_key="PROVIDE_LOG_RATE_LIMIT_OVERFLOW_POLICY",
|
310
|
-
invalid_value=overflow_policy,
|
311
|
-
valid_options=list(valid_policies),
|
312
|
-
)
|
313
|
-
|
314
|
-
# Parse complex fields
|
315
|
-
if module_levels := os.getenv("PROVIDE_LOG_MODULE_LEVELS"):
|
316
|
-
levels_dict = {}
|
317
|
-
for item in module_levels.split(","):
|
318
|
-
if ":" in item:
|
319
|
-
module, level = item.split(":", 1)
|
320
|
-
module = module.strip()
|
321
|
-
level = level.strip().upper()
|
322
|
-
if module and level in _VALID_LOG_LEVEL_TUPLE:
|
323
|
-
levels_dict[module] = level
|
324
|
-
elif strict and module and level not in _VALID_LOG_LEVEL_TUPLE:
|
325
|
-
get_config_logger().warning(
|
326
|
-
"[Foundation Config Warning] Invalid module log level, skipping",
|
327
|
-
config_key="PROVIDE_LOG_MODULE_LEVELS",
|
328
|
-
module_name=module,
|
329
|
-
invalid_level=level,
|
330
|
-
valid_options=list(_VALID_LOG_LEVEL_TUPLE),
|
331
|
-
)
|
332
|
-
if levels_dict:
|
333
|
-
config_dict["module_levels"] = levels_dict
|
334
|
-
|
335
|
-
if emoji_sets := os.getenv("PROVIDE_LOG_ENABLED_EMOJI_SETS"):
|
336
|
-
config_dict["enabled_emoji_sets"] = [
|
337
|
-
s.strip() for s in emoji_sets.split(",") if s.strip()
|
338
|
-
]
|
339
|
-
|
340
|
-
if custom_sets := os.getenv("PROVIDE_LOG_CUSTOM_EMOJI_SETS"):
|
341
|
-
try:
|
342
|
-
parsed = json.loads(custom_sets)
|
343
|
-
if isinstance(parsed, list):
|
344
|
-
config_dict["custom_emoji_sets"] = [
|
345
|
-
EmojiSetConfig(**item) if isinstance(item, dict) else item
|
346
|
-
for item in parsed
|
347
|
-
]
|
348
|
-
except json.JSONDecodeError as e:
|
349
|
-
if strict:
|
350
|
-
get_config_logger().warning(
|
351
|
-
"[Foundation Config Warning] Invalid JSON in configuration",
|
352
|
-
config_key="PROVIDE_LOG_CUSTOM_EMOJI_SETS",
|
353
|
-
error=str(e),
|
354
|
-
config_value=custom_sets[:100] + "..."
|
355
|
-
if len(custom_sets) > 100
|
356
|
-
else custom_sets,
|
357
|
-
)
|
358
|
-
except (TypeError, ValueError) as e:
|
359
|
-
if strict:
|
360
|
-
get_config_logger().warning(
|
361
|
-
"[Foundation Config Warning] Error parsing custom emoji set configuration",
|
362
|
-
config_key="PROVIDE_LOG_CUSTOM_EMOJI_SETS",
|
363
|
-
error=str(e),
|
364
|
-
error_type=type(e).__name__,
|
365
|
-
)
|
366
|
-
|
367
|
-
if user_sets := os.getenv("PROVIDE_LOG_USER_DEFINED_EMOJI_SETS"):
|
368
|
-
try:
|
369
|
-
parsed = json.loads(user_sets)
|
370
|
-
if isinstance(parsed, list):
|
371
|
-
config_dict["user_defined_emoji_sets"] = [
|
372
|
-
EmojiSet(**item) if isinstance(item, dict) else item
|
373
|
-
for item in parsed
|
374
|
-
]
|
375
|
-
except json.JSONDecodeError as e:
|
376
|
-
if strict:
|
377
|
-
get_config_logger().warning(
|
378
|
-
"[Foundation Config Warning] Invalid JSON in configuration",
|
379
|
-
config_key="PROVIDE_LOG_USER_DEFINED_EMOJI_SETS",
|
380
|
-
error=str(e),
|
381
|
-
config_value=user_sets[:100] + "..."
|
382
|
-
if len(user_sets) > 100
|
383
|
-
else user_sets,
|
384
|
-
)
|
385
|
-
except (TypeError, ValueError) as e:
|
386
|
-
if strict:
|
387
|
-
get_config_logger().warning(
|
388
|
-
"[Foundation Config Warning] Error parsing user emoji set configuration",
|
389
|
-
config_key="PROVIDE_LOG_USER_DEFINED_EMOJI_SETS",
|
390
|
-
error=str(e),
|
391
|
-
error_type=type(e).__name__,
|
392
|
-
)
|
393
|
-
|
394
|
-
return cls.from_dict(config_dict, source=ConfigSource.ENV)
|