브라우저에서 사용할 간단한 로거 만들기

브라우저에서 사용할 간단한 로거 만들기

로깅

개발 단계에서든 프로덕션 단계에서든 로깅은 필수다. 각 단계마다 로그에 포함되는 정보의 차이는 있을지라도 로깅 기능이 없는 프로그램은 개인적으로 제대로 된 프로그램이 아니라고 생각한다.

img
디스코드의 로그

개발 단계에서는 더 상세한 로그를 작성하게 하여 번거롭게 브레이크포인트를 걸지 않고서 간단한 로직들이 제대로 돌아가는 중인지 확인할 수 있고, 프로덕션 단계에서는 문제가 발생할 경우 개발자가 사용자의 로그를 보고 어떤 문제가 어떻게 발생한 것인지 분석하는 데 사용할 수 있다.

로깅 라이브러리

이러한 로깅의 중요성 덕분에 언어를 막론하고 다양한 로깅 라이브러리가 꾸준히 업데이트되고 있다. C#에서는 NLog를, Java에서는 log4j를 사용했었는데, 이제는 NodeJS용 라이브러리를 찾아야만 했다.

검색하니 나오는 것들이 몇 개 있었는데, 그 중 Winston 라이브러리가 좋아 보여서 사용하려 했다. 그러나 실행해보니 process is undefined 관련 예외를 쓰로우하면서 정상적으로 작동하지 않았다.
설마 하는 마음에 이슈를 뒤져보니 윈스턴은 공식적으로 브라우저 로깅을 지원하지 않는다고 한다.

굳이 라이브러리를 써야 하나?

그러던 중 문득 의문이 들었다. 어차피 지금 하려는 작업은 로그를 DB에 전송하는 등의 복잡한 단계가 필요 없는 작업이다. 그냥 브라우저 콘솔에 디스코드마냥 로그를 출력해주면 되는 일이다. 단, 확장 프로그램에서 사용하는 로깅이기 때문에 콘텐츠 스크립트에서 로그를 작성할 경우 사이트 자체의 로그와 겹치지 않게 적절한 레이블을 출력해주면 된다.

이런 간단한 로거를 사용하자고 라이브러리를 추가해 사용하는 것은 낭비라는 생각이 뒤늦게 들었다.

직접 구현

그리고 나서 생각해 보니 구현에 시간이 그렇게 많이 들 것 같지 않았다. 예전이었다면 패키지를 찾기 전에 구현할 생각부터 했을 텐데 시간에 쫓기다 보니 패키지를 먼저 찾고 뒤늦게서야 직접 짜는 것이 더 빠를 것 같다는 생각을 하게 된 것이다.

아무튼, 필요한 것은 다음과 같다.

  1. 타임스탬프 출력
  2. 레이블 출력
  3. console.error, warn, info, debug, trace에 맞게 출력하는 기능
  4. 오브젝트는 JSON.stringify를 사용하지 않고 console의 출력 기능을 사용
  5. 각 로그 메서드마다 다른 색상으로 강조
  6. 레벨을 나누어 유연하게 특정 레벨까지의 로그만 출력되도록 설정 (개발/배포 환경 고려)

음. 다시 봐도 별 것 없다. 따라서 당연히 완성된 코드도 별 게 없었다. 작성하는 데 5분 정도 걸린 것 같다.

아래는 구현한 부분이다.

import moment from "moment";

type SupportedLogMethod = "error" | "warn" | "info" | "debug" | "trace";

const logger = {
    level: 5,
    levels: {
        error: 1,
        warn: 2,
        info: 3,
        debug: 4,
        trace: 5,
    },
    colors: {
        error: "#F21B2D",
        warn: "#f2a20c",
        info: "#1B9AF2",
        debug: "#b1ffc8",
        trace: "#00fbf3",
    } as {
        [key in SupportedLogMethod]: string;
    },
    methods: {
        error: "ERROR",
        warn: "WARN",
        info: "INFO",
        debug: "DEBUG",
        trace: "TRACE",
    },
    label: "WDFlexCrawler",
    log: (method: SupportedLogMethod, ...message: any[]) => {
        const color = logger.colors[method];
        const methodString = logger.methods[method];

        const isLevelEnabled = logger.levels[method] <= logger.level;

        if (!isLevelEnabled) {
            return;
        }

        const timeStr = moment().format("HH:mm:ss.SSS");

        const colorLabel = "#c100ff";

        const formattedMessage = `${timeStr}${logger.label ? ` %c${logger.label}` : ""} %c[${methodString}]`;

        if (!logger.label) {
            console[method](formattedMessage, `color: ${color}; font-weight: bold;`, ...message);
        } else {
            console[method](
                formattedMessage,
                `color: ${colorLabel}; font-weight: bold;`,
                `color: ${color}; font-weight: bold;`,
                ...message,
            );
        }
    },
    error: (...message: any[]) => logger.log("error", ...message),
    warn: (...message: any[]) => logger.log("warn", ...message),
    info: (...message: any[]) => logger.log("info", ...message),
    debug: (...message: any[]) => logger.log("debug", ...message),
    trace: (...message: any[]) => logger.log("trace", ...message),
};

export default logger;

사용은 다음과 같이 하면 된다.

import logger from "@lib/logging";

browser.runtime.onInstalled.addListener(() => {
    logger.trace("Extension installed!");
});
img
출력된 로그

댓글

이 블로그의 인기 게시물

C# 남아도는 메모리에도 불구하고 OutOfMemoryException이 발생한다면?

MySQL 데이터 타입과 Java 데이터 타입 비교/매칭

테일즈위버 OST 전곡 모음