1 /*
2 * Copyright (c) 2021 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 #include "os/thread.h"
18 #include "string_helpers.h"
19 #include "generated/base_options.h"
20
21 #include <cstdarg>
22 #include <cstdlib>
23 #include <cstring>
24
25 #include <fstream>
26 #include <iostream>
27 #include <string_view>
28
29 namespace panda {
30
31 Logger *Logger::logger = nullptr;
32 os::memory::Mutex Logger::mutex; // NOLINT(fuchsia-statically-constructed-objects)
33 FUNC_MOBILE_LOG_PRINT mlog_buf_print = nullptr;
34
Initialize(const base_options::Options & options)35 void Logger::Initialize(const base_options::Options &options)
36 {
37 panda::Logger::ComponentMask component_mask;
38 for (const auto &s : options.GetLogComponents()) {
39 component_mask |= Logger::ComponentMaskFromString(s);
40 }
41
42 if (options.GetLogStream() == "std") {
43 Logger::InitializeStdLogging(Logger::LevelFromString(options.GetLogLevel()), component_mask);
44 } else if (options.GetLogStream() == "file" || options.GetLogStream() == "fast-file") {
45 const std::string &file_name = options.GetLogFile();
46 Logger::InitializeFileLogging(file_name, Logger::LevelFromString(options.GetLogLevel()), component_mask);
47 } else if (options.GetLogStream() == "dummy") {
48 Logger::InitializeDummyLogging(Logger::LevelFromString(options.GetLogLevel()), component_mask);
49 } else {
50 UNREACHABLE();
51 }
52 }
53
~Message()54 Logger::Message::~Message()
55 {
56 if (print_system_error_) {
57 stream_ << ": " << os::Error(errno).ToString();
58 }
59
60 Logger::Log(level_, component_, stream_.str());
61
62 if (level_ == Level::FATAL) {
63 std::abort();
64 }
65 }
66
GetComponentTag(Logger::Component component)67 static const char *GetComponentTag(Logger::Component component)
68 {
69 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
70 #define D(e, v, str) \
71 if (component == Logger::Component::e) { \
72 return str; \
73 }
74 LOG_COMPONENT_LIST(D)
75 #undef D
76
77 UNREACHABLE();
78 }
79
80 /* static */
Log(Level level,Component component,const std::string & str)81 void Logger::Log(Level level, Component component, const std::string &str)
82 {
83 if (!IsLoggingOn(level, component)) {
84 return;
85 }
86
87 os::memory::LockHolder<os::memory::Mutex> lock(mutex);
88 if (!IsLoggingOn(level, component)) {
89 return;
90 }
91
92 size_t nl = str.find('\n');
93 if (nl == std::string::npos) {
94 logger->LogLineInternal(level, component, str);
95 logger->WriteMobileLog(level, GetComponentTag(component), str.c_str());
96 } else {
97 size_t i = 0;
98 while (nl != std::string::npos) {
99 std::string line = str.substr(i, nl - i);
100 logger->LogLineInternal(level, component, line);
101 logger->WriteMobileLog(level, GetComponentTag(component), line.c_str());
102 i = nl + 1;
103 nl = str.find('\n', i);
104 }
105
106 logger->LogLineInternal(level, component, str.substr(i));
107 logger->WriteMobileLog(level, GetComponentTag(component), str.substr(i).c_str());
108 }
109 }
110
GetLevelTag(Logger::Level level)111 static const char *GetLevelTag(Logger::Level level)
112 {
113 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
114 #define D(e, v, short_str, str) \
115 if (level == Logger::Level::e) { \
116 return short_str; \
117 }
118 LOG_LEVEL_LIST(D)
119 #undef D
120
121 UNREACHABLE();
122 }
123
124 /* static */
GetPrefix(Logger::Level level,Logger::Component component)125 std::string GetPrefix(Logger::Level level, Logger::Component component)
126 {
127 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
128 return helpers::string::Format("[TID %06x] %s/%s: ", os::thread::GetCurrentThreadId(), GetLevelTag(level),
129 GetComponentTag(component));
130 }
131
132 /* static */
InitializeFileLogging(const std::string & log_file,Level level,ComponentMask component_mask)133 void Logger::InitializeFileLogging(const std::string &log_file, Level level, ComponentMask component_mask)
134 {
135 if (IsInitialized()) {
136 return;
137 }
138
139 os::memory::LockHolder<os::memory::Mutex> lock(mutex);
140
141 if (IsInitialized()) {
142 return;
143 }
144
145 std::ofstream stream(log_file);
146 if (stream) {
147 // CODECHECK-NOLINTNEXTLINE(CPP_RULE_ID_SMARTPOINTER_INSTEADOF_ORIGINPOINTER)
148 logger = new FileLogger(std::move(stream), level, component_mask);
149 } else {
150 // CODECHECK-NOLINTNEXTLINE(CPP_RULE_ID_SMARTPOINTER_INSTEADOF_ORIGINPOINTER)
151 logger = new StderrLogger(level, component_mask);
152 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
153 std::string msg = helpers::string::Format("Fallback to stderr logging: cannot open log file '%s': %s",
154 log_file.c_str(), os::Error(errno).ToString().c_str());
155 logger->LogLineInternal(Level::ERROR, Component::COMMON, msg);
156 }
157 #ifdef PANDA_TARGET_UNIX
158 if (DfxController::IsInitialized() && DfxController::GetOptionValue(DfxOptionHandler::MOBILE_LOG) == 0) {
159 Logger::SetMobileLogOpenFlag(false);
160 }
161 #endif
162 }
163
164 /* static */
InitializeStdLogging(Level level,ComponentMask component_mask)165 void Logger::InitializeStdLogging(Level level, ComponentMask component_mask)
166 {
167 if (IsInitialized()) {
168 return;
169 }
170
171 {
172 os::memory::LockHolder<os::memory::Mutex> lock(mutex);
173
174 if (IsInitialized()) {
175 return;
176 }
177
178 // CODECHECK-NOLINTNEXTLINE(CPP_RULE_ID_SMARTPOINTER_INSTEADOF_ORIGINPOINTER)
179 logger = new StderrLogger(level, component_mask);
180 #ifdef PANDA_TARGET_UNIX
181 if (DfxController::IsInitialized() && DfxController::GetOptionValue(DfxOptionHandler::MOBILE_LOG) == 0) {
182 Logger::SetMobileLogOpenFlag(false);
183 }
184 #endif
185 }
186 }
187
188 /* static */
InitializeDummyLogging(Level level,ComponentMask component_mask)189 void Logger::InitializeDummyLogging(Level level, ComponentMask component_mask)
190 {
191 if (IsInitialized()) {
192 return;
193 }
194
195 {
196 os::memory::LockHolder<os::memory::Mutex> lock(mutex);
197
198 if (IsInitialized()) {
199 return;
200 }
201
202 // CODECHECK-NOLINTNEXTLINE(CPP_RULE_ID_SMARTPOINTER_INSTEADOF_ORIGINPOINTER)
203 logger = new DummyLogger(level, component_mask);
204 }
205 }
206
207 /* static */
Destroy()208 void Logger::Destroy()
209 {
210 if (!IsInitialized()) {
211 return;
212 }
213
214 Logger *l = nullptr;
215
216 {
217 os::memory::LockHolder<os::memory::Mutex> lock(mutex);
218
219 if (!IsInitialized()) {
220 return;
221 }
222
223 l = logger;
224 logger = nullptr;
225 }
226
227 delete l;
228 }
229
230 /* static */
LevelFromString(std::string_view s)231 Logger::Level Logger::LevelFromString(std::string_view s)
232 {
233 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
234 #define D(e, v, short_str, str) \
235 if (s == str) { \
236 return Logger::Level::e; \
237 }
238 LOG_LEVEL_LIST(D)
239 #undef D
240
241 UNREACHABLE();
242 }
243
244 /* static */
ComponentMaskFromString(std::string_view s)245 Logger::ComponentMask Logger::ComponentMaskFromString(std::string_view s)
246 {
247 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
248 #define D(e, v, str) \
249 if (s == str) { \
250 return panda::Logger::ComponentMask().set(Logger::Component::e); \
251 }
252 LOG_COMPONENT_LIST(D)
253 #undef D
254
255 if (s == "all") {
256 return panda::LoggerComponentMaskAll;
257 }
258
259 UNREACHABLE();
260 }
261
262 /* static */
StringfromDfxComponent(LogDfxComponent dfx_component)263 std::string Logger::StringfromDfxComponent(LogDfxComponent dfx_component)
264 {
265 switch (dfx_component) {
266 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
267 #define D(e, v, str) \
268 case Logger::LogDfxComponent::e: \
269 return str;
270 LOG_DFX_COMPONENT_LIST(D) // CODECHECK-NOLINT(C_RULE_ID_SWITCH_INDENTATION)
271 #undef D
272 default:
273 break;
274 }
275 UNREACHABLE();
276 }
277
278 /* static */
IsInLevelList(std::string_view s)279 bool Logger::IsInLevelList(std::string_view s)
280 {
281 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
282 #define D(e, v, short_str, str) \
283 if (s == str) { \
284 return true; \
285 }
286 LOG_LEVEL_LIST(D)
287 #undef D
288 return false;
289 }
290
291 /* static */
IsInComponentList(std::string_view s)292 bool Logger::IsInComponentList(std::string_view s)
293 {
294 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
295 #define D(e, v, str) \
296 if (s == str) { \
297 return true; \
298 }
299 LOG_COMPONENT_LIST(D)
300 #undef D
301 if (s == "all") {
302 return true;
303 }
304 return false;
305 }
306
307 /* static */
ProcessLogLevelFromString(std::string_view s)308 void Logger::ProcessLogLevelFromString(std::string_view s)
309 {
310 if (Logger::IsInLevelList(s)) {
311 Logger::SetLevel(Logger::LevelFromString(s));
312 } else {
313 LOG(ERROR, RUNTIME) << "Unknown level " << s;
314 }
315 }
316
317 /* static */
ProcessLogComponentsFromString(std::string_view s)318 void Logger::ProcessLogComponentsFromString(std::string_view s)
319 {
320 Logger::ResetComponentMask();
321 size_t last_pos = s.find_first_not_of(',', 0);
322 size_t pos = s.find(',', last_pos);
323 while (last_pos != std::string_view::npos) {
324 std::string_view component_str = s.substr(last_pos, pos - last_pos);
325 last_pos = s.find_first_not_of(',', pos);
326 pos = s.find(',', last_pos);
327 if (Logger::IsInComponentList(component_str)) {
328 Logger::EnableComponent(Logger::ComponentMaskFromString(component_str));
329 } else {
330 LOG(ERROR, RUNTIME) << "Unknown component " << component_str;
331 }
332 }
333 }
334
LogLineInternal(Level level,Component component,const std::string & str)335 void FileLogger::LogLineInternal(Level level, Component component, const std::string &str)
336 {
337 std::string prefix = GetPrefix(level, component);
338 stream_ << prefix << str << std::endl << std::flush;
339 }
340
LogLineInternal(Level level,Component component,const std::string & str)341 void StderrLogger::LogLineInternal(Level level, Component component, const std::string &str)
342 {
343 std::string prefix = GetPrefix(level, component);
344 std::cerr << prefix << str << std::endl << std::flush;
345 }
346
347 } // namespace panda
348