yb-observability 0.1.0__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.
File without changes
@@ -0,0 +1,13 @@
1
+ from yb_observability.config.exceptions import (
2
+ ConfigEnvironmentVariableError,
3
+ ConfigFileNotFoundError,
4
+ ConfigLoaderError,
5
+ )
6
+ from yb_observability.config.loader import ConfigLoader
7
+
8
+ __all__ = [
9
+ "ConfigEnvironmentVariableError",
10
+ "ConfigFileNotFoundError",
11
+ "ConfigLoader",
12
+ "ConfigLoaderError",
13
+ ]
@@ -0,0 +1,10 @@
1
+ class ConfigLoaderError(Exception):
2
+ """Base exception for configuration loading errors."""
3
+
4
+
5
+ class ConfigFileNotFoundError(ConfigLoaderError):
6
+ """Raised when the configuration file cannot be found."""
7
+
8
+
9
+ class ConfigEnvironmentVariableError(ConfigLoaderError):
10
+ """Raised when a required environment variable is missing."""
@@ -0,0 +1,72 @@
1
+ from os import getenv
2
+ from pathlib import Path
3
+ from re import Match, compile
4
+ from typing import Any
5
+
6
+ from yaml import YAMLError, safe_load
7
+
8
+ from yb_observability.config.exceptions import (
9
+ ConfigEnvironmentVariableError,
10
+ ConfigFileNotFoundError,
11
+ ConfigLoaderError,
12
+ )
13
+
14
+ _ENV_VAR_PATTERN = compile(r"\$\{([A-Za-z][A-Za-z0-9_]*)\}")
15
+
16
+
17
+ class ConfigLoader:
18
+ """Load YAML configuration and resolve environment variables."""
19
+
20
+ def __init__(self, config_path: str | Path) -> None:
21
+ self.config_path = Path(config_path)
22
+
23
+ def load(self) -> dict[str, Any]:
24
+ """Load and resolve the configuration file."""
25
+
26
+ if not self.config_path.is_file():
27
+ raise ConfigFileNotFoundError(
28
+ f"Configuration file not found: {self.config_path}"
29
+ )
30
+
31
+ try:
32
+ content = self.config_path.read_text(encoding="utf-8")
33
+ except OSError as exc:
34
+ raise ConfigLoaderError(
35
+ f"Unable to read configuration file: {self.config_path}"
36
+ ) from exc
37
+
38
+ resolved_content = self._resolve_environment_variables(content)
39
+
40
+ try:
41
+ config = safe_load(resolved_content)
42
+ except YAMLError as exc:
43
+ raise ConfigLoaderError(
44
+ f"Invalid YAML configuration: {self.config_path}"
45
+ ) from exc
46
+
47
+ if config is None:
48
+ return {}
49
+
50
+ if not isinstance(config, dict):
51
+ raise ConfigLoaderError(
52
+ "The root of the configuration file must be a YAML mapping."
53
+ )
54
+
55
+ return config
56
+
57
+ @staticmethod
58
+ def _resolve_environment_variables(content: str) -> str:
59
+ """Resolve ${ENV_VAR} references using environment variables."""
60
+
61
+ def replace(match: Match[str]) -> str:
62
+ variable_name = match.group(1)
63
+ value = getenv(variable_name)
64
+
65
+ if value is None:
66
+ raise ConfigEnvironmentVariableError(
67
+ f"Required environment variable is not set: {variable_name}"
68
+ )
69
+
70
+ return value
71
+
72
+ return _ENV_VAR_PATTERN.sub(replace, content)
@@ -0,0 +1,3 @@
1
+ from yb_observability.logging.manager import configure_logging
2
+
3
+ __all__ = ["configure_logging"]
@@ -0,0 +1,51 @@
1
+ from pathlib import Path
2
+
3
+ from loguru import logger
4
+
5
+ _LOG_FORMAT = (
6
+ "<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green> | "
7
+ "<level>{level: <5}</level> | "
8
+ "<cyan>[{extra[service]: <10}]</cyan> | "
9
+ "<blue>{name}:{function}:{line}</blue>\n"
10
+ "<level>| {message}</level>"
11
+ )
12
+
13
+
14
+ def configure_logging(
15
+ service: str,
16
+ *,
17
+ log_directory: str | Path = "logs",
18
+ ) -> None:
19
+ """Configure application logging for a service.
20
+
21
+ Args:
22
+ service: Name identifying the application or service.
23
+ log_directory: Root directory used for log files.
24
+ """
25
+ if not service.strip():
26
+ raise ValueError("Service name cannot be empty.")
27
+
28
+ log_path = Path(log_directory) / service
29
+ log_path.mkdir(parents=True, exist_ok=True)
30
+
31
+ logger.remove()
32
+
33
+ logger.configure(
34
+ extra={"service": service.upper()},
35
+ )
36
+
37
+ logger.add(
38
+ sink=lambda message: print(message, end=""),
39
+ format=_LOG_FORMAT,
40
+ level="DEBUG",
41
+ colorize=True,
42
+ )
43
+
44
+ logger.add(
45
+ log_path / "{time:YYYY-MM-DD}.log",
46
+ format=_LOG_FORMAT,
47
+ level="DEBUG",
48
+ rotation="00:00",
49
+ retention="30 days",
50
+ encoding="utf-8",
51
+ )
@@ -0,0 +1,203 @@
1
+ Metadata-Version: 2.5
2
+ Name: yb-observability
3
+ Version: 0.1.0
4
+ Summary: Reusable Python infrastructure for application configuration and observability.
5
+ Author: Yassine Bouali
6
+ License-File: LICENSE
7
+ Requires-Python: <3.15,>=3.14
8
+ Requires-Dist: loguru<1.0.0,>=0.7.3
9
+ Requires-Dist: pyyaml<7.0,>=6.0
10
+ Description-Content-Type: text/markdown
11
+
12
+ # yb-observability
13
+
14
+ Reusable Python infrastructure for application configuration and observability.
15
+
16
+ `yb-observability` provides common infrastructure that can be shared across Python applications without coupling the library to a specific application or architecture.
17
+
18
+ The project currently focuses on configuration and logging, with additional observability capabilities planned for future releases.
19
+
20
+ ## Features
21
+
22
+ ### Configuration
23
+
24
+ * YAML configuration loading.
25
+ * Environment variable resolution.
26
+ * Nested environment variable support.
27
+ * Configuration validation.
28
+ * Clear configuration-related exceptions.
29
+
30
+ ### Logging
31
+
32
+ * Loguru-based logging.
33
+ * Explicit application/service identity.
34
+ * Consistent human-readable log format.
35
+ * Console logging.
36
+ * File logging.
37
+ * Daily log rotation.
38
+ * Configurable retention.
39
+ * Per-service log directories.
40
+
41
+ ### Future
42
+
43
+ The library is designed to grow with additional infrastructure such as:
44
+
45
+ * Structured/JSON logging.
46
+ * Request and correlation IDs.
47
+ * Metrics.
48
+ * Distributed tracing.
49
+ * OpenTelemetry integrations.
50
+ * Additional observability integrations.
51
+
52
+ Features are introduced incrementally and remain independent where possible.
53
+
54
+ ## Installation
55
+
56
+ Install the package from PyPI:
57
+
58
+ ```bash
59
+ pip install yb-observability
60
+ ```
61
+
62
+ With `uv`:
63
+
64
+ ```bash
65
+ uv add yb-observability
66
+ ```
67
+
68
+ ## Basic Usage
69
+
70
+ Configuration:
71
+
72
+ ```python
73
+ from yb_observability.config import ConfigLoader
74
+
75
+ config = ConfigLoader("config.yml").load()
76
+ ```
77
+
78
+ Logging:
79
+
80
+ ```python
81
+ from yb_observability.logging import configure_logging
82
+
83
+ configure_logging(service="my-service")
84
+ ```
85
+
86
+ The application remains responsible for deciding what should be logged. `yb-observability` is responsible for configuring the logging infrastructure and providing a consistent logging interface.
87
+
88
+ ## Design Goals
89
+
90
+ ### Reusable
91
+
92
+ The library must not depend on a specific application, framework, infrastructure provider, or domain.
93
+
94
+ ### Explicit
95
+
96
+ Applications should explicitly configure the behavior they require rather than relying on hidden conventions.
97
+
98
+ ### Consistent
99
+
100
+ Applications using the library should receive consistent configuration and logging behavior.
101
+
102
+ ### Extensible
103
+
104
+ New capabilities should be possible without breaking the existing public API.
105
+
106
+ ### Testable
107
+
108
+ Core functionality should be covered by automated tests.
109
+
110
+ ### Production-oriented
111
+
112
+ The library should be suitable for both local development and production applications.
113
+
114
+ ## Project Structure
115
+
116
+ ```text
117
+ yb-observability/
118
+ ├── src/
119
+ │ └── yb_observability/
120
+ │ ├── config/
121
+ │ └── logging/
122
+ │
123
+ ├── tests/
124
+ │ ├── config/
125
+ │ └── logging/
126
+ │
127
+ ├── docs/
128
+ │ ├── architecture/
129
+ │ ├── configuration/
130
+ │ └── logging/
131
+ │
132
+ ├── .github/
133
+ │ └── workflows/
134
+ │
135
+ ├── CHANGELOG.md
136
+ ├── CODE_OF_CONDUCT.md
137
+ ├── CONTRIBUTING.md
138
+ ├── LICENSE
139
+ ├── README.md
140
+ ├── SECURITY.md
141
+ └── pyproject.toml
142
+ ```
143
+
144
+ ## Development
145
+
146
+ The project uses:
147
+
148
+ * Python 3.14
149
+ * uv
150
+ * Ruff
151
+ * Pyright
152
+ * Pytest
153
+ * Loguru
154
+ * PyYAML
155
+
156
+ Install development dependencies:
157
+
158
+ ```bash
159
+ uv sync
160
+ ```
161
+
162
+ Run the test suite:
163
+
164
+ ```bash
165
+ uv run pytest
166
+ ```
167
+
168
+ Run linting:
169
+
170
+ ```bash
171
+ uv run ruff check .
172
+ ```
173
+
174
+ Check formatting:
175
+
176
+ ```bash
177
+ uv run ruff format --check .
178
+ ```
179
+
180
+ Run type checking:
181
+
182
+ ```bash
183
+ uv run pyright
184
+ ```
185
+
186
+ ## Contributing
187
+
188
+ Contributions are welcome.
189
+
190
+ Before submitting a pull request, make sure that:
191
+
192
+ * Tests pass.
193
+ * Ruff checks pass.
194
+ * Formatting checks pass.
195
+ * Pyright checks pass.
196
+ * New functionality includes appropriate tests.
197
+ * Public API changes are documented.
198
+
199
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for development and contribution guidelines.
200
+
201
+ ## License
202
+
203
+ This project is licensed under the terms defined in [LICENSE](LICENSE).
@@ -0,0 +1,10 @@
1
+ yb_observability/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ yb_observability/config/__init__.py,sha256=uQ3gLWWL0__YYRIn9Dit4mZ0YerKgCqgmvjFlfFzQw0,324
3
+ yb_observability/config/exceptions.py,sha256=DUaxgVVOyveg5CcYqp115_FObDBu3sjRdZKvT6jGj-4,334
4
+ yb_observability/config/loader.py,sha256=Ad9L4HxBOxlWNb7ic5I6qFnZr9jtx-0WfKDQslSQh3g,2132
5
+ yb_observability/logging/__init__.py,sha256=bvTUA55YxHhw9z2Ba82REmPTHta7oyyHP9q7jFPhxZw,96
6
+ yb_observability/logging/manager.py,sha256=1r-lXPmg7NX1fWrMKwVAg3X3Gh62EKF82UglCbhGMHk,1202
7
+ yb_observability-0.1.0.dist-info/METADATA,sha256=i4vSmiLhyQ68xkuzT55VNpMO4Z2-NggeDiSTvucV5PI,4094
8
+ yb_observability-0.1.0.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
9
+ yb_observability-0.1.0.dist-info/licenses/LICENSE,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
+ yb_observability-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.32.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
File without changes