• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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