Changelog
View SourceAll notable changes to this project will be documented in this file.
[1.1.4] - 2026-06-13
Changed
- Factored the repeated
try Expr catch _:_ -> Default endswallow idiom into shared helpers ininstrument_lib:safe_call/1,safe_call/2(fun-based) andsafe_apply/4(MFA-based, no closure). Converted the cold-path swallows (shutdown, force_flush,persistent_term:erase,logger:remove,ets:info, detector callbacks) across the span processor, exporters, registry, logger, flight recorder, resource detector, propagator and processors. Per-span hot paths, specific-class catches (error:badarg, race handling,exit:/throw:),try ... of ... catchblocks with guards and catches that log stay inline by design. - Bumped
hackney3.2.1 to 4.4.0 and thequickrandtest dependency 1.7.0 to 2.0.7. Transitive deps updated via the lock file.
[1.1.3] - 2026-05-28
Fixed
- OTP 29 compatibility: replaced the deprecated prefix
catchoperator withtry ... catch ... endthroughoutsrc/andtest/. Under OTP 29 the prefix form emits a deprecation warning, whichwarnings_as_errorsturned into a build failure.
Changed
- CI now builds and runs the test suite on OTP 29, alongside OTP 27 and 28.
- Bumped the
mecktest dependency to 1.2.0. Older versions use the deprecated prefixcatchin their own source and fail to compile on OTP 29.
[1.1.2] - 2026-05-25
Fixed
- A registry restart no longer silently drops previously-created OTel
instruments.
instrument_registry:init/1recreated the ETS tables empty and reset theinstrument_metricsindex to[], but left the per-namepersistent_termentries behind. The surviving{otel_instrument, Name}madeinstrument_meter:get_instrument/1return a stale record, socreate_counter/create_up_down_counter/create_histogramshort-circuited and never re-registered the underlying metric, while the index stayed empty — the metric vanished fromcollect_all/0and the Prometheus export even thoughadd/recordkept succeeding.init/1now erases the staleotel_instrument,otel_instruments,instrument_metric,instrument_label, andinstrument_label_overflowentries, soget_instrument/1returnsundefinedafter a restart and the metric self-heals on the nextcreate_*.unregister_allclears the same set (it previously missedotel_instrument/otel_instruments).
[1.1.1] - 2026-05-16
Fixed
observable_counterinstruments now render as Prometheuscounterwith the_totalsuffix instead ofgauge, in both unlabeled and labeled exposition. Previouslycreate_observable_instrument/3hardcoded gauge-shaped underlying storage and the labeled write path tagged the vec asgauge, so tooling that relies on# TYPE … counterto decide rate-ability (Grafana,rate(), alerting rules) saw the wrong type.instrument_meter:add/3on a labeledup_down_counterno longer crashes on negative deltas. The labeled write path previously hardcoded counter-shaped vec storage and hitinstrument_counter:inc_counter/2'sVal >= 0guard. It now routes through gauge-shaped vec storage with a sign split, soadd(Instrument, -1, #{label => ...})decrements the per-label-set value.
[1.1.0] - 2026-05-11
Changed
- Histogram bucket counts and the running sum are now backed by a single
OTP
atomicsarray per histogram (slot 1 = IEEE-754 bits of the sum via CAS retry, slots 2..N+2 = signed int64 bucket counts viaatomics:add/3). Drops the per-bucket NIF resources and makesget_histogram/collect~1.5x faster;observe_histogramis unchanged. Counters, gauges, and up-down counters remain on the NIF path. - Histogram
countand per-bucketcountfields in the collected map are now integers, matching theinstrument_metrics_exporterspec which already declared them asinteger(). Callers that compared these fields with=:=againstfloat()values must update their expectations.
Added
instrument_atomicsmodule: thin wrapper overatomicsproviding signed-integer slots and IEEE-754 double slots via bit-cast with a CAS retry loop.
[1.0.0] - 2026-05-03
First stable release. Hardens the runtime under contention and at high cardinality, brings the OTLP path up to spec, fixes deadlock and leak risks in the export pipeline, and lands a full audit of the documentation against the actual public API.
Added
OTEL_METRIC_CARDINALITY_LIMIT(default 2000) caps distinct label sets per vector metric. Excess label sets aggregate into one shared series taggedotel.metric.overflow=true. Dropped count exposed viainstrument_registry:cardinality_dropped/1.instrument_otlp_retrymodule: bounded exponential backoff withRetry-Afterhonoured, classifying transport and HTTP errors per the OTel spec. Configurable viaOTEL_EXPORTER_OTLP_MAX_RETRIES,OTEL_EXPORTER_OTLP_RETRY_INITIAL_DELAY_MS,OTEL_EXPORTER_OTLP_RETRY_MAX_DELAY_MS.- Span attribute value length limit
(
OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT), per-event and per-link attribute count limits (OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT,OTEL_LINK_ATTRIBUTE_COUNT_LIMIT); excess attributes counted indropped_attributes_countand emitted asdroppedAttributesCountin OTLP. - Tracestate caps at 32 entries / 256 bytes; baggage caps at 180 entries / 8192 bytes per W3C trace-context.
instrument_attributes:apply_limits/2,3andtruncate_value/2helpers.- Span Export Path section in the design and internals guide explaining
the full chain from
end_spanto OTLP, with explicit notes thatinstrument_tracer_nifserves the flight recorder only. - Test coverage for 28 documented public APIs that previously had no
call sites in CT (
instrument_propagation:inject_headers/extract_headers/call_with_context/cast_with_context,instrument_context:remove_value/spawn_link_with_context,instrument_config:set_verbose_tracing/is_verbose_tracing/enable_exporter/disable_exporter,instrument_tracer:record_exception/update_name,instrument_test:assert_log_trace_context,instrument_logger:add_trace_context/emit,instrument_metric:remove_label/clear_labels/set_gauge_to_current_time, exporter callback contracts).
Changed
- Batch processor
force_flush/0and the max-batch-size trigger no longer block the gen_server loop. An export worker is spawned and completion arrives as{export_done, ...}; concurrent flush callers are batched and replied to viagen_server:reply/2. A kill timer enforcesexport_timeout_milliswithout blocking the loop. instrument_exportershutdown now casts the hook unregister so the reply path stays unblocked.instrument_span_processor:on_start/2now has a 5 s safety timeout; the tracer hot path uses the inlinepersistent_termform.- OTLP exporters return
{error, retryable, _}vs{error, permanent, _}. The batch processor keeps retryable batches for up tomax_batch_retriescycles and drops permanent errors. - Probability sampler hashes the upper 8 bytes of the trace id, matching the Java/Go/Python reference SDKs.
- Status transitions now follow the OTel spec:
unset -> ok|error,error -> ok(success overrides),error -> errorupdates the description,okis final. - Batch processor clamps
max_export_batch_sizetomax_queue_sizeon init with a warning. - Documentation snippets fixed across
guides/,book/, andREADME.md: 6 syntax bugs, 8+ calls to non-existent or wrong-arity functions, wrong arg shapes forset_sampler,parent_basedconfig,metric_viewrecord literal,exporter_otlp:export, and stale config keys (schedule_delay_millis,export_timeout_millis). - Book chapter prose smoothed for readability.
Fixed
- Histogram exemplar reservoirs are now freed on unregister.
- Registry unregister no longer scans the global
persistent_termindex; iterates the metric's ownlabels_mapinstead. - Baggage
set/remove/clearuseset_current/1so process-dictionary context tokens no longer accumulate. instrument_exporter_console:export/2andinstrument_metrics_exporter_console:exporter_export/2no longer crash when configured withoutput => {file, Path}. The{file, Fd}wrapper used byshutdown/1is now unwrapped beforeio:put_chars/2.
Removed
instrument_exporter_kafkadocumentation section: the module never existed insrc/exporters/.
Deprecated
- None.
[0.6.1] - 2026-04-16
Fixed
- Moved hex package
fileslist fromrebar.configtoinstrument.app.srcsodo_build.sh,do_cmake.sh, andc_src/build assets are actually shipped in the published tarball (the rebar.config{hex, [{files, ...}]}entry was silently ignored for non-standard files) - ex_doc XML parse errors caused by
<<...>>literals and comparison operators in doc comments
[0.6.0] - 2026-04-16
Changed
- Renamed
instrumentmodule toinstrument_metricto avoid conflict with Erlang'sruntime_toolsinstrument module - Bumped application version in
instrument.app.srcto 0.6.0
[0.5.0] - 2026-04-08
Added
- OTel spec compliance features: span limits, dropped counts tracking, exemplar support
- B3 ParentSpanId injection for nested spans
- Aggregation temporality support (cumulative/delta)
- Observable instrument 1-arity callbacks with attributes
- Tests for OTel spec compliance (30 new test cases)
- Throughput optimizations for exporter and processor lookup
- Updated benchmarks documentation
Fixed
- Spawned-child trace leak with session-based cleanup
- OpenTelemetry spec compliance issues in OTLP export
- Preserved tracer scope in OTLP span export
[0.4.0] - 2026-04-07
Added
- Tail-based sampling span processor
- Generic client tracing utilities and attribute-based sampling
- Flight recorder using erl_tracer NIF for low-overhead message tracing
- Global tracing enable flag for performance optimization
- Custom span_id support to tracer API
instrument_testmodule for validating instrumentationstartTimeUnixNanoto OTLP metrics export- Debug logging to broad exception handlers
- Error logging to span processor callbacks
- Benchmarks for OpenTelemetry APIs and client tracing strategies
- Design and internals documentation
- Book chapters and reference guides
- Elixir users guide
- Runnable cross-process tracing and logging examples
- Tests for metric names and attributes in exporter
- Tests for tracing disabled and custom span_id
- Regression tests for 4 bug fixes (record_only sampling, metric description/unit, histogram view boundaries, OTLP scope config)
Fixed
- Critical tracing and OTLP spec compliance issues
- Histogram and OTLP spec compliance issues
- Empty bucket validation crash in histogram
- Tuple metric name handling for OTEL metrics
- Race condition in tracer exporter list
- Race condition in metric index updates
- Vec metric cleanup and concurrent attribute race condition
mark/1,2not working in spawned child processes- erl_tracer issues: teardown, idempotency, async parent spans
- Multiple bugs in client and tail sampler
- 5 bugs in span processors, metrics, and context propagation
- 5 client tracing issues
- Test failures in metrics exporter and span processor
- E2E test skip behavior and timing issues
Changed
- Replaced seq_trace with erl_tracer NIF for flight recorder performance
- Improved flight recorder eviction performance
- Improved config auto-registration and processor shutdown handling
- Improved edge case handling in tail sampler
- Renamed exporter callbacks to avoid gen_server conflicts
- Added xref checks
- Documented processor callback restrictions
- Filter internal tracer bootstrap messages in trace_receive
[0.3.0] - 2026-04-01
Added
- OpenTelemetry-compatible API with native implementation
- Context propagation via
instrument_contextmodule - W3C TraceContext format support in
instrument_propagation - W3C Baggage propagation via
instrument_baggagemodule - B3 single-header propagator (
instrument_propagator_b3) for Zipkin compatibility - B3 multi-header propagator (
instrument_propagator_b3_multi) for X-B3-* headers - Support for
b3andb3multiinOTEL_PROPAGATORSenvironment variable - Native span implementation via
instrument_tracerwith full lifecycle management - OTel-compatible MeterProvider/Meter API via
instrument_meter - Attribute handling via
instrument_attributes - Erlang logger integration via
instrument_loggerwith automatic trace correlation - Trace/Span ID generation per W3C spec via
instrument_id - Span exporter system via
instrument_exporterwith batch processing - Console exporter (
instrument_exporter_console) with text and JSON formats - OTLP HTTP exporter (
instrument_exporter_otlp) for OpenTelemetry collectors - Cross-process context propagation helpers
- New test suites for context, tracer, meter, and exporter modules
- New
instrument_otel.hrlheader with OTel record definitions - Documentation guides: getting started, instrumentation, context propagation, exporters
- E2E tests with Docker (Prometheus + Jaeger)
- Stress tests and race condition tests
- OpenTelemetry compatibility info to README
- Sampling and processing guide, external collectors documentation
Fixed
- Context/memory leaks with instrument cleanup
Changed
- Extended
metricrecord with OTel fields (description, unit, meter, attributes) - Updated application description to mention OpenTelemetry support
- Added hackney 3.2.1 as dependency for OTLP HTTP export
- Documentation updates for B3 propagation and OTel clarity
[0.2.0] - 2026-03-31
Added
- Vec API for labeled metrics (prometheus-cpp style):
new_counter_vec/3,new_gauge_vec/3,new_histogram_vec/3 - Direct vec operations:
inc_counter_vec/2,3,get_counter_vec/2,inc_gauge_vec/2,3,dec_gauge_vec/2,3,set_gauge_vec/3,get_gauge_vec/2,observe_histogram_vec/3,get_histogram_vec/2 labels/2function to get labeled metric instances- Prometheus text format export via
instrument_prometheus:format/0 - GitHub Actions CI for OTP 26/27 on Linux and macOS
- ex_doc documentation support
- Type specs for prometheus, vector, and registry modules
Changed
- Modernized NIF code from C++ to C11 with CMake build system
- Updated README with examples and API documentation
- Updated copyright to 2017-2026
[0.1.0] - 2017-04-28
Added
- Initial release
- Counter metrics with
new_counter/2,inc_counter/1,2,get_counter/1 - Gauge metrics with
new_gauge/2,inc_gauge/1,2,dec_gauge/1,2,set_gauge/2,get_gauge/1 - Histogram metrics with
new_histogram/2,3,observe_histogram/2,get_histogram/1 - Vector support for labeled metrics
- NIF-based implementation for high performance