Effect CommunityEC
Effect Community14mo ago
3 replies
alerosa

Issues with OpenTelemetry Spans in Next.js Environment

Hi everyone, I'm working with OTEL in a Nextjs environment but I'm not able to get layer spans to work correctly.

Given the following code, when I make a request for the first time I get two traces:
- one trace has the all the Nextjs spans, and all my ON EVERY REQUEST spans nested correctly
- the other trace only has the ON FIRST REQUEST ONLY - 3 span, while the other ON FIRST REQUEST ONLY spans are nowhere to be seen.

Every subsequent request only has one trace with all the ON EVERY REQUEST and Nextjs spans, which is what I expect because layers have already been built.

import { Resource, Tracer } from "@effect/opentelemetry";
import { trace } from "@opentelemetry/api";
import { Context, Effect, Layer, ManagedRuntime } from "effect";

const getParentSpan = () => {
  const span = trace.getActiveSpan();
  return span && Tracer.makeExternalSpan(span.spanContext());
};

const effectWithActiveSpan = <A, E, R>(effect: Effect.Effect<A, E, R>) => {
  const parent = getParentSpan();

  return parent
    ? effect.pipe(Effect.withParentSpan(parent))
    : effect.pipe(Effect.withSpan("orphaned"));
};

const make = Effect.gen(function* () {
  yield* Effect.logInfo("Executed once when layers are built");

  return {
    doSomething: () =>
      Effect.gen(function* () {
        yield* Effect.logInfo("Executed on every request");
        return;
      }).pipe(Effect.withSpan("ON EVERY REQUEST - 3")),
  };
}).pipe(Effect.withSpan("ON FIRST REQUEST ONLY - 3")); // creates a span in a different trace

class MyService extends Context.Tag("MyService")<
  MyService,
  Effect.Effect.Success<typeof make>
>() {
  static Live = Layer.effect(this, make).pipe(
    Layer.withSpan("ON FIRST REQUEST ONLY - 2"), // does not create a span
  );
}

const program = () =>
  Effect.gen(function* () {
    const myService = yield* MyService;
    return yield* myService.doSomething();
  }).pipe(Effect.withSpan("ON EVERY REQUEST - 2"));

const TracingLive = Layer.provide(
  Tracer.layerGlobal,
  Resource.layer({ serviceName: "test" }),
);

const MainLayer = MyService.Live.pipe(
  Layer.provide(TracingLive),
  Layer.withSpan("ON FIRST REQUEST ONLY - 1"), // does not create a span
);

const managedRuntime = ManagedRuntime.make(MainLayer);

export async function POST() {
  await program().pipe(
    Effect.withSpan("ON EVERY REQUEST - 1"),
    effectWithActiveSpan,
    managedRuntime.runPromiseExit,
  );

  return new Response(null);
}


My expectation would be that on the first request I should have all the Nextjs spans + all my spans to belong to the same trace.
For some reasons it seems that Layer.withSpan doesn't create any span in this case, while Effect.withSpan attached to my service constructor effect does, but on a different trace.

Any idea on what's wrong with my code? Thank you
Was this page helpful?