DuckDB

Telemetry for DuckDB Extensions Without the Pain

I open-sourced the telemetry client I use across Query.Farm's DuckDB extensions. It's two files, one function call, and it only collects platform and version info.

I maintain a lot of DuckDB extensions at Query.FarmAirport, Tributary, Marisa, and quite a few others. One thing I never had good visibility into was which platforms and DuckDB versions people were actually using them on. DuckDB runs on a wide matrix — linux_amd64, osx_arm64, WASM, Windows — and each new DuckDB release can change what works and what doesn’t. I was guessing about what to test against.

So I built a small telemetry client that I embed in all of my extensions. It sends a single JSON payload when the extension loads — just platform and version information, nothing else. I’ve been using it for a while now, and it’s been useful enough that I’ve open-sourced it for other extension authors.

What Gets Sent

One JSON object, once, at extension load time:

{
  "extension_name": "your_extension",
  "extension_version": "v1.0.0",
  "duckdb_platform": "linux_amd64",
  "duckdb_library_version": "v1.2.1",
  "duckdb_release_codename": "Kirimomi",
  "duckdb_source_id": "abc123"
}

No queries, no file paths, no device IDs, no PII. Just enough to answer “what platforms are people running my extension on?” and “which DuckDB versions are in the wild?”

Two Files, One Function Call

The whole thing is a header and a .cpp file that you copy into your extension’s source tree. No submodules, no package manager. It uses DuckDB’s bundled yyjson for JSON and auto-loads httpfs at runtime for the HTTP request — so there are zero external dependencies.

Add it to your CMakeLists.txt:

set(EXTENSION_SOURCES
    src/your_extension.cpp
    src/query_farm_telemetry.cpp
)

Then call it at the end of your Load function:

#include "query_farm_telemetry.hpp"

static void LoadInternal(ExtensionLoader &loader) {
    // ... your extension loading code ...

    QueryFarmSendTelemetry(loader, "your_extension_name", "v1.0.0");
}

That’s it. Three arguments — the loader, your extension name, your version string.

How It Behaves

I care a lot about telemetry not getting in the way. A few things I was deliberate about:

The HTTP POST runs asynchronously via std::async on native platforms — extension loading isn’t delayed at all. On WASM, where threads aren’t available, it runs synchronously, but the payload is small enough that it doesn’t matter.

If httpfs can’t be loaded — maybe someone’s running DuckDB in an air-gapped environment — the function returns silently. If the HTTP request itself fails, the exception is caught and discarded. Telemetry never causes an extension to break. This was a hard rule from the start.

The INTERNAL_FUNC macro applies __attribute__((visibility("hidden"))) on GCC/Clang so the telemetry function doesn’t show up in your extension’s public symbol table. On Windows, symbols are hidden by default.

And there’s an opt-out. Set QUERY_FARM_TELEMETRY_OPT_OUT to any value:

export QUERY_FARM_TELEMETRY_OPT_OUT=1

The function returns immediately without sending anything.

Why I’m Sharing This

The DuckDB community extension ecosystem is growing fast. But as an extension author, you’re flying blind — you don’t know which platforms people use, which DuckDB versions they’re on, or whether anyone is loading your extension at all. I was in that position, and the data from this telemetry client has directly influenced which platforms I run CI against and when I drop support for older DuckDB versions.

If you maintain a DuckDB extension and want that same visibility, this is a low-effort way to get it.

Get It

MIT-licensed. Two source files:

github.com/Query-farm/query-farm-telemetry-client

#DuckDB #C++ #Open Source #Query.Farm #DuckDB Extensions