aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/debug/Log.hpp
blob: a43d6feec023ddab4757c5a4204a62baa05bab64 (plain)
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
83
84
85
86
87
88
89
90
91
#pragma once
#include <string>
#include <format>
#include <iostream>
#include <fstream>
#include <chrono>
#include "../includes.hpp"
#include "../helpers/MiscFunctions.hpp"

#define LOGMESSAGESIZE   1024
#define ROLLING_LOG_SIZE 4096

enum LogLevel {
    NONE = -1,
    LOG  = 0,
    WARN,
    ERR,
    CRIT,
    INFO,
    TRACE
};

namespace Debug {
    inline std::string     logFile;
    inline int64_t* const* disableLogs   = nullptr;
    inline int64_t* const* disableTime   = nullptr;
    inline bool            disableStdout = false;
    inline bool            trace         = false;
    inline bool            shuttingDown  = false;

    inline std::string     rollingLog = ""; // rolling log contains the ROLLING_LOG_SIZE tail of the log

    void                   init(const std::string& IS);
    template <typename... Args>
    void log(LogLevel level, std::format_string<Args...> fmt, Args&&... args) {
        if (level == TRACE && !trace)
            return;

        if (shuttingDown)
            return;

        std::string logMsg = "";

        switch (level) {
            case LOG: logMsg += "[LOG] "; break;
            case WARN: logMsg += "[WARN] "; break;
            case ERR: logMsg += "[ERR] "; break;
            case CRIT: logMsg += "[CRITICAL] "; break;
            case INFO: logMsg += "[INFO] "; break;
            case TRACE: logMsg += "[TRACE] "; break;
            default: break;
        }

        // print date and time to the ofs
        if (disableTime && !**disableTime) {
#ifndef _LIBCPP_VERSION
            logMsg += std::format("[{:%T}] ", std::chrono::hh_mm_ss{std::chrono::system_clock::now() - std::chrono::floor<std::chrono::days>(std::chrono::system_clock::now())});
#else
            auto c = std::chrono::hh_mm_ss{std::chrono::system_clock::now() - std::chrono::floor<std::chrono::days>(std::chrono::system_clock::now())};
            logMsg += std::format("{:%H}:{:%M}:{:%S}", c.hours(), c.minutes(), c.subseconds());

#endif
        }

        // no need for try {} catch {} because std::format_string<Args...> ensures that vformat never throw std::format_error
        // because
        // 1. any faulty format specifier that sucks will cause a compilation error.
        // 2. and `std::bad_alloc` is catastrophic, (Almost any operation in stdlib could throw this.)
        // 3. this is actually what std::format in stdlib does
        logMsg += std::vformat(fmt.get(), std::make_format_args(args...));

        rollingLog += logMsg + "\n";
        if (rollingLog.size() > ROLLING_LOG_SIZE)
            rollingLog = rollingLog.substr(rollingLog.size() - ROLLING_LOG_SIZE);

        if (!disableLogs || !**disableLogs) {
            // log to a file
            std::ofstream ofs;
            ofs.open(logFile, std::ios::out | std::ios::app);
            ofs << logMsg << "\n";

            ofs.close();
        }

        // log it to the stdout too.
        if (!disableStdout)
            std::cout << logMsg << "\n";
    }

    void wlrLog(wlr_log_importance level, const char* fmt, va_list args);
};