e-volv-logs 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,145 @@
1
+ # See https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files for more about ignoring files.
2
+
3
+ # compiled output
4
+ dist
5
+ tmp
6
+ out-tsc
7
+
8
+ # dependencies
9
+ node_modules
10
+
11
+ # IDEs and editors
12
+ /.idea
13
+ .project
14
+ .classpath
15
+ .c9/
16
+ *.launch
17
+ .settings/
18
+ *.sublime-workspace
19
+
20
+ # IDE - VSCode
21
+ .vscode/*
22
+ !.vscode/settings.json
23
+ !.vscode/tasks.json
24
+ !.vscode/launch.json
25
+ !.vscode/extensions.json
26
+
27
+ # misc
28
+ /.sass-cache
29
+ /connect.lock
30
+ /coverage
31
+ /libpeerconnection.log
32
+ npm-debug.log
33
+ yarn-error.log
34
+ testem.log
35
+ /typings
36
+
37
+ # System Files
38
+ .DS_Store
39
+ Thumbs.db
40
+
41
+ .nx/cache
42
+ .nx/workspace-data
43
+ .cursor/rules/nx-rules.mdc
44
+ .github/instructions/nx.instructions.md
45
+
46
+ .claude/worktrees
47
+ .claude/settings.local.json
48
+ .nx/polygraph
49
+
50
+ # Next.js
51
+ .next
52
+ out
53
+ # Terraform
54
+ infra/.terraform/
55
+ infra/*.tfstate
56
+ infra/*.tfstate.backup
57
+ # Terraform writes rollback copies as terraform.tfstate.<epoch>.backup, which
58
+ # the pattern above does not match — the timestamp sits before `.backup`. One
59
+ # such file was sitting untracked on 2026-08-09 holding 68 plaintext
60
+ # secret_data values (Surreal password, JWT secret, vault key, Stripe live key,
61
+ # GitHub App private key), fully stageable by `git add -A`.
62
+ infra/*.tfstate.*.backup
63
+ infra/terraform.tfvars
64
+ # Any sibling of the real tfvars — .bak, .orig, .save, dated copies. The exact
65
+ # path above does not match them, so an editor backup or a hand-rolled copy of
66
+ # a file holding every production secret lands in `git status` as untracked and
67
+ # would be staged by `git add -A`. Nearly happened on 2026-08-09.
68
+ infra/terraform.tfvars.*
69
+ !infra/terraform.tfvars.example
70
+ infra/.terraform.lock.hcl
71
+
72
+ # Environment files — never commit; contain live credentials
73
+ .env
74
+ .env.local
75
+
76
+ # Untracked 2026-08-07: this working copy accumulates real credential values as
77
+ # the remediation gates are worked through, so it cannot be a tracked file.
78
+ # The sanitised instructions live in docs/GATE-2-CREDENTIALS.md, which stays
79
+ # tracked and must never carry a real value.
80
+ docs/PRODUCTION-FIX-PLAN.md
81
+ .env.*.local
82
+ apps/*/.env
83
+ apps/*/.env.local
84
+
85
+ # Python bytecode caches
86
+ __pycache__/
87
+ *.pyc
88
+
89
+ # Nx/TS incremental build info
90
+ apps/evolve-backend/tsconfig.tsbuildinfo
91
+
92
+ # Test output (nx python targets write here)
93
+ reports/
94
+
95
+ # Terraform plan files embed sensitive variable values — never commit them.
96
+ # `-out=` takes an arbitrary name, so the two patterns below only catch plans a
97
+ # person happened to call "tfplan". Files named tfdom/tfgh/tfpost were found
98
+ # untracked on 2026-08-09. Plans are zip archives, so a name-based rule is the
99
+ # only defence — hence the explicit directory below for anything ad hoc.
100
+ infra/tfplan
101
+ infra/*.tfplan
102
+ infra/tf-plans/
103
+
104
+ # Playwright MCP writes browser console logs, traces and screenshots here when
105
+ # an agent drives the app. Session artefacts, not source — and they capture
106
+ # whatever the logged-in page happened to print, so they do not belong in git.
107
+ .playwright-mcp/
108
+
109
+ # Coverage artefacts — rewritten by every test run, never useful in a diff.
110
+ apps/evolve-ai/.coverage
111
+ test-output
112
+
113
+ # E2E build/output artefacts
114
+ apps/*-e2e/out-tsc/
115
+ apps/*-e2e/test-output/
116
+ playwright-report/
117
+ test-results/
118
+
119
+ # Memtrace (local code index — machine-local, never committed)
120
+ .memdb/
121
+ .memtrace/
122
+ .memtrace-workspace
123
+
124
+ # Local MCP server config — holds a Memtrace licence key.
125
+ .mcp.json
126
+
127
+ # Written by the Firebase Auth emulator that docs/E2E-TESTING.md tells you to start.
128
+ firebase-debug.log
129
+ firestore-debug.log
130
+ ui-debug.log
131
+
132
+ # TypeScript incremental build state (packages/logs-js writes one at its root).
133
+ *.tsbuildinfo
134
+ .logs-keys.json
135
+ .logs-keys-mspeed.json
136
+
137
+ # Recording rig: a saved production session, equivalent to a password.
138
+ tools/record/.auth/
139
+
140
+ # Recording rig: raw .webm captures and crop manifests. Only the rendered
141
+ # mp4/jpg under public/media/tour is kept.
142
+ tools/record/out/
143
+
144
+ # Recording rig scratch probes.
145
+ tools/record/*.tmp.ts
@@ -0,0 +1,158 @@
1
+ Metadata-Version: 2.5
2
+ Name: e-volv-logs
3
+ Version: 0.1.0
4
+ Summary: Evolve Log Manager SDK — logs, traces and error capture for Python.
5
+ Project-URL: Homepage, https://github.com/Pactify-Pty-Ltd/theevolve/tree/main/packages/logs-py
6
+ Project-URL: Source, https://github.com/Pactify-Pty-Ltd/theevolve
7
+ License: MIT
8
+ Requires-Python: >=3.10
9
+ Requires-Dist: httpx>=0.28.0
10
+ Provides-Extra: langchain
11
+ Requires-Dist: langchain-core>=0.3; extra == 'langchain'
12
+ Description-Content-Type: text/markdown
13
+
14
+ # e-volv-logs
15
+
16
+ Evolve Log Manager SDK for Python — batched log shipping, trace context and
17
+ error capture against the Evolve ingest endpoint
18
+ (`POST /api/public/v1/logs`). Companion: the Node.js SDK `@e-volv/logs`.
19
+
20
+ Python 3.10+.
21
+
22
+ ```bash
23
+ pip install e-volv-logs
24
+ ```
25
+
26
+ ## Quick start
27
+
28
+ ```python
29
+ import e_volv_logs as evolve_logs
30
+ from e_volv_logs import init, log, span, trace
31
+
32
+ init(
33
+ key="evk_…", # project ingest key
34
+ url="https://your-host/api/public/v1/logs",
35
+ service="api",
36
+ environment="production",
37
+ release="1.4.2",
38
+ )
39
+
40
+ log.info("order created", {"orderId": "o_1", "total": 42.5})
41
+ log.error("payment failed", {"orderId": "o_1"})
42
+
43
+ try:
44
+ await charge()
45
+ except Exception as err:
46
+ log.exception(err, {"orderId": "o_1"}) # exception.type/message/stack
47
+
48
+ # Traces live in contextvars — they flow across await. httpx/requests
49
+ # requests made inside a trace get a W3C traceparent header automatically.
50
+ with trace():
51
+ with span("db.query", {"table": "orders"}):
52
+ await db.query("SELECT …")
53
+ await httpx.get("https://internal/svc") # traceparent injected
54
+ ```
55
+
56
+ If `key` or `url` is missing, `init()` returns a no-op client and warns once.
57
+ The SDK never raises into user code.
58
+
59
+ ## Batching, retries, drops
60
+
61
+ - Flushes at **200 events**, **2 s** (age of the oldest buffered event), or a
62
+ **512 KB** payload — whichever comes first. Bodies are gzipped
63
+ (`Content-Encoding: gzip`).
64
+ - **429** retries with exponential backoff (0.5 s doubling, capped at 10 s),
65
+ up to 3 attempts. **413** halves the batch and retries.
66
+ - When the pending buffer exceeds **2× batch size**, the **oldest** events are
67
+ dropped; losses are visible on `client.dropped`.
68
+ - `atexit` flushes whatever is pending.
69
+
70
+ ## Options
71
+
72
+ `init(key=, url=, service=, environment=, release=, redact_keys=[...],
73
+ sample_rate=1.0, capture_excepthook=True)` — `redact_keys` are merged into the
74
+ backstop regex `password|secret|token|authorization|cookie|set-cookie|api[-_]?key`
75
+ (case-insensitive), applied to attrs before queueing.
76
+
77
+ ## stdlib logging
78
+
79
+ ```python
80
+ import logging
81
+ from e_volv_logs import init, LogHandler
82
+
83
+ init(key="evk_…", url="…", service="api")
84
+ logging.getLogger("myapp").addHandler(LogHandler())
85
+ ```
86
+
87
+ Levels map to OTel severities (DEBUG→5, INFO→9, WARNING→13, ERROR→17,
88
+ CRITICAL→21). `extra={...}` on the log call becomes event attrs; `exc_info`
89
+ becomes `exception.*` fields. `sys.excepthook` capture is on by default.
90
+
91
+ ## FastAPI / Starlette
92
+
93
+ ```python
94
+ from e_volv_logs.integrations.fastapi import EvolveLogsMiddleware
95
+
96
+ app.add_middleware(EvolveLogsMiddleware) # one root span per request
97
+ ```
98
+
99
+ The middleware continues the trace when the inbound request carries an
100
+ `x-evolve-traceparent` header (Evolve backend → evolve-ai propagation),
101
+ otherwise starts a fresh root trace.
102
+
103
+ ## LangChain and LangGraph
104
+
105
+ ```bash
106
+ pip install "e-volv-logs[langchain]"
107
+ ```
108
+
109
+ ```python
110
+ from e_volv_logs.integrations.langchain import EvolveLogsCallbackHandler
111
+
112
+ result = graph.invoke(
113
+ {"question": "…"},
114
+ config={"callbacks": [EvolveLogsCallbackHandler()]},
115
+ )
116
+ ```
117
+
118
+ One span per LangChain run, nested by `parent_run_id`, all sharing one trace —
119
+ so a LangGraph node's tool calls and model calls appear inside the node on the
120
+ Evolve trace graph. Span names:
121
+
122
+ | Callback | Span name |
123
+ | -------------------------------------- | -------------- |
124
+ | `on_chain_start` with `langgraph_node` | `graph.<node>` |
125
+ | `on_chain_start` otherwise | `chain.<name>` |
126
+ | `on_tool_start` | `tool.<name>` |
127
+ | `on_llm_start` / `on_chat_model_start` | `llm.<model>` |
128
+
129
+ Errors end the span through `_log_span_error` (severity error, `exception.*`
130
+ attrs), exactly like `span()` raised inside a body.
131
+
132
+ ## Queue hops
133
+
134
+ A span on the consumer side joins the producer's trace only if the traceparent
135
+ travels with the job:
136
+
137
+ ```python
138
+ from e_volv_logs import run_with_traceparent, span, traceparent
139
+
140
+ # producer
141
+ queue.enqueue(work, payload, trace=traceparent())
142
+
143
+ # consumer: the body runs as a new hop of the producer's trace.
144
+ with run_with_traceparent(job.trace), span("queue.work", {"queue": "work"}):
145
+ handle(job)
146
+ ```
147
+
148
+ `run_with_traceparent(header)` parses a W3C traceparent; an absent or malformed
149
+ header starts a fresh trace, so a producer that sends nothing still yields a
150
+ trace of its own.
151
+
152
+ ## Development
153
+
154
+ ```bash
155
+ cd packages/logs-py
156
+ uv sync
157
+ uv run pytest tests/ -q
158
+ ```
@@ -0,0 +1,145 @@
1
+ # e-volv-logs
2
+
3
+ Evolve Log Manager SDK for Python — batched log shipping, trace context and
4
+ error capture against the Evolve ingest endpoint
5
+ (`POST /api/public/v1/logs`). Companion: the Node.js SDK `@e-volv/logs`.
6
+
7
+ Python 3.10+.
8
+
9
+ ```bash
10
+ pip install e-volv-logs
11
+ ```
12
+
13
+ ## Quick start
14
+
15
+ ```python
16
+ import e_volv_logs as evolve_logs
17
+ from e_volv_logs import init, log, span, trace
18
+
19
+ init(
20
+ key="evk_…", # project ingest key
21
+ url="https://your-host/api/public/v1/logs",
22
+ service="api",
23
+ environment="production",
24
+ release="1.4.2",
25
+ )
26
+
27
+ log.info("order created", {"orderId": "o_1", "total": 42.5})
28
+ log.error("payment failed", {"orderId": "o_1"})
29
+
30
+ try:
31
+ await charge()
32
+ except Exception as err:
33
+ log.exception(err, {"orderId": "o_1"}) # exception.type/message/stack
34
+
35
+ # Traces live in contextvars — they flow across await. httpx/requests
36
+ # requests made inside a trace get a W3C traceparent header automatically.
37
+ with trace():
38
+ with span("db.query", {"table": "orders"}):
39
+ await db.query("SELECT …")
40
+ await httpx.get("https://internal/svc") # traceparent injected
41
+ ```
42
+
43
+ If `key` or `url` is missing, `init()` returns a no-op client and warns once.
44
+ The SDK never raises into user code.
45
+
46
+ ## Batching, retries, drops
47
+
48
+ - Flushes at **200 events**, **2 s** (age of the oldest buffered event), or a
49
+ **512 KB** payload — whichever comes first. Bodies are gzipped
50
+ (`Content-Encoding: gzip`).
51
+ - **429** retries with exponential backoff (0.5 s doubling, capped at 10 s),
52
+ up to 3 attempts. **413** halves the batch and retries.
53
+ - When the pending buffer exceeds **2× batch size**, the **oldest** events are
54
+ dropped; losses are visible on `client.dropped`.
55
+ - `atexit` flushes whatever is pending.
56
+
57
+ ## Options
58
+
59
+ `init(key=, url=, service=, environment=, release=, redact_keys=[...],
60
+ sample_rate=1.0, capture_excepthook=True)` — `redact_keys` are merged into the
61
+ backstop regex `password|secret|token|authorization|cookie|set-cookie|api[-_]?key`
62
+ (case-insensitive), applied to attrs before queueing.
63
+
64
+ ## stdlib logging
65
+
66
+ ```python
67
+ import logging
68
+ from e_volv_logs import init, LogHandler
69
+
70
+ init(key="evk_…", url="…", service="api")
71
+ logging.getLogger("myapp").addHandler(LogHandler())
72
+ ```
73
+
74
+ Levels map to OTel severities (DEBUG→5, INFO→9, WARNING→13, ERROR→17,
75
+ CRITICAL→21). `extra={...}` on the log call becomes event attrs; `exc_info`
76
+ becomes `exception.*` fields. `sys.excepthook` capture is on by default.
77
+
78
+ ## FastAPI / Starlette
79
+
80
+ ```python
81
+ from e_volv_logs.integrations.fastapi import EvolveLogsMiddleware
82
+
83
+ app.add_middleware(EvolveLogsMiddleware) # one root span per request
84
+ ```
85
+
86
+ The middleware continues the trace when the inbound request carries an
87
+ `x-evolve-traceparent` header (Evolve backend → evolve-ai propagation),
88
+ otherwise starts a fresh root trace.
89
+
90
+ ## LangChain and LangGraph
91
+
92
+ ```bash
93
+ pip install "e-volv-logs[langchain]"
94
+ ```
95
+
96
+ ```python
97
+ from e_volv_logs.integrations.langchain import EvolveLogsCallbackHandler
98
+
99
+ result = graph.invoke(
100
+ {"question": "…"},
101
+ config={"callbacks": [EvolveLogsCallbackHandler()]},
102
+ )
103
+ ```
104
+
105
+ One span per LangChain run, nested by `parent_run_id`, all sharing one trace —
106
+ so a LangGraph node's tool calls and model calls appear inside the node on the
107
+ Evolve trace graph. Span names:
108
+
109
+ | Callback | Span name |
110
+ | -------------------------------------- | -------------- |
111
+ | `on_chain_start` with `langgraph_node` | `graph.<node>` |
112
+ | `on_chain_start` otherwise | `chain.<name>` |
113
+ | `on_tool_start` | `tool.<name>` |
114
+ | `on_llm_start` / `on_chat_model_start` | `llm.<model>` |
115
+
116
+ Errors end the span through `_log_span_error` (severity error, `exception.*`
117
+ attrs), exactly like `span()` raised inside a body.
118
+
119
+ ## Queue hops
120
+
121
+ A span on the consumer side joins the producer's trace only if the traceparent
122
+ travels with the job:
123
+
124
+ ```python
125
+ from e_volv_logs import run_with_traceparent, span, traceparent
126
+
127
+ # producer
128
+ queue.enqueue(work, payload, trace=traceparent())
129
+
130
+ # consumer: the body runs as a new hop of the producer's trace.
131
+ with run_with_traceparent(job.trace), span("queue.work", {"queue": "work"}):
132
+ handle(job)
133
+ ```
134
+
135
+ `run_with_traceparent(header)` parses a W3C traceparent; an absent or malformed
136
+ header starts a fresh trace, so a producer that sends nothing still yields a
137
+ trace of its own.
138
+
139
+ ## Development
140
+
141
+ ```bash
142
+ cd packages/logs-py
143
+ uv sync
144
+ uv run pytest tests/ -q
145
+ ```
@@ -0,0 +1,40 @@
1
+ """Evolve Log Manager SDK for Python (e-volv-logs)."""
2
+
3
+ from evolve_logs import * # noqa: F403
4
+ from evolve_logs import (
5
+ FLUSH_MS,
6
+ MAX_BATCH,
7
+ MAX_PAYLOAD_BYTES,
8
+ SEVERITY,
9
+ LogHandler,
10
+ LogsClient,
11
+ current_context,
12
+ get_client,
13
+ init,
14
+ log,
15
+ patch_httpx,
16
+ patch_requests,
17
+ run_with_traceparent,
18
+ span,
19
+ trace,
20
+ traceparent,
21
+ )
22
+
23
+ __all__ = [
24
+ "FLUSH_MS",
25
+ "MAX_BATCH",
26
+ "MAX_PAYLOAD_BYTES",
27
+ "SEVERITY",
28
+ "LogHandler",
29
+ "LogsClient",
30
+ "current_context",
31
+ "get_client",
32
+ "init",
33
+ "log",
34
+ "patch_httpx",
35
+ "patch_requests",
36
+ "run_with_traceparent",
37
+ "span",
38
+ "trace",
39
+ "traceparent",
40
+ ]
@@ -0,0 +1,2 @@
1
+ """Integrations for e_volv_logs."""
2
+ from evolve_logs.integrations import * # noqa: F403
@@ -0,0 +1,4 @@
1
+ """FastAPI integration re-export."""
2
+ from evolve_logs.integrations.fastapi import EvolveLogsMiddleware
3
+
4
+ __all__ = ["EvolveLogsMiddleware"]
@@ -0,0 +1,4 @@
1
+ """LangChain integration re-export."""
2
+ from evolve_logs.integrations.langchain import EvolveLogsCallbackHandler
3
+
4
+ __all__ = ["EvolveLogsCallbackHandler"]
@@ -0,0 +1,95 @@
1
+ """Evolve Log Manager SDK for Python.
2
+
3
+ Batches events (200 events / 2 s / 512 KB), gzips the payload, retries with
4
+ backoff on 429/413, and drops oldest when the pending buffer exceeds 2x the
5
+ batch size. Trace context lives in :mod:`contextvars`, so it flows across
6
+ ``await``. Everything is fail-silent: a logging SDK must never raise into user
7
+ code.
8
+ """
9
+
10
+ from evolve_logs.client import (
11
+ FLUSH_MS,
12
+ MAX_BATCH,
13
+ MAX_PAYLOAD_BYTES,
14
+ LogsClient,
15
+ init,
16
+ )
17
+ from evolve_logs.context import (
18
+ current_context,
19
+ run_with_traceparent,
20
+ span,
21
+ trace,
22
+ traceparent,
23
+ )
24
+ from evolve_logs.handler import LogHandler
25
+ from evolve_logs.patches import patch_httpx, patch_requests
26
+ from evolve_logs.types import SEVERITY
27
+
28
+ __all__ = [
29
+ "FLUSH_MS",
30
+ "MAX_BATCH",
31
+ "MAX_PAYLOAD_BYTES",
32
+ "SEVERITY",
33
+ "LogHandler",
34
+ "LogsClient",
35
+ "current_context",
36
+ "init",
37
+ "log",
38
+ "patch_httpx",
39
+ "patch_requests",
40
+ "run_with_traceparent",
41
+ "span",
42
+ "trace",
43
+ "traceparent",
44
+ ]
45
+
46
+
47
+ class _LogProxy:
48
+ """Module-level logger bound to the default client (set by ``init``)."""
49
+
50
+ def trace(self, message, attrs=None):
51
+ client = get_client()
52
+ if client:
53
+ client.trace(message, attrs)
54
+
55
+ def debug(self, message, attrs=None):
56
+ client = get_client()
57
+ if client:
58
+ client.debug(message, attrs)
59
+
60
+ def info(self, message, attrs=None):
61
+ client = get_client()
62
+ if client:
63
+ client.info(message, attrs)
64
+
65
+ def warn(self, message, attrs=None):
66
+ client = get_client()
67
+ if client:
68
+ client.warn(message, attrs)
69
+
70
+ warning = warn
71
+
72
+ def error(self, message, attrs=None):
73
+ client = get_client()
74
+ if client:
75
+ client.error(message, attrs)
76
+
77
+ def fatal(self, message, attrs=None):
78
+ client = get_client()
79
+ if client:
80
+ client.fatal(message, attrs)
81
+
82
+ def exception(self, err, attrs=None):
83
+ client = get_client()
84
+ if client:
85
+ client.exception(err, attrs)
86
+
87
+
88
+ log = _LogProxy()
89
+
90
+
91
+ def get_client() -> LogsClient | None:
92
+ """The client installed by :func:`init`, or None before init."""
93
+ from evolve_logs.client import get_client as _get
94
+
95
+ return _get()