NestJS + Opentelemetry (Loki)
Jaeyoun Nam
Posted on August 18, 2024
Prerequisite
Opentelemetry Collector 가 준비되어 있어야한다. (준비하러가기)
Opentelemetry Trace를 볼 수 있는 Grafana Setting (준비하러가기)
(옵션) Opentelemetry에 대한 지식 (하다가 막히고 공부해도 된다 ^0^) (공부하러가기)
(옵션) NestJS + Opentelemetry 포스팅 (보러가기)
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)
결국 이유는 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);
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;
}
여기서 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";
Attach Logger
NestApp을 만들 때, 로거를 만들어 넘겨줍니다. 이는 nestjs/common의 logger를 대체합니다.
const app = await NestFactory.create<NestExpressApplication>(
AppModule,
new ExpressAdapter(expressApp),
{
logger: createLogger(),
Provider Winston Module
nestjs-winston
으로 winston logger를 만들었으니, AppModule에 Provider로 제공해줍니다.
@Module({
providers: [
Logger,
결과
이제 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 로 로그를 볼 수 있다.
Posted on August 18, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
November 14, 2024