웹 개발에서 사용하기 쉬운 로그 시작

34070 단어 logtech
이런 일지를 쓴 사람을 대상으로
{
    "message": "HogeController.index(). finding hoge...",
    "level": "info"
}
{
    "message": "HogeService.findListById(). finding hoge...",
    "level": "info"
}
{
    "message": "HogeRepository.findListById(). finding hoge...",
    "level": "info"
}
{
    "message": "HogeRepository.findListById(). found Hoge!",
    "level": "info"
}
{
    "message": "HogeService.findListById(). found Hoge!",
    "level": "info"
}
{
    "message": "HogeController.index(). found Hoge!",
    "level": "info"
}
참고로 문장info에 기록된 것은 동작마다 쓰여졌지만 프레임 등의 설정과 해체 처리는 별개의 것이다.도대체 응용 프로그램 내부의 흐름도라는 거야.

나쁜 예로부터


우선, 저의 이전 일지를 보세요.참고로 응용의 구조는 Conroller-Service-Repository(DB 접근층)의 관계다.Rails와 Laavel 등의 사용자는 Repository를 Active Record 및 Eloquent로 교체하십시오.
샘플 코드는 Type Sciept로 작성됩니다.이것은 특정 프레임워크의 실제 코드가 아니라 그림으로 쓴 것이므로 주의하십시오.
먼저 Controller가 사용됩니다.처리 시작 전과 종료 후 각각 info등급의 로그를 썼다.왜 처리 전후에 로그info를 썼는지는 오류가 있을 때 어떤 처리를 했는지 알고 싶어서입니다.
오류가 있으면 error 수평 로그를 쓰고 프레임워크 오류 처리 프로그램에 넘기십시오.실제로 프레임워크의 오류 처리 프로그램은 오류를 받은 후에도 오류 로그를 직접 쓴다.왜 일부러 오류 로그를 다시 한 번 써야 합니까? 왜냐하면 메타 정보가 어느 컨트롤러에서 발생한 오류인지 알고 싶기 때문입니다.
HogeControllerr
class HogeController {
  async index(request) {
    // 処理が始まる前にログ。
    logger.info('HogeController.index call!');
    
    // TypeScriptの書き方ではないがイメージ。例外があればcatch。
    try {
      const hoges = await HogeService.getAllHoge(request.user);
      
      // 処理が終わったらログ。
      logger.info('HogeController.index called!');
      return hoges;
      
    // geAtAllHogeがエラーを投げたら
    } catch (error) {
      // どのコントローラーかエラーログ書いてた。
      // userIdつけてるときもあれば、つけてないときもあった。
      logger.error(`Error in HogeContoller.index(). userId: ${request.userId}, error: ${error}`);
      
      // もう一度エラー投げて、フレームワークにエラーをハンドリングしてもらう。expressとかだったら next(error)。
      throw error;
    }
  }
}
다음은 서비스입니다.여기도 Controller와 마찬가지로 처리 전후에 로그를 썼습니다.그렇다면 복제 과정에서 기록된 내용도 부정확하다.
HogeService
class HogeService {
  async getAllHoge(requestUser){
    // 処理が始まるよというログ。
    logger.info('HogeService.getAllHoge. finding hoge... ');
    
    // なんかの条件チェック失敗したら例外投げる。
    if(!requestUser.canGetHoge()){
      // 一応エラー発生した場所もエラーログ書いておく。
      logger.error('requestUser.canGetHoge() 権限エラー')
      throw new Error('権限エラー');
    }
    
    const models = await HogeRepository.findAllHoges();
     
    // 処理が成功したよというログ。 
    // 修正の過程でメソッド名が変わったのにログの内容は修正忘れてる。
    logger.info('HogeService.getHoges found hoge!');
    
    return models;
  }
}
다음은 같은 일을 반복하는 만큼 생략하는 리포지토리를 계속하고 싶다.마찬가지로 처리 전후info등급의 로그를 작성하고, 오류가 있으면 그 자리error등급의 로그를 작성하면서 오류를 반복적으로 던진다.
또 로그 서비스는 info등급 이상의 로그만 기록했다.

어느 날 자신의 일지를 사용하기 어려웠다는 걸 알게 됐어요.


