openinfra-logger 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.
- openinfra_logger-0.1.0/CHANGELOG.md +38 -0
- openinfra_logger-0.1.0/LICENSE +21 -0
- openinfra_logger-0.1.0/MANIFEST.in +6 -0
- openinfra_logger-0.1.0/PKG-INFO +115 -0
- openinfra_logger-0.1.0/README.md +76 -0
- openinfra_logger-0.1.0/openinfra_logger/__init__.py +192 -0
- openinfra_logger-0.1.0/openinfra_logger.egg-info/PKG-INFO +115 -0
- openinfra_logger-0.1.0/openinfra_logger.egg-info/SOURCES.txt +12 -0
- openinfra_logger-0.1.0/openinfra_logger.egg-info/dependency_links.txt +1 -0
- openinfra_logger-0.1.0/openinfra_logger.egg-info/requires.txt +3 -0
- openinfra_logger-0.1.0/openinfra_logger.egg-info/top_level.txt +1 -0
- openinfra_logger-0.1.0/pyproject.toml +52 -0
- openinfra_logger-0.1.0/setup.cfg +4 -0
- openinfra_logger-0.1.0/setup.py +17 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to OpenInfra Logger will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.1.0] — 2026-05-15
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- **Node.js implementation** (`src/index.js`) — console / file / remote transports, structured JSON output, log levels (`debug` / `info` / `warn` / `error`), default metadata merging.
|
|
14
|
+
- **Python implementation** (`python/openinfra_logger/`) — feature parity with Node.
|
|
15
|
+
- **Go implementation** (`go/logger.go`) — feature parity with Node and Python.
|
|
16
|
+
- **Rust implementation** (`rust/src/lib.rs`) — zero-dependency JSON builder, file + console transports.
|
|
17
|
+
- **Auto-redaction** — sensitive keys (`password`, `token`, `secret`, `api_key`, `credit_card`) recursively replaced with `[REDACTED]` before any transport sees the entry. Case-insensitive on keys; configurable via `redactKeys` / `redact_keys`.
|
|
18
|
+
- **Remote batching transport** — Node and Python ship logs in batches (default 100 entries or 2 s, whichever first) instead of HTTP-per-log; survives endpoint failures without crashing the host process.
|
|
19
|
+
- **OpenTelemetry context injection** — when an active span is present, `trace_id` and `span_id` are extracted automatically (no extra `import`).
|
|
20
|
+
- **Datadog formatter** — renames `level` → `status`, `trace_id` → `dd.trace_id`, `span_id` → `dd.span_id`.
|
|
21
|
+
- **Elastic / ECS formatter** — renames `timestamp` → `@timestamp`, `level` → `log.level`.
|
|
22
|
+
- **Log analyzer CLI** (`tools/ai-analyzer.js`) — runs entirely on the host by default. Seven layers of analysis without a single network call: (1) clustering by normalized message shape, (2) six built-in heuristics (timeout cascades, OOM, database failures, 5xx, rate-limit, auth), (3) stack-trace dedup on the top-3 frames, (4) temporal cascade detection (bursts of ≥3 errors / 1 s), (5) per-minute anomaly windows via z-score over the baseline rate, (6) service-interaction graph derived from `trace_id` co-occurrence, (7) service breakdown and observed time window. The optional `--llm=<provider>` flag sends the clustered prompt to one of three providers for an LLM-deepened narrative: `anthropic` (cloud, needs `ANTHROPIC_API_KEY`), `ollama` (fully local — keeps the host-only guarantee), or `openai` (OpenAI-compatible endpoint such as LM Studio or vLLM). `--prompt-only` prints the prompt for manual paste.
|
|
23
|
+
- **Test matrix** — 69 tests across the four runtimes (Node 32, Python 22, Go 8, Rust 7) covering redaction, levels, transports, formatters, batching triggers, and JSON escaping.
|
|
24
|
+
- **Examples** — Express integration, security logging, Datadog integration, OpenTelemetry tracing, basic usage in Node / Python / Go / Rust.
|
|
25
|
+
- **Documentation** — installation, architecture, integration, advanced configuration under `docs/`.
|
|
26
|
+
- **CI workflow** and release automation under `.github/workflows/`.
|
|
27
|
+
|
|
28
|
+
### Fixed
|
|
29
|
+
- **Node** — invalid log levels were being emitted verbatim in the JSON because the fallback to `'info'` updated the wrong variable. The emitted entry now correctly contains `"level":"info"` when the caller passes an unsupported level.
|
|
30
|
+
- **Node** — concurrent `fs.appendFile` calls could interleave lines under bursty writes. Writes are now serialized through an internal promise chain, preserving emission order.
|
|
31
|
+
- **Python** — replaced 5 occurrences of the deprecated `datetime.datetime.utcnow()` with a forward-compatible helper that emits ISO-8601 UTC with a trailing `Z`. No more `DeprecationWarning` on Python 3.12+.
|
|
32
|
+
- **Rust** — the manual JSON builder did not escape `"`, `\`, control characters or newlines in messages or metadata, producing invalid JSON whenever user input contained any of them. Introduced `escape_json_string()` and a pure `build_json_line()` function (RFC 8259-compliant). Crate remains dependency-free.
|
|
33
|
+
|
|
34
|
+
### Security
|
|
35
|
+
- **LGPD / GDPR** — auto-redaction is on by default; disabling it requires an explicit `redactKeys: []` in the configuration.
|
|
36
|
+
|
|
37
|
+
[Unreleased]: https://github.com/jonathascordeiro20/openinfra-logger/compare/v0.1.0...HEAD
|
|
38
|
+
[0.1.0]: https://github.com/jonathascordeiro20/openinfra-logger/releases/tag/v0.1.0
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Jonathas
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: openinfra-logger
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Universal structured logging and observability for Node, Python, Go and Rust — built from the standard library only. Native batching, auto-redaction, OpenTelemetry-aware, with native Datadog and Elastic (ECS) formatters.
|
|
5
|
+
Home-page: https://github.com/jonathascordeiro20/openinfra-logger
|
|
6
|
+
Author: Jonathas
|
|
7
|
+
Author-email: Jonathas Cordeiro <jonathas.cordeiro2023@gmail.com>
|
|
8
|
+
License: MIT
|
|
9
|
+
Project-URL: Homepage, https://openinfralogger.fun
|
|
10
|
+
Project-URL: Repository, https://github.com/jonathascordeiro20/openinfra-logger
|
|
11
|
+
Project-URL: Documentation, https://github.com/jonathascordeiro20/openinfra-logger#readme
|
|
12
|
+
Project-URL: Changelog, https://github.com/jonathascordeiro20/openinfra-logger/blob/main/CHANGELOG.md
|
|
13
|
+
Project-URL: Bug Tracker, https://github.com/jonathascordeiro20/openinfra-logger/issues
|
|
14
|
+
Keywords: logging,logger,structured-logging,observability,telemetry,opentelemetry,datadog,elasticsearch,ecs,loki,zero-dependency,lgpd,gdpr,redaction
|
|
15
|
+
Classifier: Development Status :: 4 - Beta
|
|
16
|
+
Classifier: Intended Audience :: Developers
|
|
17
|
+
Classifier: Intended Audience :: System Administrators
|
|
18
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
19
|
+
Classifier: Operating System :: OS Independent
|
|
20
|
+
Classifier: Programming Language :: Python :: 3
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
25
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
26
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
27
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
28
|
+
Classifier: Topic :: System :: Logging
|
|
29
|
+
Classifier: Typing :: Typed
|
|
30
|
+
Requires-Python: >=3.6
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
32
|
+
License-File: LICENSE
|
|
33
|
+
Provides-Extra: opentelemetry
|
|
34
|
+
Requires-Dist: opentelemetry-api>=1.20; extra == "opentelemetry"
|
|
35
|
+
Dynamic: author
|
|
36
|
+
Dynamic: home-page
|
|
37
|
+
Dynamic: license-file
|
|
38
|
+
Dynamic: requires-python
|
|
39
|
+
|
|
40
|
+
# OpenInfra Logger — Python
|
|
41
|
+
|
|
42
|
+
[](https://pypi.org/project/openinfra-logger/)
|
|
43
|
+
[](https://github.com/jonathascordeiro20/openinfra-logger/blob/main/LICENSE)
|
|
44
|
+
[](https://pypi.org/project/openinfra-logger/)
|
|
45
|
+
|
|
46
|
+
**OpenInfra Logger** is a zero-dependency, structured logging library — built from the Python standard library only — with native batching, auto-redaction, OpenTelemetry trace injection, and Datadog/Elastic formatters.
|
|
47
|
+
|
|
48
|
+
The same JSON shape is emitted by sibling implementations for **Node.js, Go and Rust**, making polyglot stacks observable with a single log format.
|
|
49
|
+
|
|
50
|
+
## Install
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
pip install openinfra-logger
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Quickstart
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
from openinfra_logger import log, configure
|
|
60
|
+
|
|
61
|
+
log("System initialized", "info")
|
|
62
|
+
log("Failed to parse payload", "error", {"request_id": "abc-123"})
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## File + remote with batching
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
from openinfra_logger import log, configure
|
|
69
|
+
|
|
70
|
+
configure(
|
|
71
|
+
transports=["console", "file", "remote"],
|
|
72
|
+
file_path="./production.log",
|
|
73
|
+
remote_url="https://logs.my-infrastructure.com/ingest",
|
|
74
|
+
default_metadata={"service": "payment-gateway", "env": "production"},
|
|
75
|
+
batch_size=100,
|
|
76
|
+
flush_interval_ms=2000,
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
log("Payment processed", "info", {"transaction_id": "abc-456"})
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Datadog / Elastic formatters
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
configure(formatter="datadog") # renames level → status, trace_id → dd.trace_id
|
|
86
|
+
# configure(formatter="elastic") # renames timestamp → @timestamp, level → log.level
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Auto-redaction (LGPD / GDPR)
|
|
90
|
+
|
|
91
|
+
Sensitive keys (`password`, `token`, `secret`, `api_key`, `credit_card`) are recursively replaced with `[REDACTED]` before any transport sees the entry. Case-insensitive on key names. Override via `redact_keys=[...]` in `configure(...)`.
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
log("Login", "info", {"user": "alice", "password": "p@ss"})
|
|
95
|
+
# → "...","user":"alice","password":"[REDACTED]"
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## OpenTelemetry tracing
|
|
99
|
+
|
|
100
|
+
If an OTel span is active in the current context, `trace_id` and `span_id` are picked up automatically. Install the optional extra to enable detection:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
pip install "openinfra-logger[opentelemetry]"
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Links
|
|
107
|
+
|
|
108
|
+
- **Source** — <https://github.com/jonathascordeiro20/openinfra-logger>
|
|
109
|
+
- **Issues** — <https://github.com/jonathascordeiro20/openinfra-logger/issues>
|
|
110
|
+
- **Changelog** — <https://github.com/jonathascordeiro20/openinfra-logger/blob/main/CHANGELOG.md>
|
|
111
|
+
- **Project site** — <https://openinfralogger.fun>
|
|
112
|
+
|
|
113
|
+
## License
|
|
114
|
+
|
|
115
|
+
MIT — see [LICENSE](https://github.com/jonathascordeiro20/openinfra-logger/blob/main/LICENSE).
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# OpenInfra Logger — Python
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/openinfra-logger/)
|
|
4
|
+
[](https://github.com/jonathascordeiro20/openinfra-logger/blob/main/LICENSE)
|
|
5
|
+
[](https://pypi.org/project/openinfra-logger/)
|
|
6
|
+
|
|
7
|
+
**OpenInfra Logger** is a zero-dependency, structured logging library — built from the Python standard library only — with native batching, auto-redaction, OpenTelemetry trace injection, and Datadog/Elastic formatters.
|
|
8
|
+
|
|
9
|
+
The same JSON shape is emitted by sibling implementations for **Node.js, Go and Rust**, making polyglot stacks observable with a single log format.
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pip install openinfra-logger
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Quickstart
|
|
18
|
+
|
|
19
|
+
```python
|
|
20
|
+
from openinfra_logger import log, configure
|
|
21
|
+
|
|
22
|
+
log("System initialized", "info")
|
|
23
|
+
log("Failed to parse payload", "error", {"request_id": "abc-123"})
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## File + remote with batching
|
|
27
|
+
|
|
28
|
+
```python
|
|
29
|
+
from openinfra_logger import log, configure
|
|
30
|
+
|
|
31
|
+
configure(
|
|
32
|
+
transports=["console", "file", "remote"],
|
|
33
|
+
file_path="./production.log",
|
|
34
|
+
remote_url="https://logs.my-infrastructure.com/ingest",
|
|
35
|
+
default_metadata={"service": "payment-gateway", "env": "production"},
|
|
36
|
+
batch_size=100,
|
|
37
|
+
flush_interval_ms=2000,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
log("Payment processed", "info", {"transaction_id": "abc-456"})
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Datadog / Elastic formatters
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
configure(formatter="datadog") # renames level → status, trace_id → dd.trace_id
|
|
47
|
+
# configure(formatter="elastic") # renames timestamp → @timestamp, level → log.level
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Auto-redaction (LGPD / GDPR)
|
|
51
|
+
|
|
52
|
+
Sensitive keys (`password`, `token`, `secret`, `api_key`, `credit_card`) are recursively replaced with `[REDACTED]` before any transport sees the entry. Case-insensitive on key names. Override via `redact_keys=[...]` in `configure(...)`.
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
log("Login", "info", {"user": "alice", "password": "p@ss"})
|
|
56
|
+
# → "...","user":"alice","password":"[REDACTED]"
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## OpenTelemetry tracing
|
|
60
|
+
|
|
61
|
+
If an OTel span is active in the current context, `trace_id` and `span_id` are picked up automatically. Install the optional extra to enable detection:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
pip install "openinfra-logger[opentelemetry]"
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Links
|
|
68
|
+
|
|
69
|
+
- **Source** — <https://github.com/jonathascordeiro20/openinfra-logger>
|
|
70
|
+
- **Issues** — <https://github.com/jonathascordeiro20/openinfra-logger/issues>
|
|
71
|
+
- **Changelog** — <https://github.com/jonathascordeiro20/openinfra-logger/blob/main/CHANGELOG.md>
|
|
72
|
+
- **Project site** — <https://openinfralogger.fun>
|
|
73
|
+
|
|
74
|
+
## License
|
|
75
|
+
|
|
76
|
+
MIT — see [LICENSE](https://github.com/jonathascordeiro20/openinfra-logger/blob/main/LICENSE).
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
"""
|
|
2
|
+
OpenInfra Logger
|
|
3
|
+
Critical infrastructure library for structured observability.
|
|
4
|
+
|
|
5
|
+
@author Jonathas Cordeiro (@jonathascordeiro20)
|
|
6
|
+
@license MIT
|
|
7
|
+
@copyright (c) 2026 Jonathas Cordeiro
|
|
8
|
+
"""
|
|
9
|
+
import json
|
|
10
|
+
import logging
|
|
11
|
+
import datetime
|
|
12
|
+
import urllib.request
|
|
13
|
+
import urllib.error
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _utc_now_iso():
|
|
17
|
+
"""ISO-8601 UTC timestamp with trailing 'Z', forward-compatible with Python 3.12+."""
|
|
18
|
+
return datetime.datetime.now(datetime.timezone.utc).isoformat().replace('+00:00', 'Z')
|
|
19
|
+
|
|
20
|
+
# Internal configuration
|
|
21
|
+
_config = {
|
|
22
|
+
'transports': ['console'], # console, file, remote
|
|
23
|
+
'file_path': './app.log',
|
|
24
|
+
'remote_url': None,
|
|
25
|
+
'remote_headers': {'Content-Type': 'application/json'},
|
|
26
|
+
'default_metadata': {},
|
|
27
|
+
'formatter': 'default', # 'default', 'datadog', 'elastic'
|
|
28
|
+
'redact_keys': ['password', 'token', 'secret', 'api_key', 'credit_card'],
|
|
29
|
+
'batch_size': 100,
|
|
30
|
+
'flush_interval_ms': 2000
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
_logger = logging.getLogger("openinfra_logger")
|
|
34
|
+
_logger.setLevel(logging.DEBUG)
|
|
35
|
+
# Disable default propagation so we can handle outputs cleanly
|
|
36
|
+
_logger.propagate = False
|
|
37
|
+
|
|
38
|
+
# Ensure we have a stream handler for console transport
|
|
39
|
+
if not _logger.handlers:
|
|
40
|
+
_console_handler = logging.StreamHandler()
|
|
41
|
+
_console_handler.setFormatter(logging.Formatter('%(message)s'))
|
|
42
|
+
_logger.addHandler(_console_handler)
|
|
43
|
+
|
|
44
|
+
def configure(**kwargs):
|
|
45
|
+
"""
|
|
46
|
+
Configure the logger transports and defaults.
|
|
47
|
+
"""
|
|
48
|
+
_config.update(kwargs)
|
|
49
|
+
|
|
50
|
+
def redact_object(obj, keys_to_redact):
|
|
51
|
+
if isinstance(obj, dict):
|
|
52
|
+
result = {}
|
|
53
|
+
for k, v in obj.items():
|
|
54
|
+
if k.lower() in keys_to_redact:
|
|
55
|
+
result[k] = '[REDACTED]'
|
|
56
|
+
else:
|
|
57
|
+
result[k] = redact_object(v, keys_to_redact)
|
|
58
|
+
return result
|
|
59
|
+
elif isinstance(obj, list):
|
|
60
|
+
return [redact_object(item, keys_to_redact) for item in obj]
|
|
61
|
+
return obj
|
|
62
|
+
|
|
63
|
+
import threading
|
|
64
|
+
_remote_buffer = []
|
|
65
|
+
_flush_timer = None
|
|
66
|
+
|
|
67
|
+
def _flush_remote():
|
|
68
|
+
global _remote_buffer, _flush_timer
|
|
69
|
+
if not _remote_buffer:
|
|
70
|
+
return
|
|
71
|
+
|
|
72
|
+
payload = json.dumps(_remote_buffer)
|
|
73
|
+
_remote_buffer = []
|
|
74
|
+
|
|
75
|
+
if _flush_timer:
|
|
76
|
+
_flush_timer.cancel()
|
|
77
|
+
_flush_timer = None
|
|
78
|
+
|
|
79
|
+
try:
|
|
80
|
+
req = urllib.request.Request(
|
|
81
|
+
_config['remote_url'],
|
|
82
|
+
data=payload.encode('utf-8'),
|
|
83
|
+
headers=_config['remote_headers'],
|
|
84
|
+
method='POST'
|
|
85
|
+
)
|
|
86
|
+
urllib.request.urlopen(req, timeout=2.0)
|
|
87
|
+
except Exception as e:
|
|
88
|
+
_logger.error(json.dumps({
|
|
89
|
+
"level": "error",
|
|
90
|
+
"message": f"OpenInfra Logger: Failed to send remote logs batch: {str(e)}",
|
|
91
|
+
"timestamp": _utc_now_iso()
|
|
92
|
+
}))
|
|
93
|
+
|
|
94
|
+
def extract_trace_context():
|
|
95
|
+
"""
|
|
96
|
+
Attempt to extract trace context if OpenTelemetry is active.
|
|
97
|
+
Zero-dependency check.
|
|
98
|
+
"""
|
|
99
|
+
try:
|
|
100
|
+
import opentelemetry.trace
|
|
101
|
+
span = opentelemetry.trace.get_current_span()
|
|
102
|
+
if span and span.is_recording():
|
|
103
|
+
ctx = span.get_span_context()
|
|
104
|
+
if ctx.is_valid:
|
|
105
|
+
return {
|
|
106
|
+
'trace_id': format(ctx.trace_id, "032x"),
|
|
107
|
+
'span_id': format(ctx.span_id, "016x")
|
|
108
|
+
}
|
|
109
|
+
except ImportError:
|
|
110
|
+
pass
|
|
111
|
+
return {}
|
|
112
|
+
|
|
113
|
+
def _dispatch(log_entry, original_level):
|
|
114
|
+
redacted_entry = redact_object(log_entry, _config['redact_keys'])
|
|
115
|
+
output = json.dumps(redacted_entry)
|
|
116
|
+
|
|
117
|
+
# 1. Console transport
|
|
118
|
+
if 'console' in _config['transports']:
|
|
119
|
+
if original_level == 'error':
|
|
120
|
+
_logger.error(output)
|
|
121
|
+
elif original_level == 'warn':
|
|
122
|
+
_logger.warning(output)
|
|
123
|
+
elif original_level == 'debug':
|
|
124
|
+
_logger.debug(output)
|
|
125
|
+
else:
|
|
126
|
+
_logger.info(output)
|
|
127
|
+
|
|
128
|
+
# 2. File transport
|
|
129
|
+
if 'file' in _config['transports'] and _config.get('file_path'):
|
|
130
|
+
try:
|
|
131
|
+
with open(_config['file_path'], 'a') as f:
|
|
132
|
+
f.write(output + '\n')
|
|
133
|
+
except Exception as e:
|
|
134
|
+
_logger.error(json.dumps({
|
|
135
|
+
"level": "error",
|
|
136
|
+
"message": f"OpenInfra Logger: Failed to write to log file: {str(e)}",
|
|
137
|
+
"timestamp": _utc_now_iso()
|
|
138
|
+
}))
|
|
139
|
+
|
|
140
|
+
# 3. Remote transport
|
|
141
|
+
global _flush_timer
|
|
142
|
+
if 'remote' in _config['transports'] and _config.get('remote_url'):
|
|
143
|
+
_remote_buffer.append(redacted_entry)
|
|
144
|
+
if len(_remote_buffer) >= _config['batch_size']:
|
|
145
|
+
_flush_remote()
|
|
146
|
+
elif _flush_timer is None:
|
|
147
|
+
_flush_timer = threading.Timer(_config['flush_interval_ms'] / 1000.0, _flush_remote)
|
|
148
|
+
_flush_timer.daemon = True
|
|
149
|
+
_flush_timer.start()
|
|
150
|
+
|
|
151
|
+
def log(message: str, level: str = 'info', metadata: dict = None):
|
|
152
|
+
"""
|
|
153
|
+
Emits a structured JSON log.
|
|
154
|
+
"""
|
|
155
|
+
if metadata is None:
|
|
156
|
+
metadata = {}
|
|
157
|
+
|
|
158
|
+
normalized_level = level.lower()
|
|
159
|
+
valid_levels = {'debug', 'info', 'warn', 'error'}
|
|
160
|
+
|
|
161
|
+
if normalized_level not in valid_levels:
|
|
162
|
+
_logger.warning(json.dumps({
|
|
163
|
+
"level": "warn",
|
|
164
|
+
"message": f"Invalid log level '{level}' provided, falling back to 'info'",
|
|
165
|
+
"timestamp": _utc_now_iso()
|
|
166
|
+
}))
|
|
167
|
+
normalized_level = 'info'
|
|
168
|
+
|
|
169
|
+
log_entry = {
|
|
170
|
+
"timestamp": _utc_now_iso(),
|
|
171
|
+
"level": normalized_level,
|
|
172
|
+
"message": message,
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
trace_context = extract_trace_context()
|
|
176
|
+
|
|
177
|
+
# Merge default metadata, trace context, and specific metadata
|
|
178
|
+
log_entry.update(_config['default_metadata'])
|
|
179
|
+
log_entry.update(trace_context)
|
|
180
|
+
log_entry.update(metadata)
|
|
181
|
+
|
|
182
|
+
if _config['formatter'] == 'datadog':
|
|
183
|
+
log_entry['status'] = log_entry.pop('level', 'info')
|
|
184
|
+
if 'trace_id' in log_entry:
|
|
185
|
+
log_entry['dd.trace_id'] = log_entry.pop('trace_id')
|
|
186
|
+
if 'span_id' in log_entry:
|
|
187
|
+
log_entry['dd.span_id'] = log_entry.pop('span_id')
|
|
188
|
+
elif _config['formatter'] == 'elastic':
|
|
189
|
+
log_entry['@timestamp'] = log_entry.pop('timestamp')
|
|
190
|
+
log_entry['log.level'] = log_entry.pop('level', 'info')
|
|
191
|
+
|
|
192
|
+
_dispatch(log_entry, normalized_level)
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: openinfra-logger
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Universal structured logging and observability for Node, Python, Go and Rust — built from the standard library only. Native batching, auto-redaction, OpenTelemetry-aware, with native Datadog and Elastic (ECS) formatters.
|
|
5
|
+
Home-page: https://github.com/jonathascordeiro20/openinfra-logger
|
|
6
|
+
Author: Jonathas
|
|
7
|
+
Author-email: Jonathas Cordeiro <jonathas.cordeiro2023@gmail.com>
|
|
8
|
+
License: MIT
|
|
9
|
+
Project-URL: Homepage, https://openinfralogger.fun
|
|
10
|
+
Project-URL: Repository, https://github.com/jonathascordeiro20/openinfra-logger
|
|
11
|
+
Project-URL: Documentation, https://github.com/jonathascordeiro20/openinfra-logger#readme
|
|
12
|
+
Project-URL: Changelog, https://github.com/jonathascordeiro20/openinfra-logger/blob/main/CHANGELOG.md
|
|
13
|
+
Project-URL: Bug Tracker, https://github.com/jonathascordeiro20/openinfra-logger/issues
|
|
14
|
+
Keywords: logging,logger,structured-logging,observability,telemetry,opentelemetry,datadog,elasticsearch,ecs,loki,zero-dependency,lgpd,gdpr,redaction
|
|
15
|
+
Classifier: Development Status :: 4 - Beta
|
|
16
|
+
Classifier: Intended Audience :: Developers
|
|
17
|
+
Classifier: Intended Audience :: System Administrators
|
|
18
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
19
|
+
Classifier: Operating System :: OS Independent
|
|
20
|
+
Classifier: Programming Language :: Python :: 3
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
25
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
26
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
27
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
28
|
+
Classifier: Topic :: System :: Logging
|
|
29
|
+
Classifier: Typing :: Typed
|
|
30
|
+
Requires-Python: >=3.6
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
32
|
+
License-File: LICENSE
|
|
33
|
+
Provides-Extra: opentelemetry
|
|
34
|
+
Requires-Dist: opentelemetry-api>=1.20; extra == "opentelemetry"
|
|
35
|
+
Dynamic: author
|
|
36
|
+
Dynamic: home-page
|
|
37
|
+
Dynamic: license-file
|
|
38
|
+
Dynamic: requires-python
|
|
39
|
+
|
|
40
|
+
# OpenInfra Logger — Python
|
|
41
|
+
|
|
42
|
+
[](https://pypi.org/project/openinfra-logger/)
|
|
43
|
+
[](https://github.com/jonathascordeiro20/openinfra-logger/blob/main/LICENSE)
|
|
44
|
+
[](https://pypi.org/project/openinfra-logger/)
|
|
45
|
+
|
|
46
|
+
**OpenInfra Logger** is a zero-dependency, structured logging library — built from the Python standard library only — with native batching, auto-redaction, OpenTelemetry trace injection, and Datadog/Elastic formatters.
|
|
47
|
+
|
|
48
|
+
The same JSON shape is emitted by sibling implementations for **Node.js, Go and Rust**, making polyglot stacks observable with a single log format.
|
|
49
|
+
|
|
50
|
+
## Install
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
pip install openinfra-logger
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Quickstart
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
from openinfra_logger import log, configure
|
|
60
|
+
|
|
61
|
+
log("System initialized", "info")
|
|
62
|
+
log("Failed to parse payload", "error", {"request_id": "abc-123"})
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## File + remote with batching
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
from openinfra_logger import log, configure
|
|
69
|
+
|
|
70
|
+
configure(
|
|
71
|
+
transports=["console", "file", "remote"],
|
|
72
|
+
file_path="./production.log",
|
|
73
|
+
remote_url="https://logs.my-infrastructure.com/ingest",
|
|
74
|
+
default_metadata={"service": "payment-gateway", "env": "production"},
|
|
75
|
+
batch_size=100,
|
|
76
|
+
flush_interval_ms=2000,
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
log("Payment processed", "info", {"transaction_id": "abc-456"})
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Datadog / Elastic formatters
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
configure(formatter="datadog") # renames level → status, trace_id → dd.trace_id
|
|
86
|
+
# configure(formatter="elastic") # renames timestamp → @timestamp, level → log.level
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Auto-redaction (LGPD / GDPR)
|
|
90
|
+
|
|
91
|
+
Sensitive keys (`password`, `token`, `secret`, `api_key`, `credit_card`) are recursively replaced with `[REDACTED]` before any transport sees the entry. Case-insensitive on key names. Override via `redact_keys=[...]` in `configure(...)`.
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
log("Login", "info", {"user": "alice", "password": "p@ss"})
|
|
95
|
+
# → "...","user":"alice","password":"[REDACTED]"
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## OpenTelemetry tracing
|
|
99
|
+
|
|
100
|
+
If an OTel span is active in the current context, `trace_id` and `span_id` are picked up automatically. Install the optional extra to enable detection:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
pip install "openinfra-logger[opentelemetry]"
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Links
|
|
107
|
+
|
|
108
|
+
- **Source** — <https://github.com/jonathascordeiro20/openinfra-logger>
|
|
109
|
+
- **Issues** — <https://github.com/jonathascordeiro20/openinfra-logger/issues>
|
|
110
|
+
- **Changelog** — <https://github.com/jonathascordeiro20/openinfra-logger/blob/main/CHANGELOG.md>
|
|
111
|
+
- **Project site** — <https://openinfralogger.fun>
|
|
112
|
+
|
|
113
|
+
## License
|
|
114
|
+
|
|
115
|
+
MIT — see [LICENSE](https://github.com/jonathascordeiro20/openinfra-logger/blob/main/LICENSE).
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
CHANGELOG.md
|
|
2
|
+
LICENSE
|
|
3
|
+
MANIFEST.in
|
|
4
|
+
README.md
|
|
5
|
+
pyproject.toml
|
|
6
|
+
setup.py
|
|
7
|
+
openinfra_logger/__init__.py
|
|
8
|
+
openinfra_logger.egg-info/PKG-INFO
|
|
9
|
+
openinfra_logger.egg-info/SOURCES.txt
|
|
10
|
+
openinfra_logger.egg-info/dependency_links.txt
|
|
11
|
+
openinfra_logger.egg-info/requires.txt
|
|
12
|
+
openinfra_logger.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
openinfra_logger
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "openinfra-logger"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Universal structured logging and observability for Node, Python, Go and Rust — built from the standard library only. Native batching, auto-redaction, OpenTelemetry-aware, with native Datadog and Elastic (ECS) formatters."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.8"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "Jonathas Cordeiro", email = "jonathas.cordeiro2023@gmail.com" }
|
|
14
|
+
]
|
|
15
|
+
keywords = [
|
|
16
|
+
"logging", "logger", "structured-logging", "observability",
|
|
17
|
+
"telemetry", "opentelemetry", "datadog", "elasticsearch",
|
|
18
|
+
"ecs", "loki", "zero-dependency", "lgpd", "gdpr", "redaction"
|
|
19
|
+
]
|
|
20
|
+
classifiers = [
|
|
21
|
+
"Development Status :: 4 - Beta",
|
|
22
|
+
"Intended Audience :: Developers",
|
|
23
|
+
"Intended Audience :: System Administrators",
|
|
24
|
+
"License :: OSI Approved :: MIT License",
|
|
25
|
+
"Operating System :: OS Independent",
|
|
26
|
+
"Programming Language :: Python :: 3",
|
|
27
|
+
"Programming Language :: Python :: 3.8",
|
|
28
|
+
"Programming Language :: Python :: 3.9",
|
|
29
|
+
"Programming Language :: Python :: 3.10",
|
|
30
|
+
"Programming Language :: Python :: 3.11",
|
|
31
|
+
"Programming Language :: Python :: 3.12",
|
|
32
|
+
"Programming Language :: Python :: 3.13",
|
|
33
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
34
|
+
"Topic :: System :: Logging",
|
|
35
|
+
"Typing :: Typed"
|
|
36
|
+
]
|
|
37
|
+
# No runtime dependencies. The package is built from the standard library only.
|
|
38
|
+
dependencies = []
|
|
39
|
+
|
|
40
|
+
[project.urls]
|
|
41
|
+
Homepage = "https://openinfralogger.fun"
|
|
42
|
+
Repository = "https://github.com/jonathascordeiro20/openinfra-logger"
|
|
43
|
+
Documentation = "https://github.com/jonathascordeiro20/openinfra-logger#readme"
|
|
44
|
+
Changelog = "https://github.com/jonathascordeiro20/openinfra-logger/blob/main/CHANGELOG.md"
|
|
45
|
+
"Bug Tracker" = "https://github.com/jonathascordeiro20/openinfra-logger/issues"
|
|
46
|
+
|
|
47
|
+
[project.optional-dependencies]
|
|
48
|
+
opentelemetry = ["opentelemetry-api>=1.20"]
|
|
49
|
+
|
|
50
|
+
[tool.setuptools.packages.find]
|
|
51
|
+
include = ["openinfra_logger*"]
|
|
52
|
+
exclude = ["tests*"]
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from setuptools import setup, find_packages
|
|
2
|
+
|
|
3
|
+
setup(
|
|
4
|
+
name="openinfra-logger",
|
|
5
|
+
version="0.1.0",
|
|
6
|
+
description="Critical structured logging and observability library for modern infrastructure",
|
|
7
|
+
author="Jonathas",
|
|
8
|
+
url="https://github.com/jonathascordeiro20/openinfra-logger",
|
|
9
|
+
packages=find_packages(),
|
|
10
|
+
install_requires=[],
|
|
11
|
+
classifiers=[
|
|
12
|
+
"Programming Language :: Python :: 3",
|
|
13
|
+
"License :: OSI Approved :: MIT License",
|
|
14
|
+
"Operating System :: OS Independent",
|
|
15
|
+
],
|
|
16
|
+
python_requires='>=3.6',
|
|
17
|
+
)
|