Observability

#Zuplo OpenTelemetry Plugin

Zuplo ships with an OpenTelemetry plugin that allows you to collect and export telemetry data from your Zuplo API. Currently, the OpenTelemetry plugin implements tracing. Metrics and logging support are planned for future releases.

Enterprise Feature

OpenTelemetry is available as an add-on as part of an enterprise plan. If you would like to purchase this feature, please contact us at sales@zuplo.com or reach out to your account manager.

Most enterprise features can be used in a trial mode for a limited time. Feel free to use enterprise features for development and testing purposes.

#Tracing

Tracing enables you to monitor performance, identify bottlenecks, and troubleshoot issues in your Zuplo API. The OpenTelemetry plugin automatically instruments your API to collect trace data. You can send trace data any OpenTelemetry service such as Honeycomb, Dynatrace, Jaeger, and many more.

With tracing enabled on your Zuplo API you will see timings for each request as well as spans for plugins, handlers, and policies. The OpenTelemetry plugin supports trace propagation (W3C headers by default) so you can trace requests all the way from the client to your backend.

Trace visualization

#What is Traced?

By default, when the OpenTelemetry plugin is enabled, the following is traced:

  • Request: The entire request lifecycle is traced, including the time taken to process the request and send the response.
  • Inbound Policies: The time taken to execute all inbound policies as well as each policy is traced.
  • Handler: The request handler is traced
  • Outbound Policies: The time taken to execute all outbound policies as well as each policy is traced.
  • Subrequests: Any use of fetch within your custom policies or handlers will be traced.

#Limitations

One important limitation to keep in mind is that the clock will only increment when performing I/O operations (for example when calling fetch, using the Cache APIs, etc.). This is a limitation imposed as a security measure due Zuplo's serverless, multi-tenant architecture. In practice this shouldn't impact your ability to trace as virtually any code that isn't I/O bound is extremely fast.

#Custom Tracing

You can add custom tracing to your Zuplo API by using the OpenTelemetry API. The example below shows how to implement tracing in a custom policy.

import { ZuploContext, ZuploRequest } from "@zuplo/runtime";
import { trace } from "@opentelemetry/api";

export default async function policy(
  request: ZuploRequest,
  context: ZuploContext,
) {
  const tracer = trace.getTracer("my-tracer");
  return tracer.startActiveSpan("my-span", async (span) => {
    span.setAttribute("key", "value");

    try {
      const results = await Promise.all([
        fetch("https://api.example.com/hello"),
        fetch("https://api.example.com/world"),
      ]);
      // ...

      return request;
    } finally {
      span.end();
    }
  });
}
ts

This will result in a span that has the following spans:

|--- my-policy
|    |
|    |--- my-span
|    |    |
|    |    |--- GET https://api.example.com/hello
|    |    |
|    |    |--- GET https://api.example.com/world
txt

#Setup

Adding OpenTelemetry tracing to your Zuplo API is done by adding the OpenTelemetryPlugin in the zuplo.runtime.ts file as shown below.

For most providers you will set values for exporter.url and exporter.headers. It is common for providers to use a header for authorization.

import { OpenTelemetryPlugin } from "@zuplo/otel";
import { RuntimeExtensions, environment } from "@zuplo/runtime";
export function runtimeInit(runtime: RuntimeExtensions) {
  runtime.addPlugin(
    new OpenTelemetryPlugin({
      exporter: {
        url: "https://otlp.example.com",
        headers: {
          Authorization: `Bearer ${environment.OTEL_API_KEY}`,
        },
      },
      service: {
        name: "my-api",
      },
    }),
  );
}
ts