Skip to content

Commit e02ed25

Browse files
refactor: remove metrics module and sampling configuration
Cost attribution requires 100% trace capture. Removed: - metrics.py module (redundant with trace-based aggregation) - trace_sample_rate config option - BOTANU_TRACE_SAMPLE_RATE environment variable Backend aggregates costs from traces by run_id. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent c9f6023 commit e02ed25

File tree

10 files changed

+21
-182
lines changed

10 files changed

+21
-182
lines changed

docs/api/configuration.md

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ from botanu.sdk.config import BotanuConfig
2222
| `max_export_batch_size` | `int` | `512` | Max spans per batch |
2323
| `max_queue_size` | `int` | `2048` | Max spans in queue |
2424
| `schedule_delay_millis` | `int` | `5000` | Delay between batch exports |
25-
| `trace_sample_rate` | `float` | `1.0` | Sampling rate (1.0 = 100%) |
2625
| `propagation_mode` | `str` | `"lean"` | `"lean"` or `"full"` |
2726
| `auto_instrument_packages` | `list` | `[...]` | Packages to auto-instrument |
2827

@@ -137,9 +136,6 @@ export:
137136
queue_size: integer # Max spans in queue
138137
delay_ms: integer # Delay between exports
139138

140-
sampling:
141-
rate: float # Sampling rate (0.0-1.0)
142-
143139
propagation:
144140
mode: string # "lean" or "full"
145141

@@ -294,7 +290,6 @@ if not is_enabled():
294290
|----------|-------------|---------|
295291
| `BOTANU_ENVIRONMENT` | Fallback for environment | `"production"` |
296292
| `BOTANU_PROPAGATION_MODE` | `"lean"` or `"full"` | `"lean"` |
297-
| `BOTANU_TRACE_SAMPLE_RATE` | Sampling rate (0.0-1.0) | `"1.0"` |
298293
| `BOTANU_AUTO_DETECT_RESOURCES` | Auto-detect cloud resources | `"true"` |
299294
| `BOTANU_CONFIG_FILE` | Path to YAML config file | None |
300295

docs/getting-started/configuration.md

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,6 @@ class BotanuConfig:
8181
max_queue_size: int = 2048
8282
schedule_delay_millis: int = 5000
8383

84-
# Sampling (1.0 = 100%)
85-
trace_sample_rate: float = 1.0 # BOTANU_TRACE_SAMPLE_RATE
86-
8784
# Propagation mode
8885
propagation_mode: str = "lean" # BOTANU_PROPAGATION_MODE
8986

@@ -110,7 +107,6 @@ class BotanuConfig:
110107
|----------|-------------|---------|
111108
| `BOTANU_ENVIRONMENT` | Fallback for environment | `production` |
112109
| `BOTANU_PROPAGATION_MODE` | `lean` or `full` | `lean` |
113-
| `BOTANU_TRACE_SAMPLE_RATE` | Sampling rate (0.0-1.0) | `1.0` |
114110
| `BOTANU_AUTO_DETECT_RESOURCES` | Auto-detect cloud resources | `true` |
115111
| `BOTANU_CONFIG_FILE` | Path to YAML config | None |
116112

@@ -139,9 +135,6 @@ export:
139135
queue_size: 2048
140136
delay_ms: 5000
141137

142-
sampling:
143-
rate: 1.0
144-
145138
propagation:
146139
mode: lean
147140

@@ -259,16 +252,6 @@ enable(
259252
)
260253
```
261254

262-
## Sampling
263-
264-
For cost attribution, **always use 100% sampling** (the default):
265-
266-
```python
267-
trace_sample_rate: float = 1.0 # Never miss a transaction
268-
```
269-
270-
If you must sample, understand that cost calculations will be incomplete.
271-
272255
## Exporting Configuration
273256

274257
```python

docs/patterns/anti-patterns.md

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -238,26 +238,6 @@ track_llm_call(provider="azure_openai", ...)
238238

239239
### Sampling for Cost Attribution
240240

