Configuration¶
Configuration is read from the environment, with the same variables, defaults and
parsing as the Python library genai_otel_instrument v1.46.0. The authoritative list,
with the value produced for every raw input, is contract/config.json; the contract test
checks this SDK against all of it.
Options passed to instrument({...}) override the environment. Option names are the
camelCase form of the Python OTelConfig field (service_name -> serviceName).
Parsing rules worth knowing¶
They are the Python library's, kept on purpose so one configuration behaves the same in both languages:
- Booleans are true only for
truein any letter case.1,yes,onare false, and a leading or trailing space makes the value false. Two variables strip whitespace first:GENAI_ENABLE_CONTENT_CAPTUREandGENAI_CAPTURE_EMBEDDING_VECTORS. - Numbers that do not parse are an error, not a silent default. With
GENAI_SAMPLING_RATE=abc,instrument()logs the reason and leaves the application uninstrumented; withGENAI_FAIL_ON_ERROR=trueit throws. The one exception isOTEL_EXPORTER_OTLP_TIMEOUT, which falls back to 60 with a warning. OTEL_EXPORTER_OTLP_TIMEOUTis in seconds, as in the Python library, although the OpenTelemetry specification defines it in milliseconds. This SDK passes it to the exporters explicitly so they do not read it as milliseconds.- A variable that is set but empty stays empty; it does not fall back to the default.
OTEL_EXPORTER_OTLP_ENDPOINT=therefore selects the console exporters. GENAI_ENABLED_INSTRUMENTORSis split on commas and each name trimmed. Empty means the default list. A name this SDK has not ported is reported in the log.
Start-up cost¶
On Node 20 and later, instrument() loads no CommonJS client library in order to patch it:
a library is patched when the application loads it (require, or an import of a
CommonJS package), or at once if it already has. An application does not pay for
libraries it has installed and does not use.
ES-module builds cannot be watched that way. In an ES-module application the
ES-module build of every installed client library is imported; the ones the application
already imports cost nothing more, the others are loaded. In a CommonJS application
the ES-module builds are not touched: a library reached only through a dynamic import()
is not traced there.
Measured on the development machine with all 38 client libraries of this repository
installed (none used), Node 24: CommonJS application, instrument() 40 ms; ES-module
application, 2.3 s in instrument() and 1.1 s more until instrumentorsReady(). An
application with a handful of libraries installed pays a small fraction of that. To pay
nothing for libraries that are installed but unused, name the ones in use
(GENAI_ENABLED_INSTRUMENTORS=openai,anthropic) and switch datastore instrumentation off
if it is not wanted (GENAI_ENABLE_MCP_INSTRUMENTATION=false).
On Node 18 every installed library is loaded at instrument(), both builds, as
before: an ES-module import of a library that cannot run on Node 18 (anything on undici
7) ends the process there unless its CommonJS load is tried first.
Variables in effect in this version¶
| Variable | Default | Notes |
|---|---|---|
OTEL_SERVICE_NAME |
genai-app |
|
OTEL_EXPORTER_OTLP_ENDPOINT |
http://localhost:4318 |
Empty: console exporters |
OTEL_EXPORTER_OTLP_PROTOCOL |
by port | grpc, otherwise http/protobuf. Unset: port 4317 means gRPC |
OTEL_EXPORTER_OTLP_TIMEOUT |
60 |
Seconds |
OTEL_EXPORTER_OTLP_HEADERS |
Read by the exporters themselves | |
OTEL_METRIC_EXPORT_INTERVAL |
60000 |
Milliseconds |
OTEL_SEMCONV_STABILITY_OPT_IN |
gen_ai/dup |
gen_ai emits current attribute names only |
OTEL_ENVIRONMENT |
Sets deployment.environment.name (and legacy environment) |
|
OTEL_SERVICE_INSTANCE_ID |
Wins over the generated instance id | |
OTEL_RESOURCE_ATTRIBUTES |
Standard; an explicit service.instance.id here is respected |
|
OTEL_EXPERIMENTAL_RESOURCE_DETECTORS |
host,os |
Add process for the process attributes (opt-in since the reference's v1.46.0). host.id is included |
GENAI_OTEL_RESOURCE_HOST_IP |
off | 1, true, yes or on adds host.ip |
GENAI_ENABLE_AGENT_GRAPH_ATTRIBUTES |
true |
graph.node.* and agent.name on agent spans and LangGraph node spans; only true (any case) turns it on |
GENAI_ENABLE_SESSION_PROPAGATION |
true |
A span without a session takes its parent span's session.id / user.id (and an OpenInference setSession() / setUser()); false keeps only explicit sessions |
GENAI_SESSION_BAGGAGE_KEYS / GENAI_SESSION_BAGGAGE_ACCEPT |
empty | Comma lists of session.id, user.id: which ids a service sends to, and accepts from, other services as W3C baggage. Off by default: an id crosses a service boundary only when both sides opt in. Node only |
GENAI_EVALUATION_CACHE_SIZE |
256 |
Results each detector keeps for repeated text (keyed by a digest, never the text); 0 turns caching off |
GENAI_TOXICITY_QUANTIZE, GENAI_TOXICITY_TORCH_THREADS |
false, unset |
Passed to the toxicity detector's model settings |
GENAI_SERVICE_INSTANCE_ID_MODE |
random |
derived: a v5 UUID stable across restarts |
GENAI_ENABLED_INSTRUMENTORS |
reference list | See docs/parity-matrix.md |
GENAI_ENABLE_COST_TRACKING |
true |
|
GENAI_MCP_HASH_SALT |
Salt for hashed user identifiers on MCP spans. Unset: a random per-process salt, with a warning; hashes then do not match across processes | |
GENAI_ENABLE_HTTP_INSTRUMENTATION |
false |
A span per outbound fetch call (api.call.<method>.<host>). Off by default: every provider client already has a span, and its requests go through fetch too |
GENAI_ENABLE_MCP_INSTRUMENTATION |
true |
Datastore and tool instrumentation: one client span and the mcp.* metrics per operation. db.statement only with content capture on |
GENAI_CUSTOM_PRICING_JSON |
Same shape as the pricing table; merged over it. Applied by every instrumentor and by the cost-enrichment processor (the Python library v1.30.0 ignores it in most instrumentors) | |
GENAI_ENABLE_CONTENT_CAPTURE |
false |
|
OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT |
NO_CONTENT, SPAN_ONLY, EVENT_ONLY, SPAN_AND_EVENT; wins over the line above |
|
GENAI_CONTENT_MAX_LENGTH |
200 |
0: no limit |
GENAI_CAPTURE_EMBEDDING_VECTORS |
false |
|
GENAI_SAMPLING_RATE |
1.0 |
Below 1.0: sampling by trace-id ratio |
GENAI_METRICS_PROFILE |
standard |
full also records token histograms, granular cost and finish counters |
GENAI_RECORD_TOKEN_HISTOGRAMS, GENAI_RECORD_GRANULAR_COST_METRICS, GENAI_RECORD_FINISH_METRICS |
off | Individual switches for the above |
GENAI_PROFILE |
strict, bfsi, bank: no external egress, air-gapped, audit content capture on |
|
GENAI_FAIL_ON_ERROR |
false |
|
GENAI_FLUSH_ON_SIGTERM |
false |
With GENAI_SIGTERM_FLUSH_TIMEOUT (default 5.0 seconds) |
GENAI_SKIP_COLLECTOR_CHECK |
false |
Silences the "no collector on the default endpoint" warning |
GENAI_OTEL_LOG_LEVEL |
DEBUG, INFO, WARNING, ERROR. Unset: warnings and errors. OTEL_LOG_LEVEL is the fallback |
|
GENAI_ENABLE_PII_DETECTION |
false |
With GENAI_PII_MODE (detect, redact, block), GENAI_PII_THRESHOLD (not used by the pattern detector), GENAI_PII_GDPR_MODE, GENAI_PII_HIPAA_MODE, GENAI_PII_PCI_DSS_MODE. See evaluation.md |
GENAI_ENABLE_PROMPT_INJECTION_DETECTION |
false |
GENAI_PROMPT_INJECTION_THRESHOLD 0.5, GENAI_PROMPT_INJECTION_BLOCK_ON_DETECTION |
GENAI_ENABLE_BIAS_DETECTION |
false |
GENAI_BIAS_THRESHOLD 0.4, GENAI_BIAS_BLOCK_ON_DETECTION |
GENAI_ENABLE_RESTRICTED_TOPICS |
false |
GENAI_RESTRICTED_TOPICS_THRESHOLD 0.7, GENAI_RESTRICTED_TOPICS_BLOCK_ON_DETECTION |
GENAI_ENABLE_HALLUCINATION_DETECTION |
false |
GENAI_HALLUCINATION_THRESHOLD 0.6 |
GENAI_ENABLE_TOXICITY_DETECTION |
false |
GENAI_TOXICITY_THRESHOLD 0.7, GENAI_TOXICITY_BLOCK_ON_DETECTION. In effect once something measures it: a scorer given in code (setToxicityScorer), else the on-prem judge at GENAI_TOXICITY_JUDGE_URL, else a local ONNX model at GENAI_TOXICITY_ONNX_MODEL through the optional add-on genai-otel-instrument-node-onnx (see evaluation.md); until then nothing about toxicity is written. GENAI_TOXICITY_USE_PERSPECTIVE_API is read and has no effect: no Perspective client ships |
GENAI_OTEL_MEDIA_CAPTURE_MODE and the other GENAI_OTEL_MEDIA_* |
off |
Multimodal capture; see media.md |
GENAI_ENABLE_GPU_METRICS |
true |
NVIDIA, through nvidia-smi, every GENAI_GPU_COLLECTION_INTERVAL seconds (default 5). No nvidia-smi or no device: nothing is created, one log line. See gpu-energy.md |
GENAI_POWER_COST_PER_KWH |
0.12 |
USD; gen_ai.power.cost is measured GPU energy times this |
GENAI_ENABLE_CO2_TRACKING |
false |
gen_ai.co2.emissions is measured GPU energy times GENAI_CARBON_INTENSITY (default 475.0 gCO2e/kWh, a world average - set the grid's own figure). Needs GPU metrics |
GENAI_ENABLE_OLLAMA_SERVER_METRICS |
true |
With the Ollama instrumentor active: polls OLLAMA_BASE_URL (default http://localhost:11434) /api/ps every GENAI_OLLAMA_METRICS_INTERVAL seconds (default 5.0) for the loaded models. GENAI_OLLAMA_MAX_VRAM_GB: the GPU's VRAM, needed for the per-model share; detected with nvidia-smi when unset, and not reported when neither is available. These four are read from the environment only, as in the Python library. Every poll replaces what Ollama reported: a model that unloads is no longer reported |
GENAI_SERVER_METRICS_ENDPOINTS |
unset | Comma-separated vLLM / llama.cpp base URLs (http or https). Each /metrics is polled every 5 s: KV-cache usage per model, waiting requests, and from vLLM the prefix-cache counters (gen_ai.server.prefix_cache.queries / .hits, cumulative, in tokens). Not in the Python library (SDK_ADDITIONS.md sections 12, 13); read from the environment only |
GENAI_ENABLE_CONCURRENCY_METRICS |
true |
Count this SDK's own model calls in flight as gen_ai.server.requests.running{server_metrics_source="client"}. The Python library parses it and never reads it |
Read and parsed, not in effect yet¶
These are parsed exactly as the Python library parses them, so a shared configuration loads cleanly, but the feature behind them arrives in a later phase. Enabling one is reported in the log at start-up; it is never silently ignored.
| Variables | Feature | Phase |
|---|---|---|
GENAI_CO2_COUNTRY_ISO_CODE, GENAI_CO2_REGION, GENAI_CO2_CLOUD_PROVIDER, GENAI_CO2_CLOUD_REGION, GENAI_CO2_OFFLINE_MODE, GENAI_CO2_TRACKING_MODE, GENAI_CO2_USE_MANUAL, GENAI_CODECARBON_LOG_LEVEL |
codecarbon's location and mode settings. There is no codecarbon here: CO2 is energy times GENAI_CARBON_INTENSITY |
- |
GENAI_ENABLE_CONCURRENCY_METRICS |
Read by the Python library's configuration and used by nothing there either | - |
GENAI_EVALUATION_MODE, GENAI_EVALUATION_QUEUE_SIZE, GENAI_EVALUATION_MAX_CHARS, GENAI_TOXICITY_MAX_CHARS, GENAI_EVALUATION_SHUTDOWN_TIMEOUT |
Background evaluation (Python 1.36.0): detectors run off the request path from a bounded queue. Here they still run inline | later |
GENAI_DECISION_HTTP_MATCHER, GENAI_DECISION_LEGACY_LLM_KIND, GENAI_DECISION_PROVIDER |
Decision spans from HTTP decision services (Python 1.38.0). Decision pricing is in effect: a span with gen_ai.request.type = decision is priced |
later |
Variables the Python library reads that this SDK does not, and why, are listed in
tests/contract/config.contract.test.ts (NOT_READ). The test fails if a future
contract adds a variable that is in neither list.