• 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 "log/logger_output.h"
17 
18 #include <chrono>
19 #include <cstdarg>
20 #include <ctime>
21 #include <fstream>
22 #include <iomanip>
23 #include <iostream>
24 #include <sstream>
25 #include <string_view>
26 
27 
28 #ifndef WIN32_LEAN_AND_MEAN
29 #define WIN32_LEAN_AND_MEAN
30 #endif
31 #include <windows.h>
32 #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
33 constexpr auto ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004;
34 #endif
35 
36 #include <core/namespace.h>
37 
38 #include "log/logger.h"
39 
40 CORE_BEGIN_NAMESPACE()
41 using BASE_NS::string_view;
42 
43 class StdOutput final : public ILogger::IOutput {
44 public:
StdOutput()45     StdOutput()
46     {
47         // Set console (for this program) to use utf-8.
48         SetConsoleOutputCP(65001u);
49 
50         // Try to figure out if this output stream supports colors.
51         const HANDLE stdHandle = GetStdHandle(STD_OUTPUT_HANDLE);
52         if (stdHandle) {
53             // Check if the output is being redirected.
54             DWORD handleMode;
55             if (GetConsoleMode(stdHandle, &handleMode) != 0) {
56                 // Try to enable the option needed that supports colors.
57                 handleMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
58                 SetConsoleMode(stdHandle, handleMode);
59 
60                 GetConsoleMode(stdHandle, &handleMode);
61                 if ((handleMode & ENABLE_VIRTUAL_TERMINAL_PROCESSING) != 0) {
62                     useColor_ = true;
63                 }
64             }
65         }
66     }
67 
Write(ILogger::LogLevel logLevel,const string_view filename,int linenumber,const string_view message)68     void Write(
69         ILogger::LogLevel logLevel, const string_view filename, int linenumber, const string_view message) override
70     {
71         auto& outputStream = std::cout;
72 
73         LoggerUtils::PrintTimeStamp(outputStream);
74         const auto levelString = Logger::GetLogLevelName(logLevel, true);
75         outputStream << ' ' << std::string_view(levelString.data(), levelString.size());
76 
77         if (!filename.empty()) {
78             // Align the printed messages to same horizontal position regardless of the printed filename length.
79             // (Unless the filename is very long)
80             constexpr int fileLinkFieldSize = 30;
81 
82             auto const filenameView = LoggerUtils::GetFilename({ filename.data(), filename.size() });
83             // Break long messages to multiple lines. 0..9 on one line. 10..99 on two lines. 100..999 on three and above
84             // that four.
85             const int lineNumberLength = (linenumber < 10 ? 1 : (linenumber < 100 ? 2 : (linenumber < 1000 ? 3 : 4)));
86             const int fileLinkPadding =
87                 fileLinkFieldSize - (static_cast<int>(filenameView.length()) + lineNumberLength);
88             if (fileLinkPadding > 0) {
89                 outputStream << std::setw(fileLinkPadding) << "";
90             }
91             outputStream << " (" << filenameView << ':' << linenumber << ')';
92         }
93         outputStream << ": ";
94 
95         if (logLevel >= ILogger::LogLevel::LOG_ERROR) {
96             SetColor(outputStream, ColorCode::RED);
97         } else if (logLevel == ILogger::LogLevel::LOG_WARNING) {
98             SetColor(outputStream, ColorCode::YELLOW);
99         } else if (logLevel <= ILogger::LogLevel::LOG_DEBUG) {
100             SetColor(outputStream, ColorCode::BLACK_BRIGHT);
101         } else {
102             SetColor(outputStream, ColorCode::RESET);
103         }
104 
105         outputStream << std::string_view(message.data(), message.size());
106         SetColor(outputStream, ColorCode::RESET);
107         outputStream << std::endl;
108     }
109 
110 protected:
Destroy()111     void Destroy() override
112     {
113         delete this;
114     }
115 
116 private:
117     enum class ColorCode {
118         BLACK = 0,
119         RED,
120         GREEN,
121         YELLOW,
122         BLUE,
123         MAGENTA,
124         CYAN,
125         WHITE,
126         BLACK_BRIGHT,
127         RED_BRIGHT,
128         GREEN_BRIGHT,
129         YELLOW_BRIGHT,
130         BLUE_BRIGHT,
131         MAGENTA_BRIGHT,
132         CYAN_BRIGHT,
133         WHITE_BRIGHT,
134         RESET,
135         COLOR_CODE_COUNT
136     };
137     // Note: these must match the ColorCode enum.
138     static constexpr const string_view COLOR_CODES[static_cast<int>(ColorCode::COLOR_CODE_COUNT)] = {
139         "\x1B[30m",
140         "\x1B[31m",
141         "\x1B[32m",
142         "\x1B[33m",
143         "\x1B[34m",
144         "\x1B[35m",
145         "\x1B[36m",
146         "\x1B[37m",
147         "\x1B[30;1m",
148         "\x1B[31;1m",
149         "\x1B[32;1m",
150         "\x1B[33;1m",
151         "\x1B[34;1m",
152         "\x1B[35;1m",
153         "\x1B[36;1m",
154         "\x1B[37;1m",
155         "\x1B[0m",
156     };
157 
GetColorString(ColorCode colorCode)158     static const string_view GetColorString(ColorCode colorCode)
159     {
160         const int code = static_cast<int>(colorCode);
161         CORE_ASSERT(code >= 0 && code < static_cast<int>(ColorCode::COLOR_CODE_COUNT));
162         return COLOR_CODES[code];
163     }
164 
SetColor(std::ostream & outputStream,ColorCode colorCode)165     void SetColor(std::ostream& outputStream, ColorCode colorCode)
166     {
167         if (colorCode < ColorCode::BLACK || colorCode >= ColorCode::COLOR_CODE_COUNT) {
168             return;
169         }
170 
171         if (!useColor_) {
172             return;
173         }
174 
175         if (colorCode == currentColorString_) {
176             return;
177         }
178 
179         currentColorString_ = colorCode;
180 
181         const auto colorString = GetColorString(colorCode);
182         outputStream << std::string_view(colorString.data(), colorString.size());
183     }
184 
185     bool useColor_ { false };
186     ColorCode currentColorString_ { ColorCode::RESET };
187 };
188 
189 #if !defined(NDEBUG)
190 class WindowsDebugOutput final : public ILogger::IOutput {
191 public:
Write(ILogger::LogLevel logLevel,const string_view filename,int linenumber,const string_view message)192     void Write(
193         ILogger::LogLevel logLevel, const string_view filename, int linenumber, const string_view message) override
194     {
195         std::stringstream outputStream;
196 
197         if (!filename.empty()) {
198             outputStream << std::string_view(filename.data(), filename.size()) << '(' << linenumber << ") : ";
199         } else {
200             outputStream << "core : ";
201         }
202 
203         LoggerUtils::PrintTimeStamp(outputStream);
204         const auto levelString = Logger::GetLogLevelName(logLevel, true);
205         outputStream << ' ' << std::string_view(levelString.data(), levelString.size());
206         outputStream << ": " << std::string_view(message.data(), message.size());
207         outputStream << '\n';
208 
209         // Convert from utf8 to windows wide unicode string.
210         const std::string string = outputStream.str();
211         const int wStringLength =
212             ::MultiByteToWideChar(CP_UTF8, 0, string.c_str(), static_cast<int>(string.size()), nullptr, 0);
213         std::wstring wString(static_cast<const size_t>(wStringLength), 0);
214         ::MultiByteToWideChar(
215             CP_UTF8, 0, string.c_str(), static_cast<int>(string.size()), wString.data(), wStringLength);
216 
217         ::OutputDebugStringW(wString.c_str());
218     }
219 
220 protected:
Destroy()221     void Destroy() override
222     {
223         delete this;
224     }
225 };
226 #endif
227 
CreateLoggerConsoleOutput()228 ILogger::IOutput::Ptr CreateLoggerConsoleOutput()
229 {
230     return ILogger::IOutput::Ptr { new StdOutput };
231 }
232 
CreateLoggerDebugOutput()233 ILogger::IOutput::Ptr CreateLoggerDebugOutput()
234 {
235 #if !defined(NDEBUG)
236     return ILogger::IOutput::Ptr { new WindowsDebugOutput };
237 #else
238     return {};
239 #endif
240 }
241 CORE_END_NAMESPACE()
242