• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <chrono>
17 #include <cstdarg>
18 #include <ctime>
19 #include <fstream>
20 #include <iomanip>
21 #include <iostream>
22 #include <sstream>
23 #include <string>
24 
25 #include "Logger.h"
26 
27 #if defined(_WIN32)
28 #include <windows.h>
29 #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
30 #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
31 #endif
32 #ifdef ERROR
33 #undef ERROR
34 #endif
35 #else
36 #include <unistd.h>
37 #endif
38 
39 namespace lume {
40 
41 namespace {
42 // Note: these must match the ColorCode enum.
43 constexpr int COLOR_CODE_COUNT = 17;
44 constexpr const char* COLOR_CODES[COLOR_CODE_COUNT] = {
45     "\x1B[30m",
46     "\x1B[31m",
47     "\x1B[32m",
48     "\x1B[33m",
49     "\x1B[34m",
50     "\x1B[35m",
51     "\x1B[36m",
52     "\x1B[37m",
53     "\x1B[30;1m",
54     "\x1B[31;1m",
55     "\x1B[32;1m",
56     "\x1B[33;1m",
57     "\x1B[34;1m",
58     "\x1B[35;1m",
59     "\x1B[36;1m",
60     "\x1B[37;1m",
61     "\x1B[0m",
62 };
63 
64 // Gets the filename part from the path.
GetFilename(const std::string & path)65 std::string GetFilename(const std::string& path)
66 {
67     for (int i = static_cast<int>(path.size()) - 1; i >= 0; --i) {
68         unsigned int index = static_cast<size_t>(i);
69         if (path[index] == '\\' || path[index] == '/') {
70             return path.substr(index + 1);
71         }
72     }
73     return path;
74 }
75 
76 } // namespace
77 
78 #if !defined(__PLATFORM_AD__)
79 
80 class StdOutput : public ILogger::IOutput {
81 public:
82     enum class ColorCode {
83         BLACK = 0,
84         RED,
85         GREEN,
86         YELLOW,
87         BLUE,
88         MAGENTA,
89         CYAN,
90         WHITE,
91         BLACK_BRIGHT,
92         RED_BRIGHT,
93         GREEN_BRIGHT,
94         YELLOW_BRIGHT,
95         BLUE_BRIGHT,
96         MAGENTA_BRIGHT,
97         CYAN_BRIGHT,
98         WHITE_BRIGHT,
99         RESET,
100     };
101 
GetColorString(ColorCode aColorCode)102     static const char* GetColorString(ColorCode aColorCode)
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 defined(_WIN32)
112         // Set console (for this program) to use utf-8.
113         constexpr UINT codePageUtf8 = 65001u;
114         SetConsoleOutputCP(codePageUtf8);
115 #endif
116 
117         // Try to figure out if this output stream supports colors.
118 #ifdef _WIN32
119         const HANDLE stdHandle = GetStdHandle(STD_OUTPUT_HANDLE);
120         if (stdHandle) {
121             // Check if the output is being redirected.
122             DWORD handleMode;
123             if (GetConsoleMode(stdHandle, &handleMode) != 0) {
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                     mUseColor = true;
131                 }
132             }
133         }
134 #else
135         if (isatty(fileno(stdout))) {
136             // Using colors if the output is not being redirected.
137             mUseColor = true;
138         }
139 #endif
140     }
141 
SetColor(std::ostream & outputStream,ColorCode aColorCode)142     void SetColor(std::ostream& outputStream, ColorCode aColorCode)
143     {
144         if (!mUseColor) {
145             return;
146         }
147 
148         const char* colorString = GetColorString(aColorCode);
149         if (colorString == mCurrentColorString) {
150             return;
151         }
152 
153         mCurrentColorString = colorString;
154         if (colorString) {
155             outputStream << colorString;
156         }
157     }
158 
Write(ILogger::LogLevel logLevel,const char * filename,int linenumber,const char * message)159     void Write(ILogger::LogLevel logLevel, const char* filename, int linenumber, const char* message) override
160     {
161         auto& outputStream = std::cout;
162 
163         auto now = std::chrono::system_clock::now();
164         auto time = std::chrono::system_clock::to_time_t(now);
165         auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()) -
166                   std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch());
167         static constexpr std::streamsize digitSize = 3;
168         outputStream << std::put_time(std::localtime(&time), "%H:%M:%S.") << std::setw(digitSize) << std::left
169                      << ms.count() << " " << Logger::GetLogLevelName(logLevel, true);
170 
171         if (filename) {
172             static constexpr std::streamsize filenameSize = 30;
173             const std::string filenameLink = " (" + GetFilename(filename) + ':' + std::to_string(linenumber) + ')';
174             outputStream << std::right << std::setw(filenameSize) << filenameLink;
175         }
176         outputStream << ": ";
177 
178         if (logLevel >= ILogger::LogLevel::ERROR) {
179             SetColor(outputStream, ColorCode::RED);
180         } else if (logLevel == ILogger::LogLevel::WARNING) {
181             SetColor(outputStream, ColorCode::YELLOW);
182         } else if (logLevel <= ILogger::LogLevel::DEBUG) {
183             SetColor(outputStream, ColorCode::BLACK_BRIGHT);
184         } else {
185             SetColor(outputStream, ColorCode::RESET);
186         }
187 
188         outputStream << message;
189         SetColor(outputStream, ColorCode::RESET);
190         outputStream << std::endl;
191     }
192 
193 private:
194     bool mUseColor;
195     const char* mCurrentColorString;
196 };
197 
198 #endif // !defined(__PLATFORM_AD__)
199 
200 #if defined(_WIN32) && !defined(NDEBUG)
201 class WindowsDebugOutput : public ILogger::IOutput {
202 public:
Write(ILogger::LogLevel logLevel,const char * filename,int linenumber,const char * message)203     void Write(ILogger::LogLevel logLevel, const char* filename, int linenumber, const char* message) override
204     {
205         std::stringstream outputStream;
206 
207         if (filename) {
208             outputStream << filename << "(" << linenumber << ") : ";
209         } else {
210             outputStream << "lume : ";
211         }
212 
213         auto now = std::chrono::system_clock::now();
214         auto time = std::chrono::system_clock::to_time_t(now);
215         auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()) -
216                   std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch());
217 
218         outputStream << std::put_time(std::localtime(&time), "%D %H:%M:%S.") << ms.count() << " "
219                      << Logger::GetLogLevelName(logLevel, true);
220         outputStream << ": " << message;
221         outputStream << '\n';
222 
223         // Convert from utf8 to windows wide unicode string.
224         std::string output = outputStream.str();
225         int wStringLength =
226             ::MultiByteToWideChar(CP_UTF8, 0, output.c_str(), static_cast<int>(output.size()), nullptr, 0);
227         std::wstring wString(static_cast<size_t>(wStringLength), 0);
228         ::MultiByteToWideChar(CP_UTF8, 0, output.c_str(), static_cast<int>(output.size()), &wString[0], wStringLength);
229 
230         ::OutputDebugStringW(wString.c_str());
231     }
232 };
233 #endif
234 
235 class FileOutput : public ILogger::IOutput {
236 public:
FileOutput(const std::string & aFilePath)237     explicit FileOutput(const std::string& aFilePath) : IOutput(), mOutputStream(aFilePath, std::ios::app) {}
238 
239     ~FileOutput() override = default;
240 
Write(ILogger::LogLevel logLevel,const char * filename,int linenumber,const char * message)241     void Write(ILogger::LogLevel logLevel, const char* filename, int linenumber, const char* message) override
242     {
243         if (mOutputStream.is_open()) {
244             auto& outputStream = mOutputStream;
245 
246             auto now = std::chrono::system_clock::now();
247             auto time = std::chrono::system_clock::to_time_t(now);
248             auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()) -
249                       std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch());
250 
251             outputStream << std::put_time(std::localtime(&time), "%D %H:%M:%S.") << ms.count() << " "
252                          << Logger::GetLogLevelName(logLevel, false);
253 
254             if (filename) {
255                 outputStream << " (" << filename << ":" << linenumber << "): ";
256             } else {
257                 outputStream << ": ";
258             }
259 
260             outputStream << message << std::endl;
261         }
262     }
263 
264 private:
265     std::ofstream mOutputStream;
266 };
267 
CreateLoggerConsoleOutput()268 std::unique_ptr<ILogger::IOutput> CreateLoggerConsoleOutput()
269 {
270 #ifdef __PLATFORM_AD__
271     return std::make_unique<LogcatOutput>();
272 #else
273     return std::make_unique<StdOutput>();
274 #endif
275 }
276 
CreateLoggerDebugOutput()277 std::unique_ptr<ILogger::IOutput> CreateLoggerDebugOutput()
278 {
279 #if defined(_WIN32) && !defined(NDEBUG)
280     return std::make_unique<WindowsDebugOutput>();
281 #else
282     return std::unique_ptr<ILogger::IOutput>();
283 #endif
284 }
285 
CreateLoggerFileOutput(const char * filename)286 std::unique_ptr<ILogger::IOutput> CreateLoggerFileOutput(const char* filename)
287 {
288     return std::make_unique<FileOutput>(filename);
289 }
290 } // namespace lume
291