fmt 라이브러리로 C++ 콘솔 로거 작성하기

프로그램의 로그를 쓰기 위해 보통 Log4j, spdlog… 등 로거 프레임워크를 사용합니다. 사용하기 간편하며 강력한 기능을 제공합니다.

하지만 저는 fmt를 이용하여 간단히 사용할 수 있는 콘솔 로거를 작성해보려고 합니다. 😎

쉽고 간단한 길 놔두고 이상한 길 가기

⚠️ Warning

상용 프로그램에서 아래와 같이 로그를 사용한다면 스레드로 인한 작동 오류가 있을 수 있습니다.

fmt를 사용해보기 위해 간단히 작성된 것이므로 로거 프레임워크를 사용하기를 권장합니다.

fmt

💡 {fmt} is an open-source formatting library providing a fast and safe alternative to C stdio and C++ iostreams.

fmtC 언어의 printf()함수의 %d와 같이 문자열에 특정 포맷을 잡는 라이브러리입니다. 하지만 기존의 포맷과 다르게 사용이 쉽고 차원이 다른 기능을 제공하며 C++20에서 표준으로 채택되었다는 점이 큰 장점입니다!

더 자세한 내용을 보고 싶으시면 이 곳을 방문하여 확인해보세요.

Dependencies

fmtGithub에서 소스를 받아 직접 빌드(cmake)하거나 리눅스를 사용한다면 패키지 관리 프로그램(apt, yum, …)으로 쉽게 설치할 수 있습니다.

저는 리눅스를 사용하고 패키지 관리 프로그램을 통해 설치했습니다.

1
2
3
4
5
6
❯ dnf install fmt-devel
마지막 메타자료 만료확인 1:27:40 이전인: 2023년 05월 27일 () 오전 11시 22분 54초.
꾸러미 fmt-devel-9.1.0-2.fc38.x86_64가 이미 설치되어 있습니다.
종속성이 해결되었습니다.
처리가 필요하지 않습니다.
완료되었습니다!

Implement 🏗️

💡 Tips

git clone https://github.com/JongBin-Park/simple-logger-cpp.git

make를 이용하여 빠르게 결과물을 볼 수 있습니다.

logger.h

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#ifndef __LOGGER_H__
#define __LOGGER_H__

#include <iostream>
#include <fmt/color.h>
#include <fmt/chrono.h>

typedef enum _LogLevel
{
    LOG_LEVEL_OFF=0,
    LOG_LEVEL_DEBUG,
    LOG_LEVEL_INFO,
    LOG_LEVEL_WARN,
    LOG_LEVEL_ERROR,
} LogLevel;

class Logger
{
private:
    LogLevel m_level;

    std::time_t getNow();

public:
    Logger(LogLevel level = LOG_LEVEL_INFO) : m_level(level) {}
    ~Logger() {}

    void setLevel(LogLevel level) { m_level = level; }

    template<typename... Args>
    void writeLog(LogLevel level, std::string str, Args... args)
    {
        if(m_level <= level)
        {
            std::string content = "";
            std::chrono::time_point<std::chrono::system_clock> t = std::chrono::system_clock::now();

            switch(level)
            {
                case LOG_LEVEL_DEBUG: 
                    content = fmt::format(fg(fmt::color::sky_blue), "[{:%Y-%m-%d %H:%M:%S}] [D] " + str, t, args...);
                    break;
                case LOG_LEVEL_INFO: 
                    content = fmt::format(fg(fmt::color::lime_green), "[{:%Y-%m-%d %H:%M:%S}] [I] " + str, t, args...);
                    break;
                case LOG_LEVEL_WARN: 
                    content = fmt::format(fg(fmt::color::green_yellow), "[{:%Y-%m-%d %H:%M:%S}] [W] " + str, t, args...);
                    break;
                case LOG_LEVEL_ERROR: 
                    content = fmt::format(fg(fmt::color::indian_red), "[{:%Y-%m-%d %H:%M:%S}] [E] " + str, t, args...);
                    break;
                case LOG_LEVEL_OFF:
                    return;
            }

            std::cout << content << std::endl;
        }
    }

    template<typename... Args>
    void debug(std::string str, Args... args)
    {
        writeLog(LOG_LEVEL_DEBUG, str, args...);
    }
    template<typename... Args>
    void info(std::string str, Args... args)
    {
        writeLog(LOG_LEVEL_INFO, str, args...);
    }
    template<typename... Args>
    void warn(std::string str, Args... args)
    {
        writeLog(LOG_LEVEL_WARN, str, args...);
    }
    template<typename... Args>
    void error(std::string str, Args... args)
    {
        writeLog(LOG_LEVEL_ERROR, str, args...);
    }
};

#endif /* __LOGGER_H__ */
  • 5-6: fmt 라이브러리를 사용하기 위해 헤더 파일을 포함합니다. fmt/color.h 헤더를 이용하여 로그 레벨을 색깔별로 설정할 수 있습니다.
  • 8: 상황에 따라 달리 사용할 수 있게 로그 레벨을 정의합니다.
  • 28: 로그 정보를 제한할 때 사용됩니다.
  • 30: 실질적으로 로그를 콘솔에 출력합니다. 또한 다수의 여러 자료형을 출력하기 위해 템플릿 가변 인자를 사용합니다.
  • 60-79: 레벨별 로그 출력 함수입니다.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#include "logger.h"

int main()
{
    Logger logger;

    logger.debug("do not display current level");
    logger.info("success! {}", 12.345);
    logger.warn("you will have some problem maybe...");
    logger.error("What the...");
    return 0;
}
  • 7: 로그 레벨의 default값이 LOG_LEVEL_INFO이기 때문에 해당 라인의 debug()함수는 실행이 되지 않습니다.
  • 8: fmt 포맷팅으로 12.345라는 값을 문자열로 출력합니다.

Test 🧪

logger-test.png