이전글과 이어집니다.
프로젝트의 빌드 프로세스 변경
우선, 프로젝트 배포 시 프로젝트 내부에서 docker-compose를 이용해서, 로키와 프롬테일 모두 빌드시켜주고 있는 기존 프로세스가 잘못됐다고 판단했다. 관리가 너무 불편했다.
프롬테일 재배포해야할 때, 로키만 재배포해야할 때, 간단하게 config파일만 수정하면 되는데 내부에 config가 있기 때문에, runner를 다시 동작하게 푸시를 해줘야했다.
그리고 액션파일에서, 각 상황에 맞게 분기처리마저 해주어야 하기 때문에, 생산성이 떨어진다고 판단했다.
그리하여 서버 내부에 독립적으로 프롬테일, 로키 컨테이너를 생성하게 변경했다.
간단한 테스트 구성
내가 담당하는 서비스는 크게 두가지로 분류된다.
거의 모든 로그를 커스텀화하여 받아봐야하는 서비스
사용자의 동작 중 일부분만 로그를 남기면 되는 서비스
전자의 서비스는, 미들웨어를 통해 중앙집중식으로 관리할 계획이고
후자의 서비스는 특정 서비스 로직의 마지막 지점에만 커스텀 로그를 작성할 수 있는 함수를 구현하여 finally로 남길 것이다.
(실패 시 API 실패 로그와 함께, 특정 로그는 무조건적으로 같은 로그로 남아야 하기 때문에 finally에 작성하는 것이 좋다고 생각했다.)
전자의 경우로 로그 테스트를 위해 개발서버에, 로깅 미들웨어를 적용했다.
기본 베이스는 아래와 같고, 상황에 맞게 커스텀해서 사용하면 된다.
// log.middleware.ts
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
import { WinstonModule } from 'nest-winston';
import * as winston from 'winston';
import { LokiDto } from './loki.dto';
@Injectable()
export class LogMiddleware implements NestMiddleware {
private readonly logger = WinstonModule.createLogger({
transports: [
new winston.transports.Console(),
],
});
use(req: Request, res: Response, next: NextFunction): void {
const { userId, type, when } = req.body;
const data = new LokiDto().build(userId, type, when);
this.logger.log('info', 'Request received', data);
res.on('finish', () => {
this.logger.log('info', 'Response sent', {
...data,
});
});
next();
}
}
//app.module.ts
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { LogMiddleware } from './log.middleware';
@Module()
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(LogMiddleware)
.forRoutes('*');
}
}
그라파나에서 로그 확인
얼추 해당 패널에서, 생성한 커스텀 로그들을 확인할 수 있었다.
개선해야할 부분
LogQL을 사용한 조회
단순히 JSON형태로 로그를 작성하면 LogQL으로 조회도 쉽게 가능할 줄 알았더니 애로사항이 있었다.
해당 유저만 조회하려면?
해당 타입만 조회하려면?
의 이슈가 있다고 생각했다.
{job="info"} |= `userId` |= `type`
위의 사진의 조회를 위한 logQL이다.
유저 아이디가 TEST인 유저를 조회하려면
{job="info"} |= `"userId":"TEST"` |= `type`
이렇게 사용하면 되기야 하겠지만, 단순 Line Contains가 딱 ID, TYPE으로 떨어지게끔 해야 다른 사용자들도 편하게 사용할 수 있지 않을까. 이미 구현되어있지만 내가 못찾은걸까? 나에게 남은 숙제인 것 같다.
쿼리 시간범위 제한의 초과
Status: 500. Message: the query time range exceeds the limit (query length: 2160h0m0.001s, limit: 30d1h)
위의 에러메세지는, 내가 90일 이상의 로그를 조회하려고 했을 때 나타난 에러이다.
현재 서비스들에 필요한 로그는 거의 수 년 단위의 로그를 필요로 할 것으로 예상되는데, 이를 해결할 수 있는지에 대한 조사가 필요할 것 같다. 근본적으로 이걸 해결하지 못한다면, 굳이 서비스에 적용시킬 이유가 없다고 본다.
DB에 로깅을 하고, 이를 조회할 수 있는 방법 혹은, 그마저도 안된다면 뷰페이지를 만드는 것이 더 이득일 수도 있을 것 같다.
마무리
현재까지 적용된 두 서비스 중 전자의 서비스의 테스트 대시보드이다.
필요에 따라, 필요한 부분을 LogQL로 조회하는 패널과, 해당 서비스의 트래픽이 몰리는 시간대를 파악할 수 있는 패널을 구성해보았다.
다음 포스팅에선, 개선사항들을 개선하여 반영해보도록 하겠다.
트러블 슈팅
Verify that Loki and Promtails is configured properly
참고
2023.04 ~ 백엔드 개발자의 기록
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!