241-
**Don't** sample spans:
242-
243-
```python
244-
# BAD - Missing cost data
245-
enable(
246-
service_name="my-service",
247-
trace_sample_rate=0.1, # Only 10% of costs captured!
248-
)
249-
```
250-
251-
**Do** use 100% sampling:
252-
253-
```python
254-
# GOOD - Complete cost data
255-
enable(
256-
service_name="my-service",
257-
trace_sample_rate=1.0, # Default - don't change
258-
)
259-
```
260-
261241
### Hardcoding Configuration
262242

263243
**Don't** hardcode production values:

docs/patterns/best-practices.md

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -276,18 +276,6 @@ export OTEL_EXPORTER_OTLP_ENDPOINT=http://collector:4318
276276
export BOTANU_ENVIRONMENT=production
277277
```
278278

279-
### Never Sample for Cost Attribution
280-
281-
Always use 100% sampling for accurate cost data:
282-
283-
```python
284-
# GOOD
285-
trace_sample_rate: float = 1.0
286-
287-
# BAD - Missing cost data
288-
trace_sample_rate: float = 0.1 # Only 10% of costs captured
289-
```
290-
291279
### Use YAML for Complex Configuration
292280

293281
For multi-environment setups:

src/botanu/sdk/config.py

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,6 @@ class BotanuConfig:
6464
max_queue_size: int = 2048
6565
schedule_delay_millis: int = 5000
6666

67-
# Sampling (1.0 = 100% — never sample for cost attribution)
68-
trace_sample_rate: float = 1.0
69-
7067
# Propagation mode: "lean" (run_id + use_case only) or "full" (all context)
7168
propagation_mode: str = "lean"
7269

@@ -141,10 +138,6 @@ def __post_init__(self) -> None:
141138
if env_propagation_mode and env_propagation_mode in ("lean", "full"):
142139
self.propagation_mode = env_propagation_mode
143140

144-
env_sample_rate = os.getenv("BOTANU_TRACE_SAMPLE_RATE")
145-
if env_sample_rate:
146-
self.trace_sample_rate = float(env_sample_rate)
147-
148141
# ------------------------------------------------------------------
149142
# YAML loading
150143
# ------------------------------------------------------------------
@@ -236,7 +229,6 @@ def _from_dict(
236229
service = data.get("service", {})
237230
otlp = data.get("otlp", {})
238231
export = data.get("export", {})
239-
sampling = data.get("sampling", {})
240232
propagation = data.get("propagation", {})
241233
resource = data.get("resource", {})
242234
auto_packages = data.get("auto_instrument_packages")
@@ -252,7 +244,6 @@ def _from_dict(
252244
max_export_batch_size=export.get("batch_size", 512),
253245
max_queue_size=export.get("queue_size", 2048),
254246
schedule_delay_millis=export.get("delay_ms", 5000),
255-
trace_sample_rate=sampling.get("rate", 1.0),
256247
propagation_mode=propagation.get("mode", "lean"),
257248
auto_instrument_packages=(auto_packages if auto_packages else BotanuConfig().auto_instrument_packages),
258249
_config_file=config_file,
@@ -279,9 +270,6 @@ def to_dict(self) -> Dict[str, Any]:
279270
"queue_size": self.max_queue_size,
280271
"delay_ms": self.schedule_delay_millis,
281272
},
282-
"sampling": {
283-
"rate": self.trace_sample_rate,
284-
},
285273
"propagation": {
286274
"mode": self.propagation_mode,
287275
},

src/botanu/sdk/decorators.py

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424

2525
from botanu.models.run_context import RunContext, RunStatus
2626
from botanu.sdk.context import get_baggage, set_baggage
27-
from botanu.tracking.metrics import record_run_completed
2827

2928
T = TypeVar("T")
3029

@@ -120,7 +119,11 @@ async def async_wrapper(*args: Any, **kwargs: Any) -> T:
120119
result = await func(*args, **kwargs)
121120

122121
span_attrs = getattr(span, "attributes", None)
123-
existing_outcome = span_attrs.get("botanu.outcome.status") if span_attrs else None
122+
existing_outcome = (
123+
span_attrs.get("botanu.outcome.status")
124+
if isinstance(span_attrs, dict)
125+
else None
126+
)
124127

125128
if existing_outcome is None and auto_outcome_on_success:
126129
run_ctx.complete(RunStatus.SUCCESS)
@@ -176,7 +179,11 @@ def sync_wrapper(*args: Any, **kwargs: Any) -> T:
176179
result = func(*args, **kwargs)
177180

178181
span_attrs = getattr(span, "attributes", None)
179-
existing_outcome = span_attrs.get("botanu.outcome.status") if span_attrs else None
182+
existing_outcome = (
183+
span_attrs.get("botanu.outcome.status")
184+
if isinstance(span_attrs, dict)
185+
else None
186+
)
180187

181188
if existing_outcome is None and auto_outcome_on_success:
182189
run_ctx.complete(RunStatus.SUCCESS)
@@ -230,14 +237,6 @@ def _emit_run_completed(
230237
span.set_attribute("botanu.outcome.status", status.value)
231238
span.set_attribute("botanu.run.duration_ms", duration_ms)
232239

233-
record_run_completed(
234-
use_case=run_ctx.use_case,
235-
status=status.value,
236-
environment=run_ctx.environment,
237-
duration_ms=duration_ms,
238-
workflow=run_ctx.workflow,
239-
)
240-
241240

242241
# Alias
243242
use_case = botanu_use_case

src/botanu/tracking/__init__.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
- LLM/GenAI model calls
88
- Database, storage, and messaging operations
99
- Attempt ledger for durable cost tracking
10-
- Run completion metrics
1110
"""
1211

1312
from __future__ import annotations
@@ -44,7 +43,6 @@
4443
track_llm_call,
4544
track_tool_call,
4645
)
47-
from botanu.tracking.metrics import record_run_completed
4846

4947
__all__ = [
5048
# LLM tracking
@@ -76,6 +74,4 @@
7674
"record_tool_attempted",
7775
"LedgerEventType",
7876
"AttemptStatus",
79-
# Metrics
80-
"record_run_completed",
8177
]

src/botanu/tracking/ledger.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import time
2323
from dataclasses import dataclass, field
2424
from enum import Enum
25+
from functools import lru_cache
2526
from typing import Any, Dict, Optional
2627

2728
from opentelemetry import trace
@@ -384,12 +385,17 @@ def shutdown(self) -> None:
384385
_global_ledger: Optional[AttemptLedger] = None
385386

386387

388+
@lru_cache(maxsize=1)
389+
def _create_default_ledger() -> AttemptLedger:
390+
"""Create default ledger instance (thread-safe via lru_cache)."""
391+
return AttemptLedger()
392+
393+
387394
def get_ledger() -> AttemptLedger:
388-
"""Get the global attempt ledger instance."""
389-
global _global_ledger
390-
if _global_ledger is None:
391-
_global_ledger = AttemptLedger()
392-
return _global_ledger
395+
"""Get the global attempt ledger instance (thread-safe)."""
396+
if _global_ledger is not None:
397+
return _global_ledger
398+
return _create_default_ledger()
393399

394400

395401
def set_ledger(ledger: AttemptLedger) -> None:

src/botanu/tracking/metrics.py

Lines changed: 0 additions & 90 deletions
This file was deleted.

tests/unit/test_config.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@ def test_default_values(self):
5353
assert config.service_name == "unknown_service"
5454
assert config.deployment_environment == "production"
5555
assert config.propagation_mode == "lean"
56-
assert config.trace_sample_rate == 1.0
5756
assert config.auto_detect_resources is True
5857

5958
def test_env_var_service_name(self):
@@ -85,11 +84,6 @@ def test_explicit_values_override_env(self):
8584
config = BotanuConfig(service_name="explicit-service")
8685
assert config.service_name == "explicit-service"
8786

88-
def test_env_var_sample_rate(self):
89-
with mock.patch.dict(os.environ, {"BOTANU_TRACE_SAMPLE_RATE": "0.5"}):
90-
config = BotanuConfig()
91-
assert config.trace_sample_rate == 0.5
92-
9387
def test_env_var_propagation_mode(self):
9488
with mock.patch.dict(os.environ, {"BOTANU_PROPAGATION_MODE": "full"}):
9589
config = BotanuConfig()

0 commit comments

Comments
 (0)