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