DEV Community

Jaeyoun Nam
Jaeyoun Nam

Posted on

NestJS + Opentelemetry (Trace)

Prerequisite

NestJS에 Opentelemetry 설정

Opentelemetry 홈페이지에 가면 nodejs에 opentelemetry(이하 otel)를 설정하는 방법을 볼 수 있다.

여러가지 방법이 있는데, "알아서 자동으로" 설정해주는 Zero-code instrumentation과 Otel SDK를 사용하는 방법이 있다.

Zero-code instrumentation

NodeJS는 Zero-code instrumentation을 지원한다. 아래 패키지들을 설치하고 환경 변수만 설정해주면 끝이다.

npm install --save @opentelemetry/api
npm install --save @opentelemetry/auto-instrumentations-node
Enter fullscreen mode Exit fullscreen mode
OTEL_TRACES_EXPORTER="otlp"
OTEL_EXPORTER_OTLP_ENDPOINT="your-endpoint"
OTEL_NODE_RESOURCE_DETECTORS="env,host,os"
OTEL_SERVICE_NAME="your-service-name"
NODE_OPTIONS="--require @opentelemetry/auto-instrumentations-node/register"
Enter fullscreen mode Exit fullscreen mode

만약 .env를 사용한다면 opentelemetry SDK를 초기화하기전에 dotenv로 로드해줘야한다.

Setup with SDK

  • 많은 코드는 아니지만 코드를 짜서 Otel Javascript SDK를 설정하고 시작하는 방법이다.

  • Zero-code를 시도했는데 잘 안되거나, 좀 더 직관적으로 configuration을 설정할 때 좋은 듯 하다.

1. Setup opentelemetry SDK

  • tracer.ts 파일을 만들어 SDK를 정의하여 export한다.
import { diag, DiagConsoleLogger, DiagLogLevel } from "@opentelemetry/api";
import { getNodeAutoInstrumentations } from "@opentelemetry/auto-instrumentations-node";
import {
  CompositePropagator,
  W3CTraceContextPropagator,
  W3CBaggagePropagator,
} from "@opentelemetry/core";
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-grpc";
import { B3InjectEncoding, B3Propagator } from "@opentelemetry/propagator-b3";
import { Resource } from "@opentelemetry/resources";
import { NodeSDK } from "@opentelemetry/sdk-node";
import {
  BatchSpanProcessor,
  // ConsoleSpanExporter,
} from "@opentelemetry/sdk-trace-node";
import {
  SEMRESATTRS_SERVICE_NAME,
  SEMRESATTRS_SERVICE_VERSION,
} from "@opentelemetry/semantic-conventions";

diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.INFO);

// const traceExporter = new ConsoleSpanExporter();
const traceExporter = new OTLPTraceExporter();
const otelSDK = new NodeSDK({
  traceExporter: traceExporter,
  instrumentations: [
    getNodeAutoInstrumentations({
      "@opentelemetry/instrumentation-fs": { enabled: false },
    }),
  ],
  resource: new Resource({
    [SEMRESATTRS_SERVICE_NAME]: "your-service-name",
    [SEMRESATTRS_SERVICE_VERSION]: "1.0",
  }),
  spanProcessors: [new BatchSpanProcessor(traceExporter)],
  textMapPropagator: new CompositePropagator({
    propagators: [
      new W3CTraceContextPropagator(),
      new W3CBaggagePropagator(),
      new B3Propagator(),
      new B3Propagator({
        injectEncoding: B3InjectEncoding.MULTI_HEADER,
      }),
    ],
  }),
});

export default otelSDK;
Enter fullscreen mode Exit fullscreen mode

구문 설명

(코드만 복붙해갈걸 알지만...)

1
diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.INFO);
Enter fullscreen mode Exit fullscreen mode

Otel에서 내뱉는 로그들을 콘솔로 출력한다. 아래와 같은 형식으로 에러를 보여주니 뭔가 연결이 잘 안되는데 싶으면 켜서 보면 된다. (localhost:4317 와 연결할 수 없다는 에러; opentelemetry collector docker를 안켜놓으면 일어날 수 있느 에러다)

