Metadata-Version: 2.4
Name: fastx-opentelemetry
Version: 0.2.0
Summary: Code-based OpenTelemetry auto-instrumentation for Python
Author-email: jimo <jimo@foxmail.com>
License: Apache-2.0
Keywords: auto-instrumentation,observability,opentelemetry,tracing
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: System :: Monitoring
Requires-Python: >=3.7
Requires-Dist: importlib-metadata>=1.0; python_version < '3.8'
Requires-Dist: opentelemetry-api>=1.22
Requires-Dist: opentelemetry-instrumentation>=0.43
Requires-Dist: opentelemetry-sdk>=1.22
Provides-Extra: all
Requires-Dist: opentelemetry-exporter-otlp-proto-grpc>=1.22; extra == 'all'
Requires-Dist: opentelemetry-exporter-otlp-proto-http>=1.22; extra == 'all'
Provides-Extra: otlp-grpc
Requires-Dist: opentelemetry-exporter-otlp-proto-grpc>=1.22; extra == 'otlp-grpc'
Provides-Extra: otlp-http
Requires-Dist: opentelemetry-exporter-otlp-proto-http>=1.22; extra == 'otlp-http'
Description-Content-Type: text/markdown

# fastx-opentelemetry

OpenTelemetry 自动增强 SDK 封装，支持**零代码**和**代码**两种接入方式。自动发现并加载所有已安装的 instrumentation 库。

## 特性

- **零代码接入** — `fastx-otel-py-instrument python app.py` 一行命令完成全部增强，无需修改应用代码
- **代码接入** — `fastx_opentelemetry.init(service_name="my-app")` 完成全部配置
- **自动发现增强** — 自动检测并加载所有已安装的 `opentelemetry-instrumentation-*` 包
- **智能建议** — 检测已安装但未增强的库，提示安装对应 instrumentation 包
- **上下游服务串联** — 自动传播 Baggage 中的服务调用链（`parent.service.name` / `parent.service.names` / `current.service.name`）
- **Baggage → Span 属性** — 自动将 Baggage 条目复制为 Span 属性，无需手动操作
- **内置采样器** — 提供 `RulesBasedSampler`（按规则采样）和 `RateLimitingSampler`（限流采样）
- **自动资源检测** — 自动采集 hostname、IP、PID、instance ID 以及云平台属性（`cloud.platform`、`cloud.region` 等）
- **环境变量兼容** — 完全兼容 OpenTelemetry 标准环境变量
- **信号级端点** — 自动为 traces/metrics/logs 拼接 `/v1/traces`、`/v1/metrics`、`/v1/logs` 路径

## 安装

```bash
# 基础安装
pip install fastx-opentelemetry

# 带 OTLP gRPC 导出器
pip install fastx-opentelemetry[otlp-grpc]

# 带 OTLP HTTP 导出器
pip install fastx-opentelemetry[otlp-http]

# 全部安装
pip install fastx-opentelemetry[all]
```

---

## 方式一：零代码接入（推荐）

通过 `fastx-otel-py-instrument` 命令直接启动应用，无需修改任何代码。适用于已有项目快速接入、容器化部署等场景。

### 基本用法

```bash
# 直接用命令启动应用
fastx-otel-py-instrument python app.py

# 指定服务名和采集器地址
fastx-otel-py-instrument --service-name my-app --endpoint http://collector:4318 python app.py

# 传递应用参数
fastx-otel-py-instrument --service-name my-app python app.py --host 0.0.0.0 --port 8080
```

### 命令行参数

```
fastx-otel-py-instrument [选项] <命令> [参数...]
```

