Skip to content
This repository has been archived by the owner on Feb 3, 2023. It is now read-only.

Latest commit

 

History

History
172 lines (130 loc) · 4.71 KB

README.md

File metadata and controls

172 lines (130 loc) · 4.71 KB

FWL Tracing

Disclaimer: this package is made for our internal usage and is only open source for convenience so we might not consider Pull Requests or Issues. Feel free to fork though.

This is part of the Fewlines Web Libraries packages. It provides a simple way to do tracing with OpenTelemetry.

Installation

yarn add @fwl/tracing

Getting Started

As OpenTelemetry libraries need to monkey patch packages before them being called, starting the tracer should be the first thing you do.

Here's an example with a simple collector (e.g. Zipkin):

import { getTracer, startTracer } from "@fwl/tracing";

startTracer({
  simpleCollector: {
    serviceName: "serviceName",
    url: "http://localhost:9411/api/v2/spans",
  },
});

const tracer = getTracer();

Configure several exporters

import { getTracer, startTracer } from "@fwl/tracing";

startTracer({
  collectors: [
    {
      type: "otel",
      serviceName: "serviceName",
      url: "http://localhost:29799/v1/traces",
    },
    {
      type: "otel",
      serviceName: "serviceName",
      url: "http://localhost:8360/api/v2/otel/trace",
      authorizationHeader: {
        key: "Lightstep-Access-Token",
        value: "developer",
      },
    },
  ],
});

Note that if you configure several exporters, the serviceName property that will be used to define the service name of all exporters will be the first one.

Lightstep

And an example with a Lightstep public satellite:

import { getTracer, startTracer } from "@fwl/tracing";

startTracer({
  lightstepPublicSatelliteCollector: {
    serviceName: "serviceName",
    accessToken: process.env.LIGHTSTEP_ACCESS_TOKEN,
  },
});

const tracer = getTracer();

If you want to use Lightstep in developer mode, you could add an URL:

startTracer({
  lightstepPublicSatelliteCollector: {
    serviceName: "serviceName",
    accessToken: "",
    url: "http://localhost:8360/api/v2/otel/trace",
  },
});

Usage

Once you have a tracer (of type Tracer), you will have a span method:

import { Span, Tracer } from "@fwl/tracing";

// ...
function(tracer: Tracer) {
  tracer.span("span name", async (span: Span) => {
    // If you want to add attributes to your span with a visible value
    span.setDisclosedAttribute(key, value);

    // If you want to add attributes to your span with a redacted value
    span.setAttribute(key, value);

    // any code there that return a Promise (in this example, the callback is an `async` function so any value should do
  });
}

This way is the recommended way as it will automatically end the span for you. However, if you ever are in need to more fine grain control (for instance, in a middleware), you can use createSpan:

Here is an example of a logging middleware for Express:

import { NextFunction, Request, Response } from "express";
import { Logger } from "@fwl/logging";
import { Tracer, Span } from "@fwl/tracing";

export function loggingMiddleware(tracer: Tracer, logger: Logger) {
  function onCloseOrFinish(span: Span, startTime: bigint): () => void {
    return function () {
      const response = this as Response;
      response.removeListener("finish", onCloseOrFinish);
      const end = process.hrtime.bigint();
      logger.log(
        `${response.req.path}: ${response.statusCode} in ${end - startTime}`,
      );
      span.end();
    };
  }
  return function (
    _request: Request,
    response: Response,
    next: NextFunction,
  ): void {
    const startTime = process.hrtime.bigint();
    const span = tracer.createSpan("logging middleware");
    response.once("finish", onCloseOrFinish(span, startTime));
    next();
  };
}

Keep in mind that this method is only required because we need to call next() and we want to start the span across the whole request. The recommended method is to use tracer.span.

Tracing during tests

If you need to use the tracer in a testing environment, we provide a InMemoryTracer class that act as a regular tracer, except you won't have to launch jaeger to run your tests.InMemoryTracer also provides you with a way of testing your spans with the use of the searchSpanByName. The usage is the same, you just need to initialize InMemoryTracer instead of using startTracer().

Here is an example of use in a test file using jest:

import { InMemoryTracer } from "@fwl/tracing";

let tracer: InMemoryTracer;

beforeEach(() => {
  tracer = new InMemoryTracer();
});

test("verify span attributes", () => {
  expect.assertions(1);

  // Call the code that is using a tracer.

  const [span] = tracer.searchSpanByName("test-span");

  expect(span.attributes[0].attributeName).toBe("attribute value");
});