1.9.0/node_modules/@opentelemetry/otlp-grpc-exporter-base/src/grpc-exporter-transport.ts:160:26\n    at new Promise (<anonymous>)","message":"14 UNAVAILABLE: No connection established. Last error: connect ECONNREFUSED 127.0.0.1:4317 (2024-08-18T06:42:39.757Z)","code":"14","details":"No connection established. Last error: connect ECONNREFUSED 127.0.0.1:4317 (2024-08-18T06:42:39.757Z)","metadata":"[object Object]","name":"Error"}
Enter fullscreen mode Exit fullscreen mode
2
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-grpc";

// const traceExporter = new ConsoleSpanExporter();
const traceExporter = new OTLPTraceExporter();
Enter fullscreen mode Exit fullscreen mode

traceExporter를 설정한다. ConsoleSpanExporter로 설정하면 trace를 콘솔에 쏴주므로 디버깅 하기 좋다. 실제로 사용할 땐 OTLPTraceExporter를 설정해서 otel endpoint로 쏴주도록 하자. (default는 'localhost:4317')

Note: OTLPTraceExporter 를 "@opentelemetry/exporter-trace-otlp-grpc"에서 import 했기 때문에 GRPC의 기본 포트인 4317로 쏘는 것이다. http나 proto 패키지에서 import 했다면 4318포트로 쏠 것이다.

3
  instrumentations: [
    getNodeAutoInstrumentations({
      "@opentelemetry/instrumentation-fs": { enabled: false },
    }),
  ],
Enter fullscreen mode Exit fullscreen mode

추적 가능하게 만들것에 대한 설정이다. getNodeAutoInstrumentations는 자동으로 쓰고 있는 패키지를 분석해서 그것들을 wrapping하여 Span을 생성하도록 만든다.

"@opentelemetry/instrumentation-fs": { enabled: false } 는 자동으로 설정할 때 instrumentation-fs 는 빼고 하라는 말이다. fs에 쓰고 읽고 하는 것은 보통의 경우에 trace할 필요가 없다.

4
  spanProcessors: [new BatchSpanProcessor(traceExporter)],
Enter fullscreen mode Exit fullscreen mode

SpanProcessor는 만들어진 Span을 Export하기 전에 처리하는 단계이다. Span을 만들자마자 하나씩 보내지말고 Batch시켜서 보내준다.

SimpleSpanProcessor를 사용해도 되는데, 대부분의 경우 BatchSpanProcessor 를 추천한다.

5
  textMapPropagator: new CompositePropagator({
    propagators: [
      new W3CTraceContextPropagator(),
      new W3CBaggagePropagator(),
      new B3Propagator(),
      new B3Propagator({
        injectEncoding: B3InjectEncoding.MULTI_HEADER,
      }),
    ],
  }),
Enter fullscreen mode Exit fullscreen mode

Propagator는 말 그대로 전파시키기를 담당한다. Opentelemetry의 Trace가 의미가 있는 이유는 각 Span들이 공통된 정보로 이어져 있기 때문에 서로 연관성 있게 분석할 수 있다는 것이다.

여기서 W3CTraceContextPropagator는 traceId 같은 trace내에서 공통된 정보(Context)들을 전파한다.

W3CBaggagePropagator는 Baggage라는 정보를 Span에 전달한다. Baggage에는 User Id나 Project Id 같은 현재 Trace내의 Span에서 찍히면 좋을 것들이 담겨있다.

주의사항은 Baggage는 http request에도 찍히기 때문에 절대 credential 정보를 넣으면 안된다.

2. Import otelSDK

main.ts(프로그램 시작점)에서 otelSDK를 임포트한다.

// eslint-disable-next-line import/order
import otelSDK from "./tracer"; // otelSDK should be imported before any other imports

// You can also use the shutdown method to gracefully shut down the SDK before process shutdown
// or on some operating system signal.
process.on("SIGTERM", () => {
  otelSDK
    .shutdown()
    .then(
      () => console.log("SDK shut down successfully"),
      (err) => console.log("Error shutting down SDK", err)
    )
    .finally(() => process.exit(0));
});
Enter fullscreen mode Exit fullscreen mode

주의할 점은 linter가 자동으로 otelSDK import 구문을 아래로 내려버리기 때문에 linter disable하고 맨위에 import를 해줘야한다는 것이다.

3. start otelSDK

otelSDK.start();
Enter fullscreen mode Exit fullscreen mode

App Module 만들기 전에 (bootstrap전에) 시작해준다.

트레이스 확인하기

Grafana + Tempo 를 통해 확인할 수 있다.

Top comments (0)