• 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 #include <unistd.h>
29 
30 #include <core/namespace.h>
31 
32 #include "log/logger.h"
33 
34 CORE_BEGIN_NAMESPACE()
35 using BASE_NS::string_view;
36 
37 namespace {
38 // Gets the filename part from the path.
GetFilename(std::string_view path)39 std::string_view GetFilename(std::string_view path)
40 {
41     if (auto const pos = path.find_last_of("\\/"); pos != std::string_view::npos) {
42         return path.substr(pos + 1);
43     }
44     return path;
45 }
46 
PrintTimeStamp(std::ostream & outputStream)47 void PrintTimeStamp(std::ostream& outputStream)
48 {
49     const auto now = std::chrono::system_clock::now();
50     const auto time = std::chrono::system_clock::to_time_t(now);
51     const auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()) -
52                     std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch());
53 
54     outputStream << std::put_time(std::localtime(&time), "%D %H:%M:%S.");
55     outputStream << std::right << std::setfill('0') << std::setw(3) << ms.count() << std::setfill(' '); // 3: alignment
56 }
57 } // namespace
58 
59 class StdOutput final : public ILogger::IOutput {
60 public:
StdOutput()61     StdOutput()
62     {
63         // Try to figure out if this output stream supports colors.
64         if (isatty(fileno(stdout))) {
65             // Using colors if the output is not being redirected.
66             useColor_ = true;
67         }
68     }
69 
Write(ILogger::LogLevel logLevel,const string_view filename,int linenumber,const string_view message)70     void Write(
71         ILogger::LogLevel logLevel, const string_view filename, int linenumber, const string_view message) override
72     {
73         auto& outputStream = std::cout;
74 
75         PrintTimeStamp(outputStream);
76         const auto levelString = Logger::GetLogLevelName(logLevel, true);
77         outputStream << ' ' << std::string_view(levelString.data(), levelString.size());
78 
79         if (!filename.empty()) {
80             // Align the printed messages to same horizontal position regardless of the printed filename length.
81             // (Unless the filename is very long)
82             constexpr int fileLinkFieldSize = 30;
83 
84             auto const filenameView = GetFilename({ filename.data(), filename.size() });
85             // Break long messages to multiple lines. 0..9 on one line. 10..99 on two lines. 100..999 on three and above
86             // that four.
87             const int lineNumberLength = (linenumber < 10 ? 1 : (linenumber < 100 ? 2 : (linenumber < 1000 ? 3 : 4)));
88             const int fileLinkPadding =
89                 fileLinkFieldSize - (static_cast<int>(filenameView.length()) + lineNumberLength);
90             if (fileLinkPadding > 0) {
91                 outputStream << std::setw(fileLinkPadding) << "";
92             }
93             outputStream << " (" << filenameView << ':' << linenumber << ')';
94         }
95         outputStream << ": ";
96 
97         if (logLevel >= ILogger::LogLevel::LOG_ERROR) {
98             SetColor(outputStream, ColorCode::RED);
99         } else if (logLevel == ILogger::LogLevel::LOG_WARNING) {
100             SetColor(outputStream, ColorCode::YELLOW);
101         } else if (logLevel <= ILogger::LogLevel::LOG_DEBUG) {
102             SetColor(outputStream, ColorCode::BLACK_BRIGHT);
103         } else {
104             SetColor(outputStream, ColorCode::RESET);
105         }
106 
107         outputStream << std::string_view(message.data(), message.size());
108         SetColor(outputStream, ColorCode::RESET);
109         outputStream << std::endl;
110     }
111 
112 protected:
Destroy()113     void Destroy() override
114     {
115         delete this;
116     }
117 
118 private:
119     enum class ColorCode {
120         BLACK = 0,
121         RED,
122         GREEN,
123         YELLOW,
124         BLUE,
125         MAGENTA,
126         CYAN,
127         WHITE,
128         BLACK_BRIGHT,
129         RED_BRIGHT,
130         GREEN_BRIGHT,
131         YELLOW_BRIGHT,
132         BLUE_BRIGHT,
133         MAGENTA_BRIGHT,
134         CYAN_BRIGHT,
135         WHITE_BRIGHT,
136         RESET,
137         COLOR_CODE_COUNT
138     };
139     // Note: these must match the ColorCode enum.
140     static constexpr const string_view COLOR_CODES[static_cast<int>(ColorCode::COLOR_CODE_COUNT)] = {
141         "\x1B[30m",
142         "\x1B[31m",
143         "\x1B[32m",
144         "\x1B[33m",
145         "\x1B[34m",
146         "\x1B[35m",
147         "\x1B[36m",
148         "\x1B[37m",
149         "\x1B[30;1m",
150         "\x1B[31;1m",
151         "\x1B[32;1m",
152         "\x1B[33;1m",
153         "\x1B[34;1m",
154         "\x1B[35;1m",
155         "\x1B[36;1m",
156         "\x1B[37;1m",
157         "\x1B[0m",
158     };
159 
GetColorString(ColorCode colorCode)160     static const string_view GetColorString(ColorCode colorCode)
161     {
162         const int code = static_cast<int>(colorCode);
163         CORE_ASSERT(code >= 0 && code < static_cast<int>(ColorCode::COLOR_CODE_COUNT));
164         return COLOR_CODES[code];
165     }
166 
SetColor(std::ostream & outputStream,ColorCode colorCode)167     void SetColor(std::ostream& outputStream, ColorCode colorCode)
168     {
169         if (colorCode < ColorCode::BLACK || colorCode >= ColorCode::COLOR_CODE_COUNT) {
170             return;
171         }
172 
173         if (!useColor_) {
174             return;
175         }
176 
177         if (colorCode == currentColorString_) {
178             return;
179         }
180 
181         currentColorString_ = colorCode;
182 
183         const auto colorString = GetColorString(colorCode);
184         outputStream << std::string_view(colorString.data(), colorString.size());
185     }
186 
187     bool useColor_ { false };
188     ColorCode currentColorString_ { ColorCode::RESET };
189 };
190 
191 class FileOutput final : public ILogger::IOutput {
192 public:
FileOutput(const string_view filePath)193     explicit FileOutput(const string_view filePath) : IOutput(), outputStream_(filePath.data(), std::ios::app) {}
194 
195     ~FileOutput() override = default;
196 
Write(ILogger::LogLevel logLevel,const string_view filename,int linenumber,const string_view message)197     void Write(
198         ILogger::LogLevel logLevel, const string_view filename, int linenumber, const string_view message) override
199     {
200         if (outputStream_.is_open()) {
201             auto& outputStream = outputStream_;
202 
203             PrintTimeStamp(outputStream);
204             const auto levelString = Logger::GetLogLevelName(logLevel, true);
205             outputStream << ' ' << std::string_view(levelString.data(), levelString.size());
206 
207             if (!filename.empty()) {
208                 outputStream << " (" << std::string_view(filename.data(), filename.size()) << ':' << linenumber
209                              << "): ";
210             } else {
211                 outputStream << ": ";
212             }
213 
214             outputStream << std::string_view(message.data(), message.size()) << std::endl;
215         }
216     }
217 
218 protected:
Destroy()219     void Destroy() override
220     {
221         delete this;
222     }
223 
224 private:
225     std::ofstream outputStream_;
226 };
227 
228 
CreateLoggerConsoleOutput()229 ILogger::IOutput::Ptr CreateLoggerConsoleOutput()
230 {
231     return ILogger::IOutput::Ptr { new StdOutput };
232 }
233 
CreateLoggerDebugOutput()234 ILogger::IOutput::Ptr CreateLoggerDebugOutput()
235 {
236     return {};
237 }
238 
239 CORE_END_NAMESPACE()
240