--- warning: DO NOT CHANGE THIS MANUALLY, THIS IS GENERATED BY https://github.com/duckdb/community-extensions repository, check README there title: otlp excerpt: | DuckDB Community Extensions Query OpenTelemetry traces, logs, and metrics with SQL — or stream live OTLP/HTTP into DuckDB, DuckLake, or Iceberg extension: name: otlp description: Query OpenTelemetry traces, logs, and metrics with SQL — or stream live OTLP/HTTP into DuckDB, DuckLake, or Iceberg version: 0.8.0 language: C++ build: cmake license: MIT excluded_platforms: "wasm_mvp;wasm_threads;linux_amd64_musl;windows_amd64_mingw" requires_toolchains: rust maintainers: - smithclay repo: github: smithclay/duckdb-otlp variegata: eb83de31ec7af563f1b7c716be606b62a5b63ce6 ref: 003e855441ee28f35116f0a2f8a73f744a3019da docs: hello_world: | -- Load the extension LOAD otlp; -- Traces: find the slow spans SELECT trace_id, span_name, service_name, duration FROM read_otlp_traces('traces.jsonl') WHERE duration > 1e9 -- longer than one second ORDER BY duration DESC LIMIT 10; -- Or run a live OTLP/HTTP endpoint and query what arrives SELECT listen_url, auth_token FROM otlp_serve('otlp:localhost:4318'); -- POST telemetry to http://localhost:4318/v1/logs. -- Rows commit automatically in the background; otlp_stop commits remaining rows. SELECT status FROM otlp_stop('otlp:localhost:4318'); SELECT * FROM otlp_logs; -- Metrics: latest gauge readings (works native and in the browser) SELECT timestamp, service_name, metric_name, value FROM read_otlp_metrics_gauge('metrics.jsonl') ORDER BY timestamp DESC; -- Logs: just the errors, read straight from S3 SELECT timestamp, severity_text, body, service_name FROM read_otlp_logs('s3://bucket/logs-*.jsonl') WHERE severity_text = 'ERROR'; -- Protobuf works too, on native builds SELECT * FROM read_otlp_traces('traces.pb') LIMIT 10; extended_description: | # OpenTelemetry, in SQL Point this extension at OTLP data and query it like any other table. Traces, logs, and metrics arrive as proper columns — `service_name`, `trace_id`, `duration`, `value` — not JSON you have to dig through. The schema follows the OpenTelemetry ClickHouse exporter, so columns are typed, quick to filter, and stable. There are two ways in. ## Read files Six table functions read OTLP exports straight from disk or object storage: - `read_otlp_traces()` — spans, with attributes, events, links, and a computed duration - `read_otlp_logs()` — records, with severity, body, and trace correlation - `read_otlp_metrics_gauge()`, `read_otlp_metrics_sum()`, `read_otlp_metrics_histogram()`, `read_otlp_metrics_exp_histogram()` JSON, JSONL, and protobuf are detected automatically. Paths can be local, a glob, S3, HTTP(S), Azure, or GCS. The browser build (DuckDB-WASM) reads all three formats — try the [interactive demo](https://smithclay.github.io/duckdb-otlp/). ## Serve live ingest (native builds) `otlp_serve()` starts an OTLP/HTTP endpoint inside DuckDB. Point a Collector or SDK exporter at `http://localhost:4318` and the rows land in tables you can query: ```sql SELECT listen_url, auth_token FROM otlp_serve('otlp:localhost:4318'); ``` Leave `catalog` empty to land rows in the default catalog for quick local work. Set it to an attached [DuckLake](https://ducklake.select) lakehouse or writable Iceberg REST catalog for durable lakehouse ingest. Ingest is buffered and group-committed. A POST returns `202 Accepted` once rows are parsed and buffered; they turn durable at the next automatic background commit, currently when the oldest buffered row is about 5 seconds old or admitted request-body bytes reach about 64 MiB. `otlp_server_list()` reports the counters, and `otlp_stop()` commits what is left before it shuts down. `otlp_flush()` is optional: use it only when readers need the latest accepted rows durable immediately while the server keeps running. Stop before you close the database — a plain close does not commit buffered rows. ## Use it for - Reading and analyzing Collector or SDK exports - A small, always-on OTLP sink that writes to DuckDB, DuckLake, or Iceberg - Converting telemetry to Parquet, CSV, or anything DuckDB writes - Inspecting traces, logs, and metrics during local development ## Good to know - The live server is native-only and HTTP-only — no WASM, no gRPC. - File reads are capped at 100 MB each. - Summary metrics and the unified `read_otlp_metrics()` are not implemented yet. ## References - [OpenTelemetry Protocol (OTLP)](https://opentelemetry.io/docs/specs/otlp/) - [OpenTelemetry ClickHouse Exporter](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/exporter/clickhouseexporter) extension_star_count: 97 extension_star_count_pretty: 97 extension_download_count: 24491 extension_download_count_pretty: 24.5k image: '/images/community_extensions/social_preview/preview_community_extension_otlp.png' layout: community_extension_doc --- ### Compatibility Matrix This table covers all DuckDB releases that have not reached [end-of-life](https://duckdb.org/release_calendar), including LTS releases, and shows the 2 most recent patch releases of each. Older patch releases are not listed, but the extension may be available for them as well.
| Platform | 1.4.4 | 1.4.5 | 1.5.5 | 1.5.6 | 2.0.0 | | :--- | :---: | :---: | :---: | :---: | :---: | | Linux AMD64 | ✅ | ❌ | ✅ | ✅ | ✅ | | Linux ARM64 | ✅ | ❌ | ✅ | ✅ | ✅ | | macOS AMD64 | ✅ | ❌ | ✅ | ✅ | ✅ | | macOS ARM64 | ✅ | ❌ | ✅ | ✅ | ✅ | | Windows AMD64 | ✅ | ❌ | ❌ | ✅ | ✅ | | Windows ARM64 | ❌ | ❌ | ❌ | ❌ | ❌ | | WebAssembly | ✅ | ❌ | ✅ | ✅ | ✅ | ### Installing and Loading ```sql INSTALL {{ page.extension.name }} FROM community; LOAD {{ page.extension.name }}; ``` {% if page.docs.hello_world %} ### Example ```sql {{ page.docs.hello_world }}``` {% endif %} {% if page.docs.extended_description %} ### About {{ page.extension.name }} {{ page.docs.extended_description }} {% endif %} ### Added Functions
| function_name | function_type | description | comment | examples | |---------------------------------|---------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------|--------------------------------------------------------------------------------------------------------------------------------------------------| | otap_serve | table | Start a live OTAP/Arrow ingest server fed by several listeners -- one buffer set and one sealer behind all of them, so any one of the URIs names the whole server to otlp_stop() and otlp_flush(). The transport is always gRPC: the canonical Arrow{Logs,Traces,Metrics}Service bidirectional streams, which are disjoint from the services otlp_serve registers. Only the otap: scheme is accepted. Rows land in the catalog named by the `catalog` parameter -- an attached database, a DuckLake or Iceberg catalog among them -- or in the default catalog when it is unset. They are buffered in memory and group-committed (sealed) in batches, so an accepted request is not durable yet: otlp_flush() commits on demand and otlp_stop() commits before returning. Returns one row per listener. | NULL | [SELECT * FROM otap_serve(['otap:localhost:4317', 'otap:localhost:14317'], token := 'replace-with-a-real-token');] | | otap_serve | table | Start a live OTAP/Arrow ingest server on one listen URI. The transport is always gRPC: the canonical Arrow{Logs,Traces,Metrics}Service bidirectional streams, which are disjoint from the services otlp_serve registers. Only the otap: scheme is accepted. Rows land in the catalog named by the `catalog` parameter -- an attached database, a DuckLake or Iceberg catalog among them -- or in the default catalog when it is unset. They are buffered in memory and group-committed (sealed) in batches, so an accepted request is not durable yet: otlp_flush() commits on demand and otlp_stop() commits before returning. Returns one row per listener. | NULL | [SELECT * FROM otap_serve('otap:localhost:4317', token := 'replace-with-a-real-token');] | | otap_serve | table | Start a live OTAP/Arrow ingest server on the default listen URI otap:localhost:4317. The transport is always gRPC: the canonical Arrow{Logs,Traces,Metrics}Service bidirectional streams, which are disjoint from the services otlp_serve registers. Only the otap: scheme is accepted. Rows land in the catalog named by the `catalog` parameter -- an attached database, a DuckLake or Iceberg catalog among them -- or in the default catalog when it is unset. They are buffered in memory and group-committed (sealed) in batches, so an accepted request is not durable yet: otlp_flush() commits on demand and otlp_stop() commits before returning. Returns one row per listener. | NULL | [SELECT * FROM otap_serve(token := 'replace-with-a-real-token');] | | otlp_flush | table | Force a synchronous seal (group-commit) on the live ingest server that owns listener `listen_uri`, so rows accepted so far become readable in the target catalog. The server keeps running. | NULL | [SELECT * FROM otlp_flush('otlp:localhost:4318');] | | otlp_seal_list | table | List the recent seal (group-commit) attempts of every live ingest server, with append and commit timings, rows and admitted bytes committed, and the error of any that failed. | NULL | [SELECT listen_uri, seal_sequence, rows_committed, duration_ms FROM otlp_seal_list();] | | otlp_serve | table | Start a live OTLP ingest server fed by several listeners -- one buffer set and one sealer behind all of them, so any one of the URIs names the whole server to otlp_stop() and otlp_flush(). The transport is OTLP/HTTP by default (POST /v1/logs, /v1/traces, /v1/metrics); transport := 'grpc' serves OTLP/gRPC unary Export instead. Only the otlp: scheme is accepted. Rows land in the catalog named by the `catalog` parameter -- an attached database, a DuckLake or Iceberg catalog among them -- or in the default catalog when it is unset. They are buffered in memory and group-committed (sealed) in batches, so an accepted request is not durable yet: otlp_flush() commits on demand and otlp_stop() commits before returning. Returns one row per listener. | NULL | [SELECT * FROM otlp_serve(['otlp:localhost:4318', 'otlp:localhost:4317'], transport := ['http', 'grpc'], token := 'replace-with-a-real-token');] | | otlp_serve | table | Start a live OTLP ingest server on one listen URI. The transport is OTLP/HTTP by default (POST /v1/logs, /v1/traces, /v1/metrics); transport := 'grpc' serves OTLP/gRPC unary Export instead. Only the otlp: scheme is accepted. Rows land in the catalog named by the `catalog` parameter -- an attached database, a DuckLake or Iceberg catalog among them -- or in the default catalog when it is unset. They are buffered in memory and group-committed (sealed) in batches, so an accepted request is not durable yet: otlp_flush() commits on demand and otlp_stop() commits before returning. Returns one row per listener. | NULL | [SELECT * FROM otlp_serve('otlp:localhost:4318', token := 'replace-with-a-real-token');] | | otlp_serve | table | Start a live OTLP ingest server on the default listen URI otlp:localhost:4318. The transport is OTLP/HTTP by default (POST /v1/logs, /v1/traces, /v1/metrics); transport := 'grpc' serves OTLP/gRPC unary Export instead. Only the otlp: scheme is accepted. Rows land in the catalog named by the `catalog` parameter -- an attached database, a DuckLake or Iceberg catalog among them -- or in the default catalog when it is unset. They are buffered in memory and group-committed (sealed) in batches, so an accepted request is not durable yet: otlp_flush() commits on demand and otlp_stop() commits before returning. Returns one row per listener. | NULL | [SELECT * FROM otlp_serve(token := 'replace-with-a-real-token');] | | otlp_server_list | table | List every live ingest listener on this database with its server's target catalog, transport and live counters: rows received, buffered rows and bytes, seal totals and failures, last seal age and last error. | NULL | [SELECT listen_uri, transport, buffered_rows, seals_total FROM otlp_server_list();] | | otlp_stop | table | Stop every live ingest server on this database, committing each one's buffered rows before returning. One row per stopped server. Use this before closing a database you did not start every server on: a plain close drops buffered rows. | NULL | [SELECT * FROM otlp_stop();] | | otlp_stop | table | Stop the live ingest server that owns listener `listen_uri`, including its other listeners, committing its buffered rows before returning. | NULL | [SELECT * FROM otlp_stop('otlp:localhost:4318');] | | otlp_uri_parser | scalar | Parse an OTLP/OTAP listen URI into a struct of host, port, ipv6 and the http:// URL a listener would bind. The otlp: or otap: scheme is required; the host defaults to localhost and the port to 4318 for otlp: or 4317 for otap:, the same defaults otlp_serve and otap_serve apply. The argument must be constant. | NULL | [otlp_uri_parser('otlp:localhost:4318')] | | read_otap_logs | table | Read OpenTelemetry log records from an OTAP (OpenTelemetry Arrow Protocol) BatchArrowRecords file, returning the same 18 columns as read_otlp_logs. `path` is a single file or a glob, resolved through DuckDB's file systems (local, S3, HTTP(S), Azure, GCS). | NULL | [SELECT * FROM read_otap_logs('logs.bar');] | | read_otap_metrics_exp_histogram | table | Read OpenTelemetry exponential histogram data points from an OTAP (OpenTelemetry Arrow Protocol) BatchArrowRecords file, returning the same 27 columns as read_otlp_metrics_exp_histogram. One file can hold several metric shapes; this reader takes its own and skips the rest, so the four metric readers all read the same file. `path` is a single file or a glob, resolved through DuckDB's file systems (local, S3, HTTP(S), Azure, GCS). | NULL | [SELECT * FROM read_otap_metrics_exp_histogram('metrics.bar');] | | read_otap_metrics_gauge | table | Read OpenTelemetry gauge data points from an OTAP (OpenTelemetry Arrow Protocol) BatchArrowRecords file, returning the same 17 columns as read_otlp_metrics_gauge. One file can hold several metric shapes; this reader takes its own and skips the rest, so the four metric readers all read the same file. `path` is a single file or a glob, resolved through DuckDB's file systems (local, S3, HTTP(S), Azure, GCS). | NULL | [SELECT * FROM read_otap_metrics_gauge('metrics.bar');] | | read_otap_metrics_histogram | table | Read OpenTelemetry explicit-bucket histogram data points from an OTAP (OpenTelemetry Arrow Protocol) BatchArrowRecords file, returning the same 22 columns as read_otlp_metrics_histogram. One file can hold several metric shapes; this reader takes its own and skips the rest, so the four metric readers all read the same file. `path` is a single file or a glob, resolved through DuckDB's file systems (local, S3, HTTP(S), Azure, GCS). | NULL | [SELECT * FROM read_otap_metrics_histogram('metrics.bar');] | | read_otap_metrics_sum | table | Read OpenTelemetry sum (counter) data points from an OTAP (OpenTelemetry Arrow Protocol) BatchArrowRecords file, returning the same 19 columns as read_otlp_metrics_sum. One file can hold several metric shapes; this reader takes its own and skips the rest, so the four metric readers all read the same file. `path` is a single file or a glob, resolved through DuckDB's file systems (local, S3, HTTP(S), Azure, GCS). | NULL | [SELECT * FROM read_otap_metrics_sum('metrics.bar');] | | read_otap_traces | table | Read OpenTelemetry trace spans from an OTAP (OpenTelemetry Arrow Protocol) BatchArrowRecords file, returning the same 24 columns as read_otlp_traces. `path` is a single file or a glob, resolved through DuckDB's file systems (local, S3, HTTP(S), Azure, GCS). | NULL | [SELECT * FROM read_otap_traces('traces.bar');] | | read_otlp_logs | table | Read OpenTelemetry log records from OTLP JSON, NDJSON or protobuf files into a flat 18-column table. The encoding is detected from the file. `path` is a single file or a glob, resolved through DuckDB's file systems (local, S3, HTTP(S), Azure, GCS). | NULL | [SELECT * FROM read_otlp_logs('logs.pb');] | | read_otlp_metrics | table | Not implemented: every call raises. OTLP metrics have shape-specific schemas, so read one of read_otlp_metrics_gauge, read_otlp_metrics_sum, read_otlp_metrics_histogram or read_otlp_metrics_exp_histogram instead. | NULL | | | read_otlp_metrics_exp_histogram | table | Read OpenTelemetry exponential histogram data points from OTLP JSON, NDJSON or protobuf files into a flat 27-column table with scale, zero bucket and positive/negative buckets. One file can hold several metric shapes; this reader takes its own and skips the rest, so the four metric readers all read the same file. `path` is a single file or a glob, resolved through DuckDB's file systems (local, S3, HTTP(S), Azure, GCS). | NULL | [SELECT * FROM read_otlp_metrics_exp_histogram('metrics.pb');] | | read_otlp_metrics_gauge | table | Read OpenTelemetry gauge data points from OTLP JSON, NDJSON or protobuf files into a flat 17-column table. One file can hold several metric shapes; this reader takes its own and skips the rest, so the four metric readers all read the same file. `path` is a single file or a glob, resolved through DuckDB's file systems (local, S3, HTTP(S), Azure, GCS). | NULL | [SELECT * FROM read_otlp_metrics_gauge('metrics.pb');] | | read_otlp_metrics_histogram | table | Read OpenTelemetry explicit-bucket histogram data points from OTLP JSON, NDJSON or protobuf files into a flat 22-column table with bucket bounds and counts. One file can hold several metric shapes; this reader takes its own and skips the rest, so the four metric readers all read the same file. `path` is a single file or a glob, resolved through DuckDB's file systems (local, S3, HTTP(S), Azure, GCS). | NULL | [SELECT * FROM read_otlp_metrics_histogram('metrics.pb');] | | read_otlp_metrics_sum | table | Read OpenTelemetry sum (counter) data points from OTLP JSON, NDJSON or protobuf files into a flat 19-column table with aggregation_temporality and is_monotonic. One file can hold several metric shapes; this reader takes its own and skips the rest, so the four metric readers all read the same file. `path` is a single file or a glob, resolved through DuckDB's file systems (local, S3, HTTP(S), Azure, GCS). | NULL | [SELECT * FROM read_otlp_metrics_sum('metrics.pb');] | | read_otlp_metrics_summary | table | Not implemented: every call raises. OTLP summary data points are not decoded; the other read_otlp_metrics_* readers count them as skipped. | NULL | | | read_otlp_traces | table | Read OpenTelemetry trace spans from OTLP JSON, NDJSON or protobuf files into a flat 24-column table, including events, links and a computed duration. `path` is a single file or a glob, resolved through DuckDB's file systems (local, S3, HTTP(S), Azure, GCS). | NULL | [SELECT * FROM read_otlp_traces('traces.pb');] | ### Overloaded Functions
This extension does not add any function overloads. ### Added Types
This extension does not add any types. ### Added Settings
| name | description | input_type | scope | aliases | typed_value | |----------------|---------------------------------------------------------|------------|--------|---------|-------------| | temp_directory | Set the directory to which temporary files are written. | VARCHAR | GLOBAL | [] | post.db.tmp |