1 /*
2 * Copyright (C) 2023 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
19 #include <iostream>
20 #include <string>
21 #include <sstream>
22 #include <fstream>
23 #include <cstdarg>
24 #include <ctime>
25 #include <iomanip>
26 #include <chrono>
27
28 #ifdef __ANDROID__
29 #include <android/log.h>
30 #endif
31
32 #if _WIN32
33 #include <windows.h>
34 #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
35 #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
36 #endif
37
38 #else
39 #include <unistd.h>
40 #endif
41
42
43 namespace lume
44 {
45
46 namespace
47 {
48
49 //Gets the filename part from the path.
getFilename(const std::string & aPath)50 std::string getFilename(const std::string &aPath)
51 {
52 for (int i = static_cast<int>(aPath.size()) - 1; i >= 0 ; --i)
53 {
54 unsigned int index = static_cast<size_t>(i);
55 if (aPath[index] == '\\' || aPath[index] == '/') {
56 return aPath.substr(index + 1);
57 }
58 }
59 return aPath;
60 }
61
62 } // empty namespace
63
64
65 #if !defined(__ANDROID__)
66
67 class StdOutput : public ILogger::IOutput
68 {
69 public:
70 enum class ColorCode
71 {
72 BLACK = 0,
73 RED,
74 GREEN,
75 YELLOW,
76 BLUE,
77 MAGENTA,
78 CYAN,
79 WHITE,
80 BLACK_BRIGHT,
81 RED_BRIGHT,
82 GREEN_BRIGHT,
83 YELLOW_BRIGHT,
84 BLUE_BRIGHT,
85 MAGENTA_BRIGHT,
86 CYAN_BRIGHT,
87 WHITE_BRIGHT,
88 RESET,
89 };
90
getColorString(ColorCode aColorCode)91 static const char* getColorString(ColorCode aColorCode)
92 {
93 // Note: these must match the ColorCode enum.
94 constexpr int COLOR_CODE_COUNT = 17;
95 constexpr const char* COLOR_CODES[COLOR_CODE_COUNT] =
96 {
97 "\x1B[30m", "\x1B[31m", "\x1B[32m", "\x1B[33m",
98 "\x1B[34m", "\x1B[35m", "\x1B[36m", "\x1B[37m",
99 "\x1B[30;1m", "\x1B[31;1m", "\x1B[32;1m", "\x1B[33;1m",
100 "\x1B[34;1m", "\x1B[35;1m", "\x1B[36;1m", "\x1B[37;1m",
101 "\x1B[0m",
102 };
103
104 int colorCode = static_cast<int>(aColorCode);
105 LUME_ASSERT(colorCode >= 0 && colorCode < COLOR_CODE_COUNT);
106 return COLOR_CODES[colorCode];
107 }
108
StdOutput()109 StdOutput() : mUseColor(false), mCurrentColorString(nullptr)
110 {
111 #if _WIN32
112 // Set console (for this program) to use utf-8.
113 SetConsoleOutputCP(65001);
114 #endif
115
116 // Try to figure out if this output stream supports colors.
117 #ifdef _WIN32
118 const HANDLE stdHandle = GetStdHandle(STD_OUTPUT_HANDLE);
119 if (stdHandle)
120 {
121 // Check if the output is being redirected.
122 DWORD handleMode;
123 if (GetConsoleMode(stdHandle, &handleMode) != 0)
124 {
125 // Try to enable the option needed that supports colors.
126 handleMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
127 SetConsoleMode(stdHandle, handleMode);
128
129 GetConsoleMode(stdHandle, &handleMode);
130 if ((handleMode & ENABLE_VIRTUAL_TERMINAL_PROCESSING) != 0)
131 {
132 mUseColor = true;
133 }
134 }
135 }
136 #else
137 if (isatty(fileno(stdout)))
138 {
139 // Using colors if the output is not being redirected.
140 mUseColor = true;
141 }
142 #endif
143 }
144
setColor(std::ostream & outputStream,ColorCode aColorCode)145 void setColor(std::ostream &outputStream, ColorCode aColorCode)
146 {
147 if (!mUseColor) {
148 return;
149 }
150
151 const char* colorString = getColorString(aColorCode);
152 if (colorString == mCurrentColorString)
153 {
154 return;
155 }
156
157 mCurrentColorString = colorString;
158 if (colorString)
159 {
160 outputStream << colorString;
161 }
162 }
163
write(ILogger::LogLevel aLogLevel,const char * aFilename,int aLinenumber,const char * aMessage)164 void write(ILogger::LogLevel aLogLevel, const char *aFilename, int aLinenumber, const char *aMessage) override
165 {
166 auto &outputStream = std::cout;
167
168 auto now = std::chrono::system_clock::now();
169 auto time = std::chrono::system_clock::to_time_t(now);
170 auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()) -
171 std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch());
172
173 outputStream << std::put_time(std::localtime(&time), "%H:%M:%S.") << std::setw(3) << std::left << ms.count() << " " << Logger::getLogLevelName(aLogLevel, true);
174
175 if (aFilename)
176 {
177 const std::string filenameLink = " (" + getFilename(aFilename) + ":" + std::to_string(aLinenumber) + ")";
178 outputStream << std::right << std::setw(30) << filenameLink;
179 }
180 outputStream << ": ";
181
182 if (aLogLevel >= ILogger::LogLevel::Error)
183 {
184 setColor(outputStream, ColorCode::RED);
185 }
186 else if (aLogLevel == ILogger::LogLevel::Warning)
187 {
188 setColor(outputStream, ColorCode::YELLOW);
189 }
190 else if (aLogLevel <= ILogger::LogLevel::Debug)
191 {
192 setColor(outputStream, ColorCode::BLACK_BRIGHT);
193 }
194 else
195 {
196 setColor(outputStream, ColorCode::RESET);
197 }
198
199 outputStream << aMessage;
200 setColor(outputStream, ColorCode::RESET);
201 outputStream << std::endl;
202 }
203
204 private:
205 bool mUseColor;
206 const char *mCurrentColorString;
207
208 };
209
210 #endif // !defined(__ANDROID__)
211
212
213 #if defined(_WIN32) && !defined(NDEBUG)
214 class WindowsDebugOutput : public ILogger::IOutput
215 {
216 public:
write(ILogger::LogLevel aLogLevel,const char * aFilename,int aLinenumber,const char * aMessage)217 void write(ILogger::LogLevel aLogLevel, const char *aFilename, int aLinenumber, const char *aMessage) override
218 {
219 std::stringstream outputStream;
220
221 if (aFilename)
222 {
223 outputStream << aFilename << "(" << aLinenumber << ") : ";
224 }
225 else
226 {
227 outputStream << "lume : ";
228 }
229
230 auto now = std::chrono::system_clock::now();
231 auto time = std::chrono::system_clock::to_time_t(now);
232 auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()) -
233 std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch());
234
235 outputStream << std::put_time(std::localtime(&time), "%D %H:%M:%S.") << ms.count() << " " << Logger::getLogLevelName(aLogLevel, true);
236 outputStream << ": " << aMessage;
237 outputStream << '\n';
238
239 // Convert from utf8 to windows wide unicode string.
240 std::string message = outputStream.str();
241 int wStringLength = ::MultiByteToWideChar(CP_UTF8, 0, message.c_str(), static_cast<int>(message.size()), nullptr, 0);
242 std::wstring wString(static_cast<size_t>(wStringLength), 0);
243 ::MultiByteToWideChar(CP_UTF8, 0, message.c_str(), static_cast<int>(message.size()), &wString[0], wStringLength);
244
245 ::OutputDebugStringW(wString.c_str());
246 }
247 };
248 #endif
249
250 class FileOutput : public ILogger::IOutput
251 {
252 public:
FileOutput(const std::string & aFilePath)253 explicit FileOutput(const std::string &aFilePath) : IOutput(), mOutputStream(aFilePath, std::ios::app) {}
254
255 ~FileOutput() override = default;
256
write(ILogger::LogLevel aLogLevel,const char * aFilename,int aLinenumber,const char * aMessage)257 void write(ILogger::LogLevel aLogLevel, const char *aFilename, int aLinenumber, const char *aMessage) override
258 {
259 if (mOutputStream.is_open())
260 {
261 auto &outputStream = mOutputStream;
262
263 auto now = std::chrono::system_clock::now();
264 auto time = std::chrono::system_clock::to_time_t(now);
265 auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()) -
266 std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch());
267
268 outputStream << std::put_time(std::localtime(&time), "%D %H:%M:%S.") << ms.count() << " " << Logger::getLogLevelName(aLogLevel, false);
269
270 if (aFilename)
271 {
272 outputStream << " (" << aFilename << ":" << aLinenumber << "): ";
273 }
274 else
275 {
276 outputStream << ": ";
277 }
278
279 outputStream << aMessage << std::endl;
280 }
281 }
282 private:
283 std::ofstream mOutputStream;
284 };
285
286 #if defined(__ANDROID__)
287 class LogcatOutput : public Logger::IOutput
288 {
289 public:
write(ILogger::LogLevel aLogLevel,const char * aFilename,int aLinenumber,const char * aMessage)290 void write(ILogger::LogLevel aLogLevel, const char *aFilename, int aLinenumber, const char *aMessage) override
291 {
292 int logPriority;
293 switch (aLogLevel)
294 {
295 case ILogger::LogLevel::Verbose:
296 logPriority = ANDROID_LOG_VERBOSE;
297 break;
298
299 case ILogger::LogLevel::Debug:
300 logPriority = ANDROID_LOG_DEBUG;
301 break;
302
303 case ILogger::LogLevel::Info:
304 logPriority = ANDROID_LOG_INFO;
305 break;
306
307 case ILogger::LogLevel::Warning:
308 logPriority = ANDROID_LOG_WARN;
309 break;
310
311 case ILogger::LogLevel::Error:
312 logPriority = ANDROID_LOG_ERROR;
313 break;
314
315 case ILogger::LogLevel::Fatal:
316 logPriority = ANDROID_LOG_FATAL;
317 break;
318
319 default:
320 logPriority = ANDROID_LOG_VERBOSE;
321 break;
322 }
323
324 if (aFilename)
325 {
326 std::stringstream outputStream;
327 outputStream << "(" << getFilename(aFilename) << ":" << aLinenumber << "): ";
328 outputStream << aMessage;
329 __android_log_write(logPriority, "lume", outputStream.str().c_str());
330 }
331 else
332 {
333 __android_log_write(logPriority, "lume", aMessage);
334 }
335 }
336 };
337 #endif
338
339
340
createLoggerConsoleOutput()341 std::unique_ptr<ILogger::IOutput> createLoggerConsoleOutput()
342 {
343 #ifdef __ANDROID__
344 return std::make_unique<LogcatOutput>();
345 #else
346 return std::make_unique<StdOutput>();
347 #endif
348 }
349
350
createLoggerDebugOutput()351 std::unique_ptr<ILogger::IOutput> createLoggerDebugOutput()
352 {
353 #if defined(_WIN32) && !defined(NDEBUG)
354 return std::make_unique<WindowsDebugOutput>();
355 #else
356 return std::unique_ptr<ILogger::IOutput>();
357 #endif
358 }
359
360
createLoggerFileOutput(const char * aFilename)361 std::unique_ptr<ILogger::IOutput> createLoggerFileOutput(const char *aFilename)
362 {
363 return std::make_unique<FileOutput>(aFilename);
364 }
365
366 } // lume