LogTape 0.7.0에서 도입된 암묵적 컨텍스트 소개

홍민희·2024년 10월 29일
1

오늘 LogTape 0.7.0가 릴리스 되었는데요, 이번 새 버전에서는 애플리케이션 전체의 로그에 컨텍스트 정보를 쉽게 추가할 수 있는 강력한 새 기능인 암묵적 컨텍스트가 도입되었습니다.

암묵적 컨텍스트(implict contexts)란?

예를 들어, 애플리케이션에서 HTTP 요청을 처리할 때 요청 처리 중에 생성되는 모든 로그 메시지에 요청 ID를 포함하고 싶다고 가정해 보겠습니다. 코드베이스의 어디에서 로그가 생성되든 상관 없이 이를 포함하고 싶은 경우입니다.

암묵적 컨텍스트 이전에는 다음과 같은 방법이 필요했습니다:

  1. 모든 함수 호출에서 요청 ID를 인자로 전달
  2. 각 모듈에서 컨텍스트가 포함된 새로운 로거를 생성
  3. 또는 전역 변수 사용 (이 방법은 여러모로 문제가 있습니다)

암묵적 컨텍스트를 사용하면 요청 핸들러의 시작 시점에서 컨텍스트를 설정하기만 하면, 해당 실행 컨텍스트 내의 모든 로그 메시지에 자동으로 그 정보가 포함됩니다. 다음은 간단한 예시입니다:

function handleRequest(requestId: string) {
  withContext({ requestId }, () => {
    // 이 함수 내부나 이 함수가 호출하는 모든 함수 내의 로그 메시지에
    // 자동으로 requestId가 포함됩니다
    processRequest();
  });
}

function processRequest() {
  // requestId를 명시적으로 전달할 필요가 없습니다
  getLogger("processor").info(
    "요청 처리 중: {requestId}"
  );
}

어떻게 작동하나요?

암묵적 컨텍스트는 런타임 환경의 컨텍스트 로컬 스토리지 메커니즘(Node.js의 AsyncLocalStorage 등)을 사용하여 코드 실행 전체에 걸쳐 컨텍스트 정보를 유지합니다. 이를 통해 비동기 작업을 걸쳐도 적절하게 컨텍스트가 유지됩니다.

애플리케이션에서 암묵적 컨텍스트를 활성화하려면 LogTape을 컨텍스트 로컬 스토리지로 설정해야 합니다:

import { AsyncLocalStorage } from "node:async_hooks";
import { configure } from "@logtape/logtape";

await configure({
  // ... 기타 설정 ...
  contextLocalStorage: new AsyncLocalStorage(),
});

중첩된 컨텍스트와 우선순위

암묵적 컨텍스트의 강력한 기능 중 하나는 중첩이 가능하다는 것입니다. 컨텍스트를 중첩하면 내부 컨텍스트가 외부 컨텍스트에서 값을 상속 받고, 필요한 경우 덮어쓸 수 있습니다:

function handleRequest(requestId: string) {
  withContext({ requestId, stage: "request" }, () => {
    // 여기서 stage는 "request"입니다
    processUser(1234);
  });
}

function processUser(userId: number) {
  withContext({ userId, stage: "user" }, () => {
    // 여기서 stage는 "user"이지만, requestId는 계속 사용 가능합니다
    getLogger("processor").info(
      "사용자 {userId} 처리 중, 요청 ID: {requestId}"
    );
  });
}

컨텍스트 값 해결에는 다음과 같은 명확한 우선순위가 있습니다:

  1. 로그 메시지 내의 명시적 속성이 가장 우선됨
  2. Logger.with()로 설정된 명시적 컨텍스트가 다음으로 우선됨
  3. withContext()로 설정된 암묵적 컨텍스트가 가장 낮은 우선순위

런타임 지원

2024년 10월 현재, 암묵적 컨텍스트는 다음 환경에서 지원됩니다:

  • Node.js
  • Deno
  • Bun

웹 브라우저는 TC39 Async Context 제안의 구현을 기다리고 있어 아직 암묵적 컨텍스트를 지원하지 않습니다.

사용 사례

암묵적 컨텍스트는 다음과 같은 상황에서 특히 가치를 발휘합니다:

  1. 요청 추적: 요청 ID, 사용자 ID, 세션 ID를 요청 내의 모든 로그에 추가
  2. 트랜잭션 모니터링: 여러 작업에 걸쳐 트랜잭션 ID 추적
  3. 에러 컨텍스트: 에러 로그에 항상 관련 컨텍스트 정보가 포함되도록 보장
  4. 성능 모니터링: 여러 작업에 걸친 타이밍 정보 추가
  5. 테넌트 컨텍스트: 멀티테넌트 애플리케이션에서의 테넌트 정보 추적

모범 사례

암묵적 컨텍스트를 사용할 때는 다음과 같은 모범 사례를 고려하세요:

  1. 실행 컨텍스트 전체에 실제로 속하는 정보에 암묵적 컨텍스트를 사용
  2. 컨텍스트 데이터는 가볍게 유지—실행 전체에 걸쳐 유지된다는 점을 유념
  3. 애플리케이션 전체에서 의미 있고 일관된 키 이름 사용
  4. TypeScript를 사용하여 컨텍스트 구조의 일관성 보장 고려
  5. 애플리케이션에서 기대되는 컨텍스트 구조 문서화

마이그레이션 가이드

이미 LogTape을 사용 중이라면, 암묵적 컨텍스트를 사용하도록 업그레이드하는 것은 간단합니다:

  1. LogTape 0.7.0으로 업데이트
  2. LogTape 설정에 컨텍스트 로컬 스토리지 추가
  3. 컨텍스트가 수동으로 전달되는 위치 식별
  4. 적절한 경계에서 withContext() 호출로 교체

결론

LogTape 0.7.0의 암묵적 컨텍스트는 코드를 복잡하게 만들거나 콜 스택을 통해 컨텍스트를 수동으로 전달할 필요 없이, 로그에 컨텍스트 정보를 추가하는 강력한 방법을 제공합니다. 이는 특히 웹 서비스, API, 그리고 작업 간 컨텍스트 추적이 중요한 기타 애플리케이션에서 가치를 발휘합니다.

이 기능을 사용하여 여러분의 애플리케이션의 로깅과 관측성을 어떻게 개선하실지 궁금합니다. 꼭 한번 시도해 보시고 피드백을 주세요!

더 자세한 정보는 암묵적 컨텍스트의 전체 문서(영어)를 참조하세요.

profile
서울에서 소프트웨어 프로그래머로 일하고 있습니다. 자유·오픈 소스 소프트웨어를 만들며, 주로 Haskell, Python, TypeScript 같은 언어로 코딩합니다.
post-custom-banner

0개의 댓글