어느 날, 손님은 어떤 기능이 잘 활용되지 않는다고 말했다.고객의 id를 바탕으로 해당하는 로그 서비스를 보고 싶습니다
{
    "message": "HogeController.findListById(). finding hoge...",
    "level": "info"
}
{
    "message": "HogeService.findListById(). finding hoge...",
    "level": "info"
}
{
    "message": "HogeRepository.findListById(). finding hoge...",
    "level": "info"
}
{
    "message": "HogeRepository.findListById(). found Hoge!",
    "level": "info"
}
{
    "message": "HogeService.findListById(). found Hoge!",
    "level": "info"
}
{
    "message": "HogeRepository.findListById(). found Hoge!",
    "level": "info"
}
{
    "message": "HogeController.index(). found Hoge!",
    "level": "info"
}
...中略...
{
    "message": "HogeRepository.create(). found Hoge!",
    "level": "info"
}
{
    "message": "接続エラー",
    "level": "error"
}
{
    "message": "Error in HogeController.index(). userId: 1,  error: DBエラー",
    "level": "error"
}
{
    "message": "ここにスタックトレース",
    "level": "error"
}
이게 뭐야!?
학과를 취득한 일지도 많고 누가 그랬는지 모르는 학과의 일지도 있고 묻힌 잘못된 일지도 있다.데이터의 9할은 쓰레기다.그리고 어떻게 하면 useerId를 검색할 수 있을까요?
왜 사용할 수 없는 일지가 생겼을까요?이유를 총결하였다.
  • info 로그인 줄 알았던 것은 info 로그 등급이었다.
  • Controller, Service 등의 계층은 기록하지 않습니다.각 동작debug을 로그로 기록합니다.
  • info 오류마다 로그가 있습니다.원인을 조사하고 원 정보를 주기 쉽다.
  • 사용자가 이전에 어떤 행동을 취했는지 검색할 수 있도록 로그에userId를 추가해야 합니다.
  • 메시지 메시지에 메타데이터를 삽입하지 않습니다.제이슨의 키와 가치 기록으로 잘 검색할 수 있도록
  • 반성에 기초하다


    내가 먼저 로거를 주문했어.두 번째 파라미터에 들어간 Object는 그렇게 json으로 처리할 수 있습니다.
    사용자 정의 로거
    logger.info('message here', {
      userId: 1,
      requestId: 'gjeaijgeiajke'
    })
    
    출력
    {
      "level": "info",
      "message": "message here",
      "userId": 1,
      "requestId": "gjeaijgeiajke"
    }
    
    그런 다음 Controller 를 변경합니다.모든 error 로그를 info 로 변경하고, 오류는 프레임워크 오류 처리 프로그램의 로그에 넘깁니다.그러나 오류를 직접 기록해도 소용이 없기 때문에 메타정보를 준다.여기에 관해서는 잠시 후에 다시 이야기하자.
    HogeControllerr
    class HogeController {
      async index(request) {
        // 処理が始まる前にログ。
        logger.debug('HogeController.index call!');
        
        // TypeScriptの書き方ではないがイメージ。例外があればcatch。
        try {
          const hoges = await HogeService.getAllHoge(request.user);
          
          // 処理が終わったらログ。
          logger.debug('HogeController.index called!');
          return hoges;
          
        // geAtAllHogeがエラーを投げたら
        } catch (error) {
        // もう一度投げるならtry-catchで囲む必要ないけど、構造を前セクションと合わせたかった。ログはフレームワークのエラーハンドラーに任せる。
          throw error;
        }
      }
    }
    
    다음은 서비스 개편.이쪽도 debug를 모두 info로 개작했다.또 잘못 보고할 때 일지를 쓰는 데 시간을 낭비하지 말아야 한다.오류도 프레임워크에서 제공하는 클래스를 사용하기 위해 수정되었습니다. 오류 클래스를 직접 사용하지 않습니다.
    HogeService
    class HogeService {
      async getAllHoge(requestUser){
        logger.debug('HogeService.getAllHoge. finding hoge... ');
        
        // なんかの条件チェック失敗したら例外投げる。
        if(!requestUser.canGetHoge()){
          throw ForbiddenError('権限がありません。');
        }
        
        const models = await HogeRepository.findAllHoges();
        
        // アクションしたらinfoログをつける。
        logger.info('activity log', {
          userId: 1,
          eventType: 'view.hoge',
          timestamp: new Date().toISOString(),
        })
         
        logger.debug('HogeService.getHoges found hoge!'); 
        return models;
      }
    }
    
    마지막으로 오류 처리 프로그램의 수정입니다.내가 사용하는 프레임워크에서 프레임워크 전체의 척도를 포착하는 오류 처리 프로그램은 중간부품으로 이루어지고 오류 내용은 사용자 정의가 가능합니다.따라서 잘못된 내용에 대해 다음과 같은 내용을 부여했다.
  • 요청 URL
  • HTTP 방법
  • 요청주체
  • 조회 요청
  • 요청 매개 변수(URL의 일부)
  • 첫 번째 요청
  • 사용자의 id 요청
  • 응답 상태 코드
  • 타임스탬프
  • 오류 프로세서 사용자 정의 이미지
    export class LoggingExceptionFilter extends BaseExceptionFilter {
      catch(exception: Error, request) {
        this.logger.error(exception.message, {
          path: request.url,
          requestMethod: request.method,
          requestBody: request.body,
          requestQuery: request.query,
          requestParam: request.params,
          requestHeaders: request.headers,
          userId: (request.user as UserEntity | undefined)?.id ?? null,
          responseStatus: httpStatus,
          timestamp: new Date().toISOString(),
        });
      }
    }
    

    재생의 일지


    {
        "message": "activity log",
        "level": "info",
        "userId": 1,
        "timestamp": "2022-04-20 10:10:10",
        "eventType": "view.hoge"
    }
    {
        "message": "activity log",
        "level": "info",
        "userId": 2,
        "timestamp": "2022-04-21 10:10:10",
        "eventType": "create.hoge"
    }
    {
        "message": "DB接続エラー",
        "level": "error",
        "userId": 2,
        "timestamp": "2022-04-22 10:10:10",
        "path": "https://example.com/hoge",
        "requestMethod": "PUT",
        "requestBody": {
          "comment": "hogeよりfugaがいいと思う"
        },
        "requestQuery": "",
        "requestParam": "",
        "requestHeaders": {
          ...省略
        },
        "responseStatus": 500
    }
    
    JSON 형식이라면 클라우드 워치debug처럼 검색하면 필터는 그 사용자의 로그만 나온다.
    상태 코드의 내용에서 대체적으로 잘못된 위치를 파악할 수 있고, 잘못된 정보에서 잘못된 부분을 확인할 수 있다.또 기록 요청 바디 등을 통해 당시 상황을 이해할 수 있었다.
    이벤트타입이란 신중을 기하기 위해 로그 필터 기능 등으로 어떤 도량을 측정하려는 것이다.
    https://dev.classmethod.jp/articles/cloudwatch-logs-metric-filters-how-to-setup/
    하지만 클라우드워치와 같은 감시 도구를 이용해 비즈니스 메트릭을 측정하기에는 부적합한 것으로 밝혀져 지금은 잘 사용되지 않고 있다.
    https://zenn.dev/dove/articles/104a6320cef450
    좋아요.˙ω˙)ว ⁾⁾


    중간에 코드에 적힌 시간이 있지만 프론트 데스크톱에서 요구에 따라 추적 id를 주면 한 요청에 여러 개의 로그가 있고 이후 활동 구동 등으로 인해 추적 id가 필요할 때나 비트 데이터 분석을 할 때 id를 추적해야 할 때무슨 소용이야?
    프론트 데스크에서 팟캐스트 id를 추가해야 합니다.
    axios.get('/example.com', {
      headers: {'X-REQUEST-ID': nanoId()}
    });
    
    백엔드에서 미리 유지하고 로그에 기록합니다.(CORS를 사용한 라이센스를 잊지 마십시오.)

    최후


    나는 개인적으로 천천히 회의를 하고 있다.나는 조금씩 커지고 싶다.푸석푸석해요~
    https://side-projects.studio.site/

    좋은 웹페이지 즐겨찾기