Metadata-Version: 2.4
Name: betshare-infra-telemetry
Version: 0.1.0
Summary: Shared OpenTelemetry bootstrap for BetShareMarket Python services
License: ISC
Author: BetShareMarket
Author-email: dev@betsharemarket.com
Requires-Python: >=3.10,<4.0
Classifier: License :: OSI Approved
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Requires-Dist: opentelemetry-api (>=1.33.1,<2.0.0)
Requires-Dist: opentelemetry-exporter-otlp-proto-http (>=1.33.1,<2.0.0)
Requires-Dist: opentelemetry-sdk (>=1.33.1,<2.0.0)
Description-Content-Type: text/markdown

# betshare-infra-telemetry

Shared **OpenTelemetry bootstrap** for BetShareMarket Python services. One
`initialize_telemetry(TelemetryConfig(...))` call wires up OTLP/HTTP trace and
metric exporters, a per-process resource (service name/version/namespace,
instance id, deployment environment, pipeline family), a `starts_total` counter
and an `uptime_seconds` observable gauge, and registers an `atexit` shutdown.

```python
from betshare_infra_telemetry import TelemetryConfig, initialize_telemetry

initialize_telemetry(
    TelemetryConfig(
        service_name="consumer",
        service_version="0.1.0",
        pipeline_family="ingest",
    )
)
```

Import name: `betshare_infra_telemetry`. Distribution name: `betshare-infra-telemetry`.

## Why

This module was copy-pasted **byte-identical** (172 lines, md5 `6b16c9d0…`)
across `consumer`, `betfair-exchange-scraper`, `joiner-events` and `crawler`,
and existed as a ~266-line superset in `azure-proxy-pool`. Centralising it stops
the four copies from drifting and gives `azure-proxy-pool` a clean place to opt
into its extras.

## Public interface

```python
@dataclass
class TelemetryConfig:
    service_name: str
    service_version: str
    pipeline_family: str
    environment: str | None = None
    service_namespace: str = "betsharemarket"

def initialize_telemetry(
    config: TelemetryConfig,
    *,
    enable_log_export: bool = False,
    loguru_logger_name: str | None = None,
) -> TelemetryRuntime: ...
```

`initialize_telemetry` is **idempotent** (a module-level singleton; a second
call returns the same `TelemetryRuntime`) and **degrades gracefully**:

- if the OTel SDK is not importable → returns a no-provider runtime;
- if `OTEL_SDK_DISABLED=true` → bare providers, no exporters;
- if no OTLP endpoint env var is set → defaults to `http://127.0.0.1:4318`
  (exporters are built; a missing collector only fails at delivery time, not at
  init).

### Environment variables

- `OBSERVABILITY_OTLP_ENDPOINT` (preferred) / `OTEL_EXPORTER_OTLP_ENDPOINT` —
  base OTLP/HTTP endpoint; any `/v1/{traces,metrics,logs}` suffix is stripped.
- `OTEL_EXPORTER_OTLP_HEADERS` — `key=value,key=value` header list.
- `OTEL_METRIC_EXPORT_INTERVAL` — metric export interval ms (default `15000`).
- `OTEL_SDK_DISABLED` — `true` disables exporters.
- `ENV` / `NODE_ENV` — fallback for `deployment.environment` (default
  `development`); overridden by `TelemetryConfig.environment`.
- `LOG_LEVEL` — only when `enable_log_export=True`; loguru sink level.

### Opt-in log export (used by `azure-proxy-pool`)

`azure-proxy-pool`'s original superset additionally exported **logs** over OTLP
and bridged **loguru** into the OTel logging pipeline. That behaviour is
preserved here behind a flag, **off by default** so the four common services
keep their exact prior behaviour:

```python
initialize_telemetry(
    TelemetryConfig(service_name="azure-proxy-pool", service_version="0.1.0",
                    pipeline_family="proxy"),
    enable_log_export=True,
    loguru_logger_name="azure-proxy-pool",
)
```

When enabled (and the OTel logs SDK + loguru are installed) this sets a
`LoggerProvider` with an OTLP log exporter and adds a loguru sink that forwards
records to a named stdlib logger carrying the OTLP handler. If the logs SDK or
loguru is missing it silently no-ops (`logger_provider` stays `None`).

## Install

**Production (private registry, version-pinned per service):**

```toml
betshare-infra-telemetry = "^0.1.0"
```

**Local development (PATH dependency, used by the migration):**

```toml
betshare-infra-telemetry = { path = "../../packages/py-infra-telemetry", develop = true }
```

Or with pip: `pip install -e packages/py-infra-telemetry`.

## Test

```bash
cd packages/py-infra-telemetry && pip install -e . && pip install pytest && python -m pytest
```

