DEV Community

Jaeyoun Nam
Jaeyoun Nam

Posted on

NestJS + Opentelemetry (Loki)

Prerequisite

NestJS to Loki

원래는 Zero-code Opentelemetry 설정을 하면 자동으로 로그도 Otel collector에 보내져서 Loki에 쌓여야하는게 맞다.

근데 logging auto instrument에 이슈가 있어서, 제대로 설정해주지 않으면 로그가 쌓이지 않는다.

(이 버그덕에 Otel 공부함 🍀)

제대로 자동 설정하는 법

이슈에 있는 조합법. (뭐 마법도 아니고)

auto-instrumentation in SDK (not working)
instrumentation-winston in SDK (not working)
instrumentation-winston in SDK + winston-transport in logger transports (working)
auto-instrumentation in SDK + winston-transport in logger transports (working)
auto-instrumentation in SDK + winston-transport installed only (working)
Enter fullscreen mode Exit fullscreen mode

결국 이유는 winston-transport 가 instrumentation-winston의 dependency로 들어가 있지 않아서 따로 깔아줘야 한다는 것이다.

npm install @opentelemetry/winston-transport를 꼭 해주자.

운 좋게 제대로 설정했으면 굳이 아래 방법을 안따라도 된다.

Winston

로거로는 Winston으로 정했다. 원래 Pino를 쓰고있었지만, 이 포스팅보고 winston으로 바꿨다. (+ Winston에 대한 정보가 더 인터넷에 많음)

Pino를 쓰고 있더라도 아래 방법은 그대로 적용될 것이다.

Code

Manually Setup LoggerProvider

새로운 파일 logger.ts를 만들어 로거 세팅 작업을 해줍니다.

import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-http";
import { Resource } from "@opentelemetry/resources";
import {
  BatchLogRecordProcessor,
  LoggerProvider,
} from "@opentelemetry/sdk-logs";
import {
  SEMRESATTRS_SERVICE_NAME,
  SEMRESATTRS_SERVICE_VERSION,
} from "@opentelemetry/semantic-conventions";
import { OpenTelemetryTransportV3 } from "@opentelemetry/winston-transport";

const logExporter = new OTLPLogExporter();
const loggerProvider = new LoggerProvider({
  resource: new Resource({
    [SEMRESATTRS_SERVICE_NAME]: "your-service-name",
    [SEMRESATTRS_SERVICE_VERSION]: "1.0",
  }),
});

loggerProvider.addLogRecordProcessor(
  new BatchLogRecordProcessor(logExporter)
  // new SimpleLogRecordProcessor(new ConsoleLogRecordExporter())
);
api.logs.setGlobalLoggerProvider(loggerProvider);
Enter fullscreen mode Exit fullscreen mode

LoggerProvider를 만들어서 Exporter와 Resource, Processor를 연결한 후 GLobalLoggerProvider에 설정합니다.

Create Winston Logger

그 후에 nestJS에서 쓸 winston logger를 만들어 줍니다. 역시 logger.ts에 작성합니다.

import { OpenTelemetryTransportV3 } from "@opentelemetry/winston-transport";

export default function createLogger() {
  const transports = [
    new winston.transports.Console({
      format: winston.format.combine(
        winston.format.timestamp({ format: "YYYY-MM-DDTHH:mm:ss.SSSZ" }),
        winston.format.json(),
        winston.format.ms(),
        nestWinstonModuleUtilities.format.nestLike("API", {
          colors: true,
          prettyPrint: true,
          processId: true,
          appName: true,
        })
      ),
    }),
    new OpenTelemetryTransportV3(),
  ];
  const logger = WinstonModule.createLogger({
    defaultMeta: { environment: process.env.NODE_ENV },
    transports,
  });

  return logger;
}
Enter fullscreen mode Exit fullscreen mode

여기서 OpenTelemetryTransportV3를 winston의 transport로 등록하는데, 이게 winston에서 나온 로그를 oltp로 내보낼 수 있도록 해줍니다.

Import logger

tracer와 마찬가지로 logger도 main.ts의 맨 위에서 임포트해줍니다.

// eslint-disable-next-line import/order
import otelSDK from "./tracer"; // otelSDK should be imported before any other imports
// eslint-disable-next-line import/order
import createLogger from "./logger";
Enter fullscreen mode Exit fullscreen mode

Attach Logger

NestApp을 만들 때, 로거를 만들어 넘겨줍니다. 이는 nestjs/common의 logger를 대체합니다.

  const app = await NestFactory.create<NestExpressApplication>(
    AppModule,
    new ExpressAdapter(expressApp),
    {
      logger: createLogger(),
Enter fullscreen mode Exit fullscreen mode

Provider Winston Module

nestjs-winston으로 winston logger를 만들었으니, AppModule에 Provider로 제공해줍니다.

@Module({
  providers: [
    Logger,

Enter fullscreen mode Exit fullscreen mode

결과

이제 Winston에서 나온 로그는 Logger provider로 transport 되어 LoggerProvider의 exporter에 의해 OLTP endpoint(http://localhost:4317 or http://localhost:4318)로 전송되게 됩니다.

미리 띄워둔 Collector에 의해 Log 데이터는 수집될 것이고 Loki에 저장된다.

결과 화면

Grafana에 접속해
Explore -> Data soruce: Loki -> Label browser -> Select Service -> Show logs 로 로그를 볼 수 있다.

Image description

Top comments (0)