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