br-logging-and-profiling 0.1.0__tar.gz

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.
@@ -0,0 +1,57 @@
1
+ Metadata-Version: 2.4
2
+ Name: br-logging-and-profiling
3
+ Version: 0.1.0
4
+ Summary: Loguru-based logging facade, correlation IDs, and profiling decorator extracted from Forecasting-sidecar3.
5
+ Author: Brais Diaz
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/brais/br-logging-and-profiling
8
+ Project-URL: Repository, https://github.com/brais/br-logging-and-profiling
9
+ Keywords: logging,profiling,loguru,colorama,ray-id
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
16
+ Requires-Python: >=3.12
17
+ Description-Content-Type: text/markdown
18
+ Requires-Dist: loguru>=0.7.0
19
+ Requires-Dist: colorama>=0.4.6
20
+ Requires-Dist: pandas>=2.0
21
+ Requires-Dist: numpy<2.2,>=1.24
22
+ Provides-Extra: dev
23
+ Requires-Dist: pre-commit; extra == "dev"
24
+ Requires-Dist: pytest>=7.0; extra == "dev"
25
+ Requires-Dist: mypy>=1.0; extra == "dev"
26
+ Requires-Dist: ruff>=0.6; extra == "dev"
27
+ Requires-Dist: gitpython; extra == "dev"
28
+
29
+ # br_logging_and_profiling
30
+
31
+ Reusable logging/profiling/tracing package extracted from Forecasting-sidecar3.
32
+
33
+ ## Current state
34
+
35
+ - Logging: Loguru + colorama via `br_py_log_n_profile.do_log.log_it`
36
+ - Profiling: `profile_it` decorator (wall-clock timing, colored console output)
37
+ - Correlation IDs: `ray_id` via `contextvars.ContextVar`
38
+
39
+ ## Planned
40
+
41
+ - `tracing/` module: `init_tracing()`, `traced` decorator (OTel span replacement for `profile_it`), `@counted`/`@timed_metric` for hot-loop metrics
42
+ - Backend: Prometheus + Jaeger + Grafana + OTel Collector
43
+ - Migration phases: Phase 0 (deps/config) → Phase 1 (core tracing) → Phase 2 (migrate by layer) → Phase 3 (metrics) → Phase 4 (backend stack)
44
+
45
+ ## Install
46
+
47
+ ```bash
48
+ pip install br-logging-and-profiling
49
+ ```
50
+
51
+ ## Development
52
+
53
+ ```bash
54
+ pip install -r requirements-dev.txt
55
+ pre-commit install
56
+ pytest -m unit
57
+ ```
@@ -0,0 +1,29 @@
1
+ # br_logging_and_profiling
2
+
3
+ Reusable logging/profiling/tracing package extracted from Forecasting-sidecar3.
4
+
5
+ ## Current state
6
+
7
+ - Logging: Loguru + colorama via `br_py_log_n_profile.do_log.log_it`
8
+ - Profiling: `profile_it` decorator (wall-clock timing, colored console output)
9
+ - Correlation IDs: `ray_id` via `contextvars.ContextVar`
10
+
11
+ ## Planned
12
+
13
+ - `tracing/` module: `init_tracing()`, `traced` decorator (OTel span replacement for `profile_it`), `@counted`/`@timed_metric` for hot-loop metrics
14
+ - Backend: Prometheus + Jaeger + Grafana + OTel Collector
15
+ - Migration phases: Phase 0 (deps/config) → Phase 1 (core tracing) → Phase 2 (migrate by layer) → Phase 3 (metrics) → Phase 4 (backend stack)
16
+
17
+ ## Install
18
+
19
+ ```bash
20
+ pip install br-logging-and-profiling
21
+ ```
22
+
23
+ ## Development
24
+
25
+ ```bash
26
+ pip install -r requirements-dev.txt
27
+ pre-commit install
28
+ pytest -m unit
29
+ ```
@@ -0,0 +1,57 @@
1
+ Metadata-Version: 2.4
2
+ Name: br-logging-and-profiling
3
+ Version: 0.1.0
4
+ Summary: Loguru-based logging facade, correlation IDs, and profiling decorator extracted from Forecasting-sidecar3.
5
+ Author: Brais Diaz
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/brais/br-logging-and-profiling
8
+ Project-URL: Repository, https://github.com/brais/br-logging-and-profiling
9
+ Keywords: logging,profiling,loguru,colorama,ray-id
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
16
+ Requires-Python: >=3.12
17
+ Description-Content-Type: text/markdown
18
+ Requires-Dist: loguru>=0.7.0
19
+ Requires-Dist: colorama>=0.4.6
20
+ Requires-Dist: pandas>=2.0
21
+ Requires-Dist: numpy<2.2,>=1.24
22
+ Provides-Extra: dev
23
+ Requires-Dist: pre-commit; extra == "dev"
24
+ Requires-Dist: pytest>=7.0; extra == "dev"
25
+ Requires-Dist: mypy>=1.0; extra == "dev"
26
+ Requires-Dist: ruff>=0.6; extra == "dev"
27
+ Requires-Dist: gitpython; extra == "dev"
28
+
29
+ # br_logging_and_profiling
30
+
31
+ Reusable logging/profiling/tracing package extracted from Forecasting-sidecar3.
32
+
33
+ ## Current state
34
+
35
+ - Logging: Loguru + colorama via `br_py_log_n_profile.do_log.log_it`
36
+ - Profiling: `profile_it` decorator (wall-clock timing, colored console output)
37
+ - Correlation IDs: `ray_id` via `contextvars.ContextVar`
38
+
39
+ ## Planned
40
+
41
+ - `tracing/` module: `init_tracing()`, `traced` decorator (OTel span replacement for `profile_it`), `@counted`/`@timed_metric` for hot-loop metrics
42
+ - Backend: Prometheus + Jaeger + Grafana + OTel Collector
43
+ - Migration phases: Phase 0 (deps/config) → Phase 1 (core tracing) → Phase 2 (migrate by layer) → Phase 3 (metrics) → Phase 4 (backend stack)
44
+
45
+ ## Install
46
+
47
+ ```bash
48
+ pip install br-logging-and-profiling
49
+ ```
50
+
51
+ ## Development
52
+
53
+ ```bash
54
+ pip install -r requirements-dev.txt
55
+ pre-commit install
56
+ pytest -m unit
57
+ ```
@@ -0,0 +1,13 @@
1
+ README.md
2
+ pyproject.toml
3
+ br_logging_and_profiling.egg-info/PKG-INFO
4
+ br_logging_and_profiling.egg-info/SOURCES.txt
5
+ br_logging_and_profiling.egg-info/dependency_links.txt
6
+ br_logging_and_profiling.egg-info/requires.txt
7
+ br_logging_and_profiling.egg-info/top_level.txt
8
+ br_py_log_n_profile/__init__.py
9
+ br_py_log_n_profile/do_log/__init__.py
10
+ br_py_log_n_profile/do_log/log_it.py
11
+ br_py_log_n_profile/do_log/ray_id.py
12
+ br_py_log_n_profile/profiling/__init__.py
13
+ br_py_log_n_profile/profiling/base.py
@@ -0,0 +1,11 @@
1
+ loguru>=0.7.0
2
+ colorama>=0.4.6
3
+ pandas>=2.0
4
+ numpy<2.2,>=1.24
5
+
6
+ [dev]
7
+ pre-commit
8
+ pytest>=7.0
9
+ mypy>=1.0
10
+ ruff>=0.6
11
+ gitpython
@@ -0,0 +1,15 @@
1
+ """Reusable logging/profiling/tracing package extracted from Forecasting-sidecar3."""
2
+
3
+ from br_py_log_n_profile.do_log import get_ray_id, log, log_d, log_e, log_i, log_w, set_ray_id
4
+ from br_py_log_n_profile.profiling import profile_it
5
+
6
+ __all__ = [
7
+ "get_ray_id",
8
+ "log",
9
+ "log_d",
10
+ "log_e",
11
+ "log_i",
12
+ "log_w",
13
+ "profile_it",
14
+ "set_ray_id",
15
+ ]
@@ -0,0 +1,4 @@
1
+ from .log_it import log, log_d, log_e, log_i, log_w
2
+ from .ray_id import get_ray_id, set_ray_id
3
+
4
+ __all__ = ["get_ray_id", "log", "log_d", "log_e", "log_i", "log_w", "set_ray_id"]
@@ -0,0 +1,145 @@
1
+ import logging
2
+ import sys
3
+ import traceback
4
+ from pathlib import Path
5
+ from types import FrameType
6
+
7
+ from colorama import Fore, Style
8
+ from loguru import logger
9
+
10
+ from .ray_id import get_ray_id
11
+
12
+ __severity_color_map = {
13
+ logging.INFO: Fore.GREEN,
14
+ logging.WARNING: Fore.YELLOW,
15
+ logging.ERROR: Fore.RED,
16
+ logging.DEBUG: Fore.CYAN,
17
+ }
18
+ __root_path: Path | None = None
19
+ __log_format = "{time:YYYY-MM-DD HH:mm:ss.SS} | {level} | {name}:{function}:{line} - {message}"
20
+ __log_to_std_out_level = logging.DEBUG
21
+ __log_to_file_level = 0
22
+ __min_log_level = __log_to_std_out_level
23
+
24
+ __all__ = ["log_d", "log_e", "log_i", "log_w"]
25
+
26
+ _NAMED_LEVEL_THRESHOLDS = (
27
+ (logging.CRITICAL, "CRITICAL"),
28
+ (logging.ERROR, "ERROR"),
29
+ (logging.WARNING, "WARNING"),
30
+ (logging.INFO, "INFO"),
31
+ (logging.DEBUG, "DEBUG"),
32
+ )
33
+
34
+ _STANDARD_LEVEL_NAMES = {
35
+ logging.CRITICAL: "CRITICAL",
36
+ logging.ERROR: "ERROR",
37
+ logging.WARNING: "WARNING",
38
+ logging.INFO: "INFO",
39
+ logging.DEBUG: "DEBUG",
40
+ }
41
+
42
+
43
+ def _nearest_level_name(severity: int) -> str:
44
+ name = _STANDARD_LEVEL_NAMES.get(severity)
45
+ if name is not None:
46
+ return name
47
+ for threshold, name in _NAMED_LEVEL_THRESHOLDS:
48
+ if severity >= threshold:
49
+ return name
50
+ return "DEBUG"
51
+
52
+
53
+ class InterceptHandler(logging.Handler):
54
+ def emit(self, record: logging.LogRecord) -> None:
55
+ try:
56
+ level = logger.level(record.levelname).name
57
+ except ValueError:
58
+ level = _nearest_level_name(record.levelno)
59
+
60
+ frame: FrameType | None
61
+ frame, depth = sys._getframe(6), 6
62
+ while frame and frame.f_code.co_filename == logging.__file__:
63
+ frame = frame.f_back
64
+ depth += 1
65
+
66
+ logger.opt(depth=depth, exception=record.exc_info).log(level, record.getMessage())
67
+
68
+
69
+ def _intercept_stdlib_logging() -> None:
70
+ logging.basicConfig(handlers=[InterceptHandler()], force=True)
71
+
72
+
73
+ def root_path(root_distance: int = 5) -> Path:
74
+ global __root_path
75
+ if __root_path is None:
76
+ __root_path = Path(__file__)
77
+ try:
78
+ for _i in range(root_distance):
79
+ __root_path = __root_path.parent
80
+ except (NameError, FileNotFoundError):
81
+ logger.warning(
82
+ f"Unable to find parent for {__root_path}. "
83
+ f"Calling init_logger will enable extended "
84
+ f"features and resolve this warning."
85
+ )
86
+ return __root_path
87
+
88
+
89
+ def _init_default_logger() -> None:
90
+ log_dir = root_path() / "logs"
91
+ log_dir.mkdir(parents=True, exist_ok=True)
92
+ runtime_log_dir = log_dir / "runtime"
93
+ runtime_log_dir.mkdir(parents=True, exist_ok=True)
94
+ logger.remove()
95
+ logger.add(
96
+ sys.stdout,
97
+ format=__log_format,
98
+ colorize=True,
99
+ level=__log_to_std_out_level,
100
+ )
101
+ logger.add(
102
+ runtime_log_dir / "runtime.log",
103
+ format=__log_format,
104
+ level=__log_to_file_level,
105
+ rotation="00:00",
106
+ retention="30 days",
107
+ enqueue=True,
108
+ )
109
+ _intercept_stdlib_logging()
110
+
111
+
112
+ _init_default_logger()
113
+
114
+
115
+ def log_d(message: str, stack_limit: int = 0, stack_offset: int = 0) -> None:
116
+ log(message, logging.DEBUG, stack_limit, stack_offset + 1)
117
+
118
+
119
+ def log_w(message: str, stack_limit: int = 0, stack_offset: int = 0) -> None:
120
+ log(message, logging.WARNING, stack_limit, stack_offset + 1)
121
+
122
+
123
+ def log_i(message: str, stack_limit: int = 0, stack_offset: int = 0) -> None:
124
+ log(message, logging.INFO, stack_limit, stack_offset + 1)
125
+
126
+
127
+ def log_e(message: str, stack_limit: int = 0, stack_offset: int = 0) -> None:
128
+ log(message, logging.ERROR, stack_limit, stack_offset + 1)
129
+
130
+
131
+ def log(message: str, severity: int, stack_limit: int = 0, stack_offset: int = 0) -> None:
132
+ if __min_log_level > severity:
133
+ return
134
+ try:
135
+ stack_trace = ""
136
+ if stack_limit > 0:
137
+ stack = traceback.format_stack(limit=stack_offset + stack_limit + 1)[: -(stack_offset + 1)][-(stack_limit):]
138
+ stack_trace = "\n" + "".join(stack)
139
+ color = __severity_color_map.get(severity, Fore.WHITE)
140
+ id_of_ray = get_ray_id()
141
+ logger.opt(depth=stack_offset + 1).log(
142
+ _nearest_level_name(severity), f"{color}{message}{stack_trace} (ray:{id_of_ray}){Style.RESET_ALL}"
143
+ )
144
+ except Exception as e:
145
+ logger.exception(f"Failed to log message: {message} | Error: {e!s}")
@@ -0,0 +1,23 @@
1
+ import uuid
2
+ from contextvars import ContextVar
3
+
4
+ ray_id_var: ContextVar[uuid.UUID | None] = ContextVar("ray_id")
5
+
6
+
7
+ def set_ray_id(id: uuid.UUID) -> None:
8
+ ray_id_var.set(id)
9
+
10
+
11
+ def get_ray_id(generate: bool = True) -> uuid.UUID | None:
12
+ id_of_ray = ray_id_var.get(None)
13
+ if generate and id_of_ray is None:
14
+ id_of_ray = ray_id(source_type=0)
15
+ set_ray_id(id_of_ray)
16
+ return id_of_ray
17
+
18
+
19
+ def ray_id(source_type: int, id_of_ray: uuid.UUID | None = None) -> uuid.UUID:
20
+ if id_of_ray is not None:
21
+ return id_of_ray
22
+ id_of_ray = uuid.uuid1(node=source_type)
23
+ return id_of_ray
@@ -0,0 +1 @@
1
+ from .base import profile_it as profile_it
@@ -0,0 +1,80 @@
1
+ import time
2
+ from collections.abc import Callable
3
+ from functools import wraps
4
+
5
+ import numpy as np
6
+ import pandas as pd
7
+ from colorama import Fore
8
+
9
+ from ..do_log.log_it import log_d
10
+
11
+
12
+ def profile_it[**P, R](func: Callable[P, R]) -> Callable[P, R]:
13
+ @wraps(func)
14
+ def _measure_time(*args: P.args, **kwargs: P.kwargs) -> R:
15
+ start_time = time.time()
16
+ function_parameters = parameters_to_str(args, kwargs)
17
+ log_d(f"{func.__name__}({function_parameters}) started", stack_offset=1)
18
+ result = func(*args, **kwargs)
19
+ end_time = time.time()
20
+ execution_time = end_time - start_time
21
+ execution_time_color = (
22
+ Fore.BLUE
23
+ if execution_time < 0.01
24
+ else Fore.GREEN
25
+ if execution_time < 0.1
26
+ else Fore.YELLOW
27
+ if execution_time < 1
28
+ else Fore.RED
29
+ )
30
+ log_d(
31
+ f"{func.__name__}({function_parameters}) executed in {execution_time_color}{execution_time:.3f} seconds",
32
+ stack_offset=1,
33
+ )
34
+ return result
35
+
36
+ return _measure_time
37
+
38
+
39
+ def parameters_to_str(args: tuple[object, ...], kwargs: dict[str, object]) -> str:
40
+ def process_item(item: object) -> str:
41
+ if isinstance(item, pd.DataFrame):
42
+ return f"{len(item)}*{item.columns}"
43
+ elif isinstance(item, list):
44
+ try:
45
+ return f"list{np.array(item).shape}"
46
+ except ValueError as e:
47
+ raise e
48
+ except Exception as e:
49
+ raise e
50
+ elif isinstance(item, np.ndarray):
51
+ return f"ndarray{item.shape}"
52
+ elif isinstance(item, dict):
53
+ return process_dict(item)
54
+ else:
55
+ return str(item)
56
+
57
+ def process_dict(d: dict[object, object]) -> str:
58
+ t_parameters: list[str] = []
59
+ for key, value in d.items():
60
+ if isinstance(value, list):
61
+ try:
62
+ t_parameters.append(f"{key}: list{np.array(value).shape}")
63
+ except ValueError as e:
64
+ raise e
65
+ except Exception as e:
66
+ raise e
67
+ elif isinstance(value, pd.DataFrame):
68
+ t_parameters.append(f"{key}: {len(value)}*{value.columns}")
69
+ elif isinstance(value, np.ndarray):
70
+ t_parameters.append(f"{key}: ndarray{value.shape}")
71
+ elif isinstance(value, dict):
72
+ t_parameters.append(f"{key}: {{ {process_dict(value)} }}")
73
+ else:
74
+ t_parameters.append(f"{key}: {value}")
75
+ return ", ".join(t_parameters)
76
+
77
+ parameters = [process_item(arg) for arg in args]
78
+ parameters += [f"{k}: {process_item(kwargs[k])}" for k in kwargs]
79
+
80
+ return ", ".join(parameters)
@@ -0,0 +1,78 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "br-logging-and-profiling"
7
+ version = "0.1.0"
8
+ description = "Loguru-based logging facade, correlation IDs, and profiling decorator extracted from Forecasting-sidecar3."
9
+ readme = "README.md"
10
+ license = { text = "MIT" }
11
+ requires-python = ">=3.12"
12
+ authors = [{ name = "Brais Diaz" }]
13
+ keywords = ["logging", "profiling", "loguru", "colorama", "ray-id"]
14
+ classifiers = [
15
+ "Development Status :: 3 - Alpha",
16
+ "Intended Audience :: Developers",
17
+ "License :: OSI Approved :: MIT License",
18
+ "Programming Language :: Python :: 3",
19
+ "Programming Language :: Python :: 3.12",
20
+ "Topic :: Software Development :: Libraries :: Python Modules",
21
+ ]
22
+ dependencies = [
23
+ "loguru>=0.7.0",
24
+ "colorama>=0.4.6",
25
+ "pandas>=2.0",
26
+ "numpy>=1.24,<2.2",
27
+ ]
28
+
29
+ [project.optional-dependencies]
30
+ dev = [
31
+ "pre-commit",
32
+ "pytest>=7.0",
33
+ "mypy>=1.0",
34
+ "ruff>=0.6",
35
+ "gitpython",
36
+ ]
37
+
38
+ [project.urls]
39
+ Homepage = "https://github.com/brais/br-logging-and-profiling"
40
+ Repository = "https://github.com/brais/br-logging-and-profiling"
41
+
42
+ [tool.setuptools.packages.find]
43
+ where = ["."]
44
+ include = ["br_py_log_n_profile*"]
45
+
46
+ [tool.setuptools.package-data]
47
+ br_py_log_n_profile = ["py.typed"]
48
+
49
+ [tool.ruff]
50
+ target-version = "py312"
51
+ line-length = 120
52
+ extend-exclude = ["*.ipynb", ".testvenv"]
53
+
54
+ [tool.ruff.lint]
55
+ select = ["E", "F", "I", "UP", "B", "C4", "SIM", "W"]
56
+
57
+ [tool.mypy]
58
+ python_version = "3.12"
59
+ explicit_package_bases = true
60
+ strict = true
61
+ disallow_any_explicit = true
62
+ no_implicit_optional = true
63
+ warn_return_any = true
64
+ ignore_missing_imports = true
65
+ follow_imports = "silent"
66
+
67
+ [tool.pytest.ini_options]
68
+ minversion = "7.0"
69
+ testpaths = ["tests"]
70
+ pythonpath = ["."]
71
+ python_files = ["test_*.py"]
72
+ python_functions = ["test_*"]
73
+ addopts = "-ra --strict-markers --import-mode=importlib"
74
+ markers = [
75
+ "unit: fast, isolated, no I/O",
76
+ "integration: multiple modules/stages wired together",
77
+ "contract: validates public API contract against installed package",
78
+ ]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+