aboutsummaryrefslogtreecommitdiff
path: root/src/Core/Log.h
blob: ad1b959b6f0796ec960093be158a4018aa1b4825 (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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
#pragma once

#include <string>
#include <memory>
#include <vector>
#include <sstream>
#include <iostream>
#include <fstream>
#include <chrono>
#include <iomanip>
#include <mutex>
#include <string_view>

namespace Donut
{
    enum class LogLevel
    {
        TRACE = 0,
        INFO  = 2,
        WARN  = 3,
        ERR   = 4,
        FATAL = 5
    };

    class Logger 
    {
    public:
        static void Init();
        static void Shutdown();

        static std::shared_ptr<Logger> GetLogger();

        template<typename... Args>
        static void Trace(const std::string_view& format, const Args&... args) 
        {
            auto logger = GetLogger();
            if (logger) logger->LogMessage(LogLevel::TRACE, format, args...);
        }

        template<typename... Args>
        static void Info(const std::string_view& format, const Args&... args) 
        {
            auto logger = GetLogger();
            if (logger) logger->LogMessage(LogLevel::INFO, format, args...);
        }

        template<typename... Args>
        static void Warn(const std::string_view& format, const Args&... args) 
        {
            auto logger = GetLogger();
            if (logger) logger->LogMessage(LogLevel::WARN, format, args...);
        }

        template<typename... Args>
        static void Error(const std::string_view& format, const Args&... args) 
        {
            auto logger = GetLogger();
            if (logger) logger->LogMessage(LogLevel::ERR, format, args...);
        }

        template<typename... Args>
        static void Fatal(const std::string_view& format, const Args&... args) 
        {
            auto logger = GetLogger();
            if (logger) logger->LogMessage(LogLevel::FATAL, format, args...);
        }

        template<typename... Args>
        void LogMessage(LogLevel level, const std::string_view& format, const Args&... args) 
        {
            if (level < m_LogLevel) 
                return;

            std::lock_guard<std::mutex> lock(m_Mutex);
            std::string message = FormatString(format, args...);
            std::string fullMessage = GetTimeStamp() + " [" + GetLogLevelString(level) + "] " + message;
            
            if (m_ConsoleOutput) 
            {
                SetConsoleColor(level);
                std::cout << fullMessage << std::endl;
                ResetConsoleColor();
            }
            
            if (m_FileOutput && m_LogFile.is_open()) 
            {
                m_LogFile << fullMessage << std::endl;
                m_LogFile.flush();
            }
        }

        template<typename... Args>
        std::string FormatString(const std::string_view& format, const Args&... args)
        {
            std::string result = format.data();
            std::vector<std::string> argStrings = { ToString(args)... };
            
            size_t argIndex = 0;
            size_t pos = 0;
            
            while ((pos = result.find("{}", pos)) != std::string::npos && argIndex < argStrings.size())
            {
                result.replace(pos, 2, argStrings[argIndex]);
                pos += argStrings[argIndex].length();
                argIndex++;
            }
            
            return result;
        }

        template<typename T>
        std::string ToString(const T& value)
        {
            std::stringstream ss;
            ss << value;
            return ss.str();
        }

        void SetLogLevel(LogLevel level)      { m_LogLevel      = level;  }
        void EnableConsoleOutput(bool enable) { m_ConsoleOutput = enable; }
        void EnableFileOutput(bool enable)    { m_FileOutput    = enable; }
        void SetLogFile(const std::string& filename);
    public:
        Logger();
        ~Logger();

        std::string GetTimeStamp();
        std::string GetLogLevelString(LogLevel level);
        void SetConsoleColor(LogLevel level);
        void ResetConsoleColor();
    private:
        LogLevel      m_LogLevel;
        bool          m_ConsoleOutput;
        bool          m_FileOutput;
        std::ofstream m_LogFile;
        std::mutex    m_Mutex;

        static std::shared_ptr<Logger> s_Logger;
    };
}

#if defined(DONUT_DEBUG)
    #define DONUT_TRACE(format, ...)    ::Donut::Logger::Trace(format, ##__VA_ARGS__)
    #define DONUT_INFO(format, ...)     ::Donut::Logger::Info(format, ##__VA_ARGS__)
    #define DONUT_WARN(format, ...)     ::Donut::Logger::Warn(format, ##__VA_ARGS__)
    #define DONUT_ERROR(format, ...)    ::Donut::Logger::Error(format, ##__VA_ARGS__)
    #define DONUT_FATAL(format, ...)    ::Donut::Logger::Fatal(format, ##__VA_ARGS__)
#else
    #define DONUT_TRACE(format, ...)    {}
    #define DONUT_INFO(format, ...)     {}
    #define DONUT_WARN(format, ...)     {}
    #define DONUT_ERROR(format, ...)    {}
    #define DONUT_FATAL(format, ...)    {}
#endif