| 参数 | 说明 | 对应环境变量 |
|------|------|-------------|
| `--service-name NAME` | 服务名称 | `OTEL_SERVICE_NAME` |
| `--endpoint URL` | OTLP Collector 地址 | `OTEL_EXPORTER_OTLP_ENDPOINT` |
| `--protocol PROTO` | 传输协议：`http/protobuf`（默认）或 `grpc` | `OTEL_EXPORTER_OTLP_PROTOCOL` |
| `--compression MODE` | 压缩方式：`gzip`（默认）、`deflate` | `OTEL_EXPORTER_OTLP_COMPRESSION` |
| `--headers KV` | 请求头，逗号分隔的 `key=value` 对 | `OTEL_EXPORTER_OTLP_HEADERS` |
| `--disabled-instrumentations LIST` | 禁用的增强，逗号分隔，`*` 禁用全部 | `OTEL_PYTHON_DISABLED_INSTRUMENTATIONS` |
| `--log-level LEVEL` | 日志级别：`debug`、`info`（默认）、`warning`、`error` | — |

### 工作原理

1. CLI 解析参数，设置对应的环境变量
2. 将 `fastx_opentelemetry` 包目录加入 `PYTHONPATH`
3. 通过 `os.execv` 替换为目标进程
4. Python 启动时自动执行 `sitecustomize.py`，调用 `fastx_opentelemetry.init()` 完成初始化
5. 应用代码在完整的 OpenTelemetry 环境中运行

### 使用示例

**Flask 应用（无需改代码）：**

```bash
fastx-otel-py-instrument \
  --service-name my-flask-app \
  --endpoint http://collector:4318 \
  python app.py
```

**Django 应用：**

```bash
fastx-otel-py-instrument \
  --service-name my-django-app \
  --endpoint http://collector:4318 \
  python manage.py runserver 0.0.0.0:8000
```

**FastAPI 应用：**

```bash
fastx-otel-py-instrument \
  --service-name my-fastapi-app \
  --endpoint http://collector:4318 \
  uvicorn main:app --host 0.0.0.0 --port 8000
```

**Docker 容器：**

```dockerfile
FROM python:3.11-slim
RUN pip install fastx-opentelemetry[all]
COPY . /app
WORKDIR /app
CMD ["fastx-otel-py-instrument", "--service-name", "my-app", "--endpoint", "http://collector:4318", "python", "app.py"]
```

### 环境变量方式配置

所有 CLI 参数都可以通过环境变量设置，适合容器化部署：

```bash
export OTEL_SERVICE_NAME=my-app
export OTEL_EXPORTER_OTLP_ENDPOINT=http://collector:4318
export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf

fastx-otel-py-instrument python app.py
```

---

## 方式二：代码接入

在 Python 代码中调用 `fastx_opentelemetry.init()` 完成初始化。适合需要精细控制配置的场景。

### 最简用法

```python
import fastx_opentelemetry

fastx_opentelemetry.init()
```

配合环境变量：

```bash
export OTEL_EXPORTER_OTLP_ENDPOINT=http://collector:4318
export OTEL_SERVICE_NAME=my-app
python my_app.py
```

### Flask 应用

```python
import fastx_opentelemetry

# 必须在创建 Flask app 之前初始化
fastx_opentelemetry.init(service_name="my-flask-app")

from flask import Flask
app = Flask(__name__)

@app.route("/hello")
def hello():
    return {"message": "Hello, World!"}
```

### Django 应用

在 `settings.py` 中添加：

```python
import fastx_opentelemetry
fastx_opentelemetry.init(service_name="my-django-app")
```

### FastAPI 应用

```python
import fastx_opentelemetry

fastx_opentelemetry.init(service_name="my-fastapi-app")

from fastapi import FastAPI
app = FastAPI()

@app.get("/hello")
async def hello():
    return {"message": "Hello, World!"}
```

### 自定义采样器

```python
import fastx_opentelemetry
from fastx_opentelemetry import RulesBasedSampler, SamplingRule, RateLimitingSampler
from opentelemetry.sdk.trace.sampling import ALWAYS_ON, ALWAYS_OFF, TraceIdRatioBased

sampler = RulesBasedSampler(
    rules=[
        SamplingRule(match_name="/health.*", sampler=ALWAYS_ON),
        SamplingRule(match_name="/static/.*", sampler=ALWAYS_OFF),
        SamplingRule(match_name="/api/.*", sampler=TraceIdRatioBased(0.1)),
    ],
    default_sampler=RateLimitingSampler(max_spans_per_second=50),
)

fastx_opentelemetry.init(
    service_name="my-app",
    sampler=sampler,
)
```

