DEV Community

Jaeyoun Nam
Jaeyoun Nam

Posted on

1 1

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

Image of Docusign

🛠️ Bring your solution into Docusign. Reach over 1.6M customers.

Docusign is now extensible. Overcome challenges with disconnected products and inaccessible data by bringing your solutions into Docusign and publishing to 1.6M customers in the App Center.

Learn more

Top comments (0)

Image of Docusign

🛠️ Bring your solution into Docusign. Reach over 1.6M customers.

Docusign is now extensible. Overcome challenges with disconnected products and inaccessible data by bringing your solutions into Docusign and publishing to 1.6M customers in the App Center.

Learn more