Skip to content

The Telemetry Package

vantacloud-telemetry is a naming layer over the plain OpenTelemetry SDK of your language. It writes the names, units, labels and boundaries Telemetry Conventions states, so your code names its operations and the package does the rest. It installs no framework, owns no exporter of its own and wraps nothing but the SDK, so every other OpenTelemetry instrument and library your code uses works beside it unchanged.

LanguagePackageInstalled from, once released publicly
Rustvantacloud-telemetrycrates.io
TypeScript and JavaScript on Node.jsvantacloud-telemetrynpm
Pythonvantacloud-telemetryPyPI

The packages are published to these registries from the first public release on; before it, they are available to the organization’s own projects from its package feed, and the plain-SDK path below needs no package at all.

Each package names the conventions version it writes, 1 today, as a constant it exports and in the vantacloud.conventions.version attribute it sets on every record; a package’s own version says nothing of the conventions, and a release that moves to a new conventions version says so in its notes. Every package is open source under the Apache-2.0 OR MIT license, so you read, vendor or fork it as you like. From the first public release on, the source is the public mirror github.com/TheClockwyrks/VantaCloudTelemetry, which the packages name as their repository.

init sets up the SDK’s tracer, meter and logger providers over the OTLP exporter, with the resource attributes service.name, service.version and vantacloud.conventions.version. It takes the service’s name and version and nothing else, and it applies the histogram boundaries of the conventions to every duration histogram.

init reads its environment from the standard OpenTelemetry variables alone, OTEL_EXPORTER_OTLP_ENDPOINT for where it exports and OTEL_RESOURCE_ATTRIBUTES for the resource attributes the platform adds, and from no configuration key of your project. The platform sets both on your workload’s environment. The endpoint is an http:// or an https:// URL, the second reached over TLS with the collector’s certificate verified against the system’s trust store, so a collector outside the cluster is named as readily as the platform’s own. init sets service.instance.id to the pod’s name, read from HOSTNAME, unless OTEL_RESOURCE_ATTRIBUTES names one, so each replica of your workload exports its own series of every metric.

init registers the process gauges: process.memory.usage, and the allocator gauges where the process’s allocator offers them. In Rust they are read from jemalloc on Linux targets, so a Rust service sets tikv-jemallocator as its global allocator under cfg(target_os = "linux") and enables the package’s jemalloc feature, which exports the allocator’s statistics on Linux alone and does nothing on any other target. A Node.js or Python process reports process.memory.usage alone.

operation wraps one operation. It takes the operation’s name, whether callers wait on it beyond its acknowledgement and, optionally, the instant the operation started, opens a span named for the operation, and records vantacloud.operation.ack when your code marks the acknowledgement and vantacloud.operation.duration when the operation ends, each with the outcome class your code’s result maps to. Both count from the start instant, the moment operation is called when none is given. Work claimed from a queue passes the enqueue time stored with its job, so its ack counts from the job’s enqueueing as the conventions state.

An HTTP middleware for the common servers of each language wraps every route this way, naming each by its method and route template. It records the ack at the response’s first byte. For a route your code marks waited on, it wraps the response’s body and records the duration when the body ends, so a streamed answer’s duration runs to its last byte.

The db module records the database series of a process holding its own connection pool. The pool reports each event to it as the event happens:

EventCarriesRecords
A connection takenThe operation it was taken for and how long the caller waitedvantacloud.db.pool.wait
A connection releasedHow long it was heldvantacloud.db.connection.held
A statement finishedIts schema, its command and its durationvantacloud.db.statement.duration
An errorIts kindvantacloud.db.errors

Your pool’s wiring calls the module, in Rust from the pool’s acquire and release hooks. The module also samples the engine every 15 seconds for the lock waits of the pool’s database role, recording vantacloud.db.lock.waits and vantacloud.db.lock.wait by pool and database alone.

error emits an error log record in the shape Error Logging states, inside the current span, from the error, its kind and the operation.

use vantacloud_telemetry::{Outcome, Telemetry};
#[cfg(target_os = "linux")]
#[global_allocator]
static ALLOCATOR: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc;
let telemetry = Telemetry::init("shop-api", env!("CARGO_PKG_VERSION"))?;
let job = queue.claim().await?;
let settled = telemetry
.operation("orders.settle")
.waited_on()
.started_at(job.due_at.max(job.enqueued_at))
.run(|op| async move {
op.acknowledge();
settle(job.order).await
})
.await;

The outcome class is read from the error’s code: invalid-argument, not-found, conflict, refused, limit-exceeded and permission-denied are client-error, and every other code is server-error.

import { init } from "vantacloud-telemetry";
const telemetry = init({ service: "shop-api", version: "1.4.2" });
const job = await queue.claim();
await telemetry.operation(
"orders.settle",
{ waitedOn: true, startedAt: max(job.dueAt, job.enqueuedAt) },
async (op) => {
op.acknowledge();
await settle(job.order);
},
);
from vantacloud_telemetry import init
telemetry = init(service="shop-api", version="1.4.2")
job = await queue.claim()
async with telemetry.operation(
"orders.settle", waited_on=True, started_at=max(job.due_at, job.enqueued_at)
) as op:
op.acknowledge()
await settle(job.order)

The package is a convenience. Code that sets the resource attributes, registers the instruments under the names, units, labels and boundaries the conventions state and logs errors in their shape, through the plain OpenTelemetry SDK of any language, is conformant in the same way, and its dashboards and alerts are the same.