### 完整配置

```python
import fastx_opentelemetry
from opentelemetry.sdk.trace.export import ConsoleSpanExporter, SimpleSpanProcessor

console_processor = SimpleSpanProcessor(ConsoleSpanExporter())

loaded = fastx_opentelemetry.init(
    service_name="my-app",
    endpoint="http://collector:4317",  # 自动拼接 /v1/traces, /v1/metrics, /v1/logs
    protocol="grpc",
    compression="gzip",
    headers={"Authorization": "Bearer token123"},
    extra_resource_attributes={
        "deployment.environment": "production",
        "service.version": "1.2.3",
    },
    span_processors=[console_processor],
    disabled_instrumentations=["sqlite3"],
)

print(f"Loaded instrumentors: {loaded}")
```

### 禁用特定增强

```python
fastx_opentelemetry.init(
    service_name="my-app",
    disabled_instrumentations=["sqlite3", "urllib"],
)
```

或通过环境变量：

```bash
export OTEL_PYTHON_DISABLED_INSTRUMENTATIONS=sqlite3,urllib
```

禁用所有增强（仅初始化 SDK，不加载任何 instrumentor）：

```python
fastx_opentelemetry.init(
    service_name="my-app",
    disabled_instrumentations=["*"],
)
```

---

## 上下游服务串联

`FastxBaggagePropagator` 和 `FastxSpanProcessor` 在 `init()` 时自动启用，无需额外配置。它们实现了与 Go 端 `fastx_otel.go` 一致的 Baggage 传播逻辑：

- **`FastxBaggagePropagator`** — 包装 W3C Baggage 传播器，提取时自动转换服务链：
  - 将 `current.service.name` 移入 `parent.service.name`
  - 将 `current.service.name` 追加到 `parent.service.names`（以 `++` 分隔，去重）
  - 将 `current.service.name` 设置为本地服务名
- **`FastxSpanProcessor`** — 在 Span 开始时，自动将所有 Baggage 条目复制为 Span 属性

这意味着在微服务调用链中，每个服务的 Span 都会携带完整的上游服务链信息，便于在后端（如 Jaeger、Grafana）中追踪调用路径。

## 环境变量

| 变量 | 说明 | 默认值 |
|------|------|--------|
| `FASTX_PYTHON_INSTRUMENTATION_ENABLED` | 全局开关，`false` 时跳过全部初始化 | `true` |
| `OTEL_SERVICE_NAME` | 服务名称 | `unknown_service` |
| `OTEL_EXPORTER_OTLP_ENDPOINT` | OTLP Collector 地址（自动拼接信号路径） | — |
| `OTEL_EXPORTER_OTLP_PROTOCOL` | 传输协议 `http/protobuf` 或 `grpc` | `http/protobuf` |
| `OTEL_EXPORTER_OTLP_COMPRESSION` | 压缩方式 `gzip`/`deflate` | `gzip` |
| `OTEL_EXPORTER_OTLP_TRACES_HEADERS` | 导出器请求头 | — |
| `OTEL_TRACES_SAMPLER` | 采样器名称 | `parentbased_always_on` |
| `OTEL_PYTHON_DISABLED_INSTRUMENTATIONS` | 禁用的增强列表（逗号分隔，`*` 禁用全部） | — |
| `OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED` | 是否启用日志自动增强 | `true` |
| `IP_ADDR` | 自动检测为 `host.ip` 资源属性 | 本地非回环 IP |
| `CLOUD_TYPE` | 自动检测为 `cloud.platform` 资源属性 | — |
| `REGION_CODE` | 自动检测为 `cloud.region` 资源属性 | — |
| `AZ_CODE` | 自动检测为 `cloud.availability` 资源属性 | — |
| `env_cd` | 自动检测为 `deployment.environment.name` 资源属性 | — |

## 安装增强库

