Metadata-Version: 2.5
Name: ko-observability
Version: 0.3.0
Summary: Org-wide OpenTelemetry bootstrap for Python services
Project-URL: Repository, https://github.com/Komus-ai/ko-observability
Project-URL: Documentation, https://github.com/Komus-ai/ko-observability/tree/main/docs
Author-email: Komus-ai <dev@cinco.ai>
License: MIT
Keywords: observability,opentelemetry,otlp,structlog
Classifier: Development Status :: 4 - Beta
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Requires-Python: >=3.12
Requires-Dist: opentelemetry-api>=1.27.0
Requires-Dist: opentelemetry-exporter-otlp-proto-common>=1.27.0
Requires-Dist: opentelemetry-exporter-otlp-proto-grpc>=1.27.0
Requires-Dist: opentelemetry-sdk>=1.27.0
Requires-Dist: protobuf<8.0,>=5.0
Requires-Dist: structlog>=24.4.0
Provides-Extra: dev
Requires-Dist: httpx>=0.27.0; extra == 'dev'
Requires-Dist: mypy>=1.13.0; extra == 'dev'
Requires-Dist: opentelemetry-sdk>=1.27.0; extra == 'dev'
Requires-Dist: pytest>=8.3.0; extra == 'dev'
Requires-Dist: ruff>=0.8.0; extra == 'dev'
Provides-Extra: elasticsearch
Requires-Dist: opentelemetry-instrumentation-elasticsearch>=0.48b0; extra == 'elasticsearch'
Provides-Extra: fastapi
Requires-Dist: fastapi>=0.115.0; extra == 'fastapi'
Requires-Dist: opentelemetry-instrumentation-fastapi>=0.48b0; extra == 'fastapi'
Provides-Extra: httpx
Requires-Dist: opentelemetry-instrumentation-httpx>=0.48b0; extra == 'httpx'
Provides-Extra: query
Requires-Dist: httpx>=0.27.0; extra == 'query'
Description-Content-Type: text/markdown

# ko-observability (Python)

Org-wide OpenTelemetry bootstrap for Python services: one-call, idempotent
`init_otel` for process startup, lazy `get_tracer` / `get_meter` accessors for library code
that doesn't own process lifecycle, and a structlog processor (`otel_bridge` /
`make_otel_bridge`) that stamps each log line with the active span's `trace_id`/`span_id`.

## Install

```bash
pip install ko-observability
```

Optional extras, install alongside the base package as needed:

```bash
# Auto-instrument FastAPI request/response spans
pip install "ko-observability[fastapi]"

# Auto-instrument outbound httpx calls
pip install "ko-observability[httpx]"

# Auto-instrument the Elasticsearch client
pip install "ko-observability[elasticsearch]"

# Read your own service's telemetry back out (admin-panel style queries)
pip install "ko-observability[query]"
```

## Quick start

```python
from ko_observability import init_otel, get_tracer

init_otel(service_name="my-service", service_namespace="komus")

tracer = get_tracer(__name__)
with tracer.start_as_current_span("do-work"):
    ...
```

Call `init_otel` exactly once at process startup. It's safe to call again (or to let library
code fall back to `get_tracer`/`get_meter` before it runs) — both paths are no-ops on top of an
already-configured provider. `OTEL_SDK_DISABLED=true` turns the whole thing into a no-op, and a
misconfigured exporter degrades to "telemetry doesn't work" rather than crashing the host process.

### Where does it go?

| `TELEMETRY_DESTINATION` | Output |
|---|---|
| `file` (default) | `$TELEMETRY_FILE_DIR/{traces,metrics,logs}.jsonl` (default `./telemetry`), one OTLP/JSON request per line |
| `otlp` | OTLP/gRPC to `OTEL_EXPORTER_OTLP_ENDPOINT` (default `http://localhost:4317`) |

Full contract: [docs/otel.md → Local file mode](../../docs/otel.md#local-file-mode-the-default).

## Execution helpers

Generic wrappers so service code never imports `opentelemetry`. Full guide with Go and JS
equivalents: [docs/02-utilisation.md](../../docs/02-utilisation.md#plomberie-dexécution).

| Helper | Signature | Role |
|---|---|---|
| `current_traceparent` | `() -> str \| None` | W3C `traceparent` of the active span, `None` outside a valid span |
| `extract_traceparent` | `(traceparent: str \| None) -> Context \| None` | parent context, `None` if absent/invalid (never raises) |
| `start_execution_span` | `(name, *, traceparent=None, attributes=None)` context manager | current span for one execution, continuing `traceparent`; exceptions recorded (ERROR) and re-raised |
| `set_current_span_attributes` | `(attrs: Mapping) -> None` | attributes on the current span; no-op if not recording |
| `execution_context` | `(**kv)` context manager | binds structlog contextvars, restored on exit; stamped on spans via `init_otel(context_attr_map=...)` |
| `force_flush` | `(timeout_ms: int = 2000) -> None` | flushes tracer and logger providers; never raises |

Re-exports: `Span`, `SpanKind`, `Status`, `StatusCode`, `InMemoryMetricReader`.

```python
from ko_observability import start_execution_span, set_current_span_attributes, force_flush

with start_execution_span("task.run", traceparent=incoming_tp, attributes={"task.id": "t-1"}):
    set_current_span_attributes({"step": "load"})
force_flush()
```

## Reading telemetry back (`query` extra)

```python
from ko_observability.query import get_query_client

client = get_query_client()
traces = client.list_traces(limit=20)
```

Scoped to the caller's own `service_name` by default. See [`docs/query.md`](../../docs/query.md).

## Docs

Full documentation — env vars, the logs/metrics pipelines, semantic conventions, and the
Collector deployment this SDK ships telemetry to — lives in the repository:

https://github.com/Komus-ai/ko-observability/tree/main/docs
