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 로 로그를 볼 수 있다.

Top comments (0)