1 /*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "Logger.h"
17
18 #include <cstdarg>
19 #include <set>
20 #include <string>
21
22 namespace lume {
23 namespace {
24 constexpr int LOG_BUFFER_SIZE = 1024;
25
26 // Note: these must match the LogLevel enum.
27 constexpr int LOG_LEVEL_COUNT = 7;
28
29 constexpr const char* LOG_LEVEL_NAMES[LOG_LEVEL_COUNT] = {
30 "Verbose",
31 "Debug",
32 "Info",
33 "Warning",
34 "Error",
35 "Fatal",
36 "None",
37 };
38
39 constexpr const char* LOG_LEVEL_NAMES_SHORT[LOG_LEVEL_COUNT] = {
40 "V",
41 "D",
42 "I",
43 "W",
44 "E",
45 "F",
46 "N",
47 };
48 } // namespace
49
GetLogLevelName(LogLevel logLevel,bool shortName)50 const char* Logger::GetLogLevelName(LogLevel logLevel, bool shortName)
51 {
52 const int logLevelIndex = static_cast<int>(logLevel);
53 LUME_ASSERT(logLevelIndex >= 0 && logLevelIndex < LOG_LEVEL_COUNT);
54
55 return shortName ? LOG_LEVEL_NAMES_SHORT[logLevelIndex] : LOG_LEVEL_NAMES[logLevelIndex];
56 }
57
Logger(bool defaultOutputs)58 Logger::Logger(bool defaultOutputs)
59 {
60 if (defaultOutputs) {
61 AddOutput(CreateLoggerConsoleOutput());
62 AddOutput(CreateLoggerDebugOutput());
63
64 // Not writing to a file by default.
65 // This can be enabled with: addOutput(createLoggerFileOutput("./logfile.txt"))
66 }
67 }
68
69 Logger::~Logger() = default;
70
Vlog(LogLevel logLevel,const char * filename,int linenumber,const char * format,va_list args)71 void Logger::Vlog(LogLevel logLevel, const char* filename, int linenumber, const char* format, va_list args)
72 {
73 LUME_ASSERT_MSG(logLevel != LogLevel::NONE, "'NONE' is not a valid log level for writing to the log.");
74
75 if (mLogLevel > logLevel) {
76 return;
77 }
78
79 char buffer[LOG_BUFFER_SIZE];
80 #if defined(__STDC_LIB_EXT1__) || defined(__STDC_WANT_SECURE_LIB__)
81 auto ret = vsnprintf_s(buffer, sizeof(buffer), format, args);
82 if (ret < 0) {
83 return;
84 }
85 #else
86 auto ret = vsnprintf(buffer, sizeof(buffer), format, args);
87 if (ret < 0) {
88 return;
89 }
90 #endif
91
92 for (auto& output : mOutputs) {
93 output->Write(logLevel, filename, linenumber, buffer);
94 }
95 }
96
Write(LogLevel logLevel,const char * filename,int linenumber,const char * buffer)97 void Logger::Write(LogLevel logLevel, const char* filename, int linenumber, const char* buffer)
98 {
99 for (auto& output : mOutputs) {
100 output->Write(logLevel, filename, linenumber, buffer);
101 }
102 }
103
104 FORMAT_FUNC(5, 6)
Log(LogLevel logLevel,const char * filename,int linenumber,FORMAT_ATTRIBUTE const char * format,...)105 void Logger::Log(LogLevel logLevel, const char* filename, int linenumber, FORMAT_ATTRIBUTE const char* format, ...)
106 {
107 va_list vl;
108 va_start(vl, format);
109 Vlog(logLevel, filename, linenumber, format, vl);
110 va_end(vl);
111 }
112
113 FORMAT_FUNC(6, 7)
LogAssert(const char * filename,int linenumber,bool expression,const char * expressionString,FORMAT_ATTRIBUTE const char * format,...)114 bool Logger::LogAssert(const char* filename, int linenumber, bool expression, const char* expressionString,
115 FORMAT_ATTRIBUTE const char* format, ...)
116 {
117 if (expression) {
118 return true;
119 }
120
121 va_list vl;
122 va_start(vl, format);
123
124 char buffer[LOG_BUFFER_SIZE];
125 #if defined(__STDC_LIB_EXT1__) || defined(__STDC_WANT_SECURE_LIB__)
126 vsnprintf_s(buffer, sizeof(buffer), format, vl);
127 #else
128 vsnprintf(buffer, sizeof(buffer), format, vl);
129 #endif
130
131 va_end(vl);
132
133 Log(LogLevel::FATAL, filename, linenumber, "Assert failed (%s). %s", expressionString, buffer);
134 return false;
135 }
136
GetLogLevel() const137 ILogger::LogLevel Logger::GetLogLevel() const
138 {
139 return mLogLevel;
140 }
141
SetLogLevel(LogLevel logLevel)142 void Logger::SetLogLevel(LogLevel logLevel)
143 {
144 mLogLevel = logLevel;
145 }
146
AddOutput(std::unique_ptr<IOutput> output)147 void Logger::AddOutput(std::unique_ptr<IOutput> output)
148 {
149 if (output) {
150 std::lock_guard<std::mutex> guard(mLoggerMutex);
151 mOutputs.push_back(std::move(output));
152 }
153 }
154
155 namespace {
156 Logger g_loggerInstance(true); // Global logger instance.
157
158 std::set<std::string> g_registeredOnce; // Global set of ids used by the LUME_ONCE macro.
159 std::mutex g_onceMutex;
160 } // namespace
161
GetLogger()162 ILogger& GetLogger()
163 {
164 return g_loggerInstance;
165 }
166
CheckOnce(const char * aId)167 bool CheckOnce(const char* aId)
168 {
169 std::lock_guard<std::mutex> guard(g_onceMutex);
170
171 size_t size = g_registeredOnce.size();
172 g_registeredOnce.insert(std::string(aId));
173
174 // Something was inserted if the size changed.
175 return size != g_registeredOnce.size();
176 }
177
CheckOnceReset()178 void CheckOnceReset()
179 {
180 std::lock_guard<std::mutex> guard(g_onceMutex);
181 g_registeredOnce.clear();
182 }
183 } // namespace lume
184