`fastx-opentelemetry` 会自动检测已安装的库并加载对应的 instrumentation。你需要手动安装需要的 instrumentation 包：

```bash
# Web 框架
pip install opentelemetry-instrumentation-flask
pip install opentelemetry-instrumentation-django
pip install opentelemetry-instrumentation-fastapi

# HTTP 客户端
pip install opentelemetry-instrumentation-requests
pip install opentelemetry-instrumentation-httpx
pip install opentelemetry-instrumentation-urllib

# 数据库
pip install opentelemetry-instrumentation-sqlalchemy
pip install opentelemetry-instrumentation-psycopg2
pip install opentelemetry-instrumentation-redis
pip install opentelemetry-instrumentation-pymongo

# 消息队列
pip install opentelemetry-instrumentation-celery
pip install opentelemetry-instrumentation-kafka-python
```

检查已安装的增强库：

```bash
pip list | grep opentelemetry-instrumentation
```

## API 参考

### `fastx_opentelemetry.init(**kwargs)`

初始化 OpenTelemetry SDK 并加载所有已安装的 instrumentor。

**参数：**

| 参数 | 类型 | 说明 |
|------|------|------|
| `service_name` | `str \| None` | 服务名称，回退到 `OTEL_SERVICE_NAME` |
| `endpoint` | `str \| None` | OTLP Collector 地址（自动拼接信号路径） |
| `protocol` | `str \| None` | `http/protobuf` 或 `grpc` |
| `compression` | `str \| None` | `gzip` 或 `deflate` |
| `headers` | `dict \| None` | OTLP 导出器请求头 |
| `extra_resource_attributes` | `dict \| None` | 额外的资源属性 |
| `span_processors` | `list \| None` | 自定义 SpanProcessor 列表 |
| `sampler` | `Sampler \| None` | 自定义采样器实例 |
| `disabled_instrumentations` | `list \| None` | 禁用的 instrumentor 名称列表，`"*"` 禁用全部 |

**返回值：** `list[str]` — 成功加载的 instrumentor 名称列表。

### `fastx_opentelemetry.list_available_instrumentors()`

返回所有已安装的 instrumentor 名称列表。

### `fastx_opentelemetry.suggest_missing_instrumentations(loaded)`

检查已安装但未增强的库，返回建议安装的 instrumentation 包名列表。

### 采样器

#### `RulesBasedSampler(rules, default_sampler=None)`

按规则匹配 span 名称或属性，委托给不同的采样器。

#### `SamplingRule(sampler, match_name=None, match_attributes=None)`

单条采样规则。`match_name` 为正则表达式匹配 span 名称，`match_attributes` 为属性键值对匹配。

#### `RateLimitingSampler(max_spans_per_second)`

基于令牌桶算法的限流采样器，限制每秒采样的 span 数量。

### Baggage 传播与 Span 处理

#### `FastxBaggagePropagator`

包装 W3C Baggage 传播器，在 `extract` 时自动转换服务调用链。`init()` 时自动设置为全局传播器。

#### `FastxSpanProcessor`

在 Span `on_start` 时将所有 Baggage 条目复制为 Span 属性。`init()` 时自动添加到 TracerProvider。

## 本地开发

```bash
# 安装依赖
pip install -e ".[all]"

# 运行示例（零代码方式）
fastx-otel-py-instrument --service-name my-app --endpoint http://127.0.0.1:4318 python examples/app.py

# 运行示例（代码方式）
export OTEL_EXPORTER_OTLP_ENDPOINT=http://127.0.0.1:4318
export OTEL_SERVICE_NAME=my-app
python examples/app.py
```

## 日志打印trace_id

### 无代码增强方式

配置环境变量
```shell
export OTEL_PYTHON_LOG_CORRELATION=true
export OTEL_PYTHON_LOG_FORMAT=" %(msg)s [trace_id=%(otelTraceID)s]"
```

### 代码方式

```shell
import logging
logging.basicConfig(level=logging.INFO, format='[trace_id=%(otelTraceID)s] %(message)s')
```

## License

Apache-2.0
