1 /**
2 * Copyright (c) 2021-2022 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/filesystem.h"
18 #include "os/thread.h"
19 #include "string_helpers.h"
20 #include "generated/base_options.h"
21
22 #include <cstdarg>
23 #include <cstdlib>
24 #include <cstring>
25
26 #include <fstream>
27 #include <iostream>
28 #include <string_view>
29
30 namespace panda {
31
32 Logger *Logger::logger = nullptr;
33 thread_local int Logger::nesting = 0;
34
35 #include <logger_impl_gen.inc>
36
Initialize(const base_options::Options & options)37 void Logger::Initialize(const base_options::Options &options)
38 {
39 panda::Logger::ComponentMask component_mask;
40 auto load_components = [&component_mask](auto components) {
41 for (const auto &s : components) {
42 component_mask |= Logger::ComponentMaskFromString(s);
43 }
44 };
45 Level level = Level::LAST;
46
47 if (options.WasSetLogFatal()) {
48 ASSERT_PRINT(level == Level::LAST, "There are conflicting logger options");
49 load_components(options.GetLogFatal());
50 level = Level::FATAL;
51 } else if (options.WasSetLogError()) {
52 ASSERT_PRINT(level == Level::LAST, "There are conflicting logger options");
53 load_components(options.GetLogError());
54 level = Level::ERROR;
55 } else if (options.WasSetLogWarning()) {
56 ASSERT_PRINT(level == Level::LAST, "There are conflicting logger options");
57 load_components(options.GetLogWarning());
58 level = Level::WARNING;
59 } else if (options.WasSetLogInfo()) {
60 ASSERT_PRINT(level == Level::LAST, "There are conflicting logger options");
61 load_components(options.GetLogInfo());
62 level = Level::INFO;
63 } else if (options.WasSetLogDebug()) {
64 ASSERT_PRINT(level == Level::LAST, "There are conflicting logger options");
65 load_components(options.GetLogDebug());
66 level = Level::DEBUG;
67 } else {
68 ASSERT_PRINT(level == Level::LAST, "There are conflicting logger options");
69 load_components(options.GetLogComponents());
70 level = Logger::LevelFromString(options.GetLogLevel());
71 }
72
73 #ifdef ENABLE_HILOG
74 Logger::InitializeHiLogging(level, component_mask);
75 return;
76 #endif
77
78 if (options.GetLogStream() == "std") {
79 Logger::InitializeStdLogging(level, component_mask);
80 } else if (options.GetLogStream() == "file" || options.GetLogStream() == "fast-file") {
81 const std::string &file_name = options.GetLogFile();
82 Logger::InitializeFileLogging(file_name, level, component_mask, options.GetLogStream() == "fast-file");
83 } else if (options.GetLogStream() == "dummy") {
84 Logger::InitializeDummyLogging(level, component_mask);
85 } else {
86 UNREACHABLE();
87 }
88 }
89
90 #ifndef NDEBUG
91 /**
92 * In debug builds this function allowes or disallowes proceeding with actual logging (i.e. creating Message{})
93 */
94 /* static */
IsMessageSuppressed(Level level,Component component)95 bool Logger::IsMessageSuppressed([[maybe_unused]] Level level, [[maybe_unused]] Component component)
96 {
97 // Allowing only to log if it's not a nested log, or it's nested and it's severity is suitable
98 return level >= Logger::logger->nested_allowed_level_ && nesting > 0;
99 }
100
101 /**
102 * Increases log nesting (i.e. depth, or how many instances of Message{} is active atm) in a given thread
103 */
104 /* static */
LogNestingInc()105 void Logger::LogNestingInc()
106 {
107 nesting++;
108 }
109
110 /**
111 * Decreases log nesting (i.e. depth, or how many instances of Message{} is active atm) in a given thread
112 */
113 /* static */
LogNestingDec()114 void Logger::LogNestingDec()
115 {
116 nesting--;
117 }
118 #endif // NDEBUG
119
printf(const char * format,...)120 auto Logger::Buffer::printf(const char *format, ...) -> Buffer &
121 {
122 va_list args;
123 va_start(args, format); // NOLINT(cppcoreguidelines-pro-type-vararg)
124
125 [[maybe_unused]] int put = vsnprintf_s(this->data(), this->size(), this->size() - 1, format, args);
126 ASSERT(put >= 0 && static_cast<size_t>(put) < BUFFER_SIZE);
127
128 va_end(args);
129 return *this;
130 }
131
132 os::memory::Mutex Logger::mutex; // NOLINT(fuchsia-statically-constructed-objects)
133 FUNC_MOBILE_LOG_PRINT mlog_buf_print = nullptr;
134
~Message()135 Logger::Message::~Message()
136 {
137 if (print_system_error_) {
138 stream_ << ": " << os::Error(errno).ToString();
139 }
140
141 Logger::Log(level_, component_, stream_.str());
142 #ifndef NDEBUG
143 panda::Logger::LogNestingDec();
144 #endif
145
146 if (level_ == Level::FATAL) {
147 std::cerr << "FATAL ERROR" << std::endl;
148 std::cerr << "Backtrace [tid=" << os::thread::GetCurrentThreadId() << "]:\n";
149 PrintStack(std::cerr);
150 std::abort();
151 }
152 }
153
154 /* static */
Log(Level level,Component component,const std::string & str)155 void Logger::Log(Level level, Component component, const std::string &str)
156 {
157 if (!IsLoggingOn(level, component)) {
158 return;
159 }
160
161 os::memory::LockHolder<os::memory::Mutex> lock(mutex);
162 if (!IsLoggingOn(level, component)) {
163 return;
164 }
165
166 size_t nl = str.find('\n');
167 if (nl == std::string::npos) {
168 logger->LogLineInternal(level, component, str);
169 logger->WriteMobileLog(level, GetComponentTag(component), str.c_str());
170 } else {
171 size_t i = 0;
172 while (nl != std::string::npos) {
173 std::string line = str.substr(i, nl - i);
174 logger->LogLineInternal(level, component, line);
175 logger->WriteMobileLog(level, GetComponentTag(component), line.c_str());
176 i = nl + 1;
177 nl = str.find('\n', i);
178 }
179
180 logger->LogLineInternal(level, component, str.substr(i));
181 logger->WriteMobileLog(level, GetComponentTag(component), str.substr(i).c_str());
182 }
183 }
184
185 /* static */
GetPrefix(Logger::Level level,Logger::Component component)186 std::string GetPrefix(Logger::Level level, Logger::Component component)
187 {
188 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
189 return helpers::string::Format("[TID %06x] %s/%s: ", os::thread::GetCurrentThreadId(), GetLevelTag(level),
190 GetComponentTag(component));
191 }
192
193 /* static */
InitializeFileLogging(const std::string & log_file,Level level,const ComponentMask & component_mask,bool is_fast_logging)194 void Logger::InitializeFileLogging(const std::string &log_file, Level level, const ComponentMask &component_mask,
195 bool is_fast_logging)
196 {
197 if (IsInitialized()) {
198 return;
199 }
200
201 os::memory::LockHolder<os::memory::Mutex> lock(mutex);
202
203 if (IsInitialized()) {
204 return;
205 }
206
207 auto path = os::GetAbsolutePath(log_file);
208 if (path == "") {
209 logger = new StderrLogger(level, component_mask);
210 std::string msg = helpers::string::Format("Fallback to stderr logging: resolve file path error");
211 logger->LogLineInternal(Level::ERROR, Component::COMMON, msg);
212 return;
213 }
214
215 std::ofstream stream(path);
216 if (stream) {
217 if (is_fast_logging) {
218 logger = new FastFileLogger(std::move(stream), level, component_mask);
219 } else {
220 logger = new FileLogger(std::move(stream), level, component_mask);
221 }
222 } else {
223 logger = new StderrLogger(level, component_mask);
224 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
225 std::string msg = helpers::string::Format("Fallback to stderr logging: cannot open log file '%s': %s",
226 log_file.c_str(), os::Error(errno).ToString().c_str());
227 logger->LogLineInternal(Level::ERROR, Component::COMMON, msg);
228 }
229 }
230
231 #ifdef ENABLE_HILOG
232 /* static */
InitializeHiLogging(Level level,const ComponentMask & component_mask)233 void Logger::InitializeHiLogging(Level level, const ComponentMask &component_mask)
234 {
235 if (IsInitialized()) {
236 return;
237 }
238
239 {
240 os::memory::LockHolder<os::memory::Mutex> lock(mutex);
241
242 if (IsInitialized()) {
243 return;
244 }
245
246 logger = new HiLogger(level, component_mask);
247 }
248 }
249 #endif
250
251 /* static */
InitializeStdLogging(Level level,const ComponentMask & component_mask)252 void Logger::InitializeStdLogging(Level level, const ComponentMask &component_mask)
253 {
254 if (IsInitialized()) {
255 return;
256 }
257
258 {
259 os::memory::LockHolder<os::memory::Mutex> lock(mutex);
260
261 if (IsInitialized()) {
262 return;
263 }
264
265 logger = new StderrLogger(level, component_mask);
266 }
267 }
268
269 /* static */
InitializeDummyLogging(Level level,const ComponentMask & component_mask)270 void Logger::InitializeDummyLogging(Level level, const ComponentMask &component_mask)
271 {
272 if (IsInitialized()) {
273 return;
274 }
275
276 {
277 os::memory::LockHolder<os::memory::Mutex> lock(mutex);
278
279 if (IsInitialized()) {
280 return;
281 }
282
283 logger = new DummyLogger(level, component_mask);
284 }
285 }
286
287 /* static */
Destroy()288 void Logger::Destroy()
289 {
290 if (!IsInitialized()) {
291 return;
292 }
293
294 Logger *l = nullptr;
295
296 {
297 os::memory::LockHolder<os::memory::Mutex> lock(mutex);
298
299 if (!IsInitialized()) {
300 return;
301 }
302
303 l = logger;
304 logger = nullptr;
305 }
306
307 delete l;
308 }
309
310 /* static */
ProcessLogLevelFromString(std::string_view s)311 void Logger::ProcessLogLevelFromString(std::string_view s)
312 {
313 if (Logger::IsInLevelList(s)) {
314 Logger::SetLevel(Logger::LevelFromString(s));
315 } else {
316 LOG(ERROR, RUNTIME) << "Unknown level " << s;
317 }
318 }
319
320 /* static */
ProcessLogComponentsFromString(std::string_view s)321 void Logger::ProcessLogComponentsFromString(std::string_view s)
322 {
323 Logger::ResetComponentMask();
324 size_t last_pos = s.find_first_not_of(',', 0);
325 size_t pos = s.find(',', last_pos);
326 while (last_pos != std::string_view::npos) {
327 std::string_view component_str = s.substr(last_pos, pos - last_pos);
328 last_pos = s.find_first_not_of(',', pos);
329 pos = s.find(',', last_pos);
330 if (Logger::IsInComponentList(component_str)) {
331 Logger::EnableComponent(Logger::ComponentMaskFromString(component_str));
332 } else {
333 LOG(ERROR, RUNTIME) << "Unknown component " << component_str;
334 }
335 }
336 }
337
LogLineInternal(Level level,Component component,const std::string & str)338 void FileLogger::LogLineInternal(Level level, Component component, const std::string &str)
339 {
340 std::string prefix = GetPrefix(level, component);
341 stream_ << prefix << str << std::endl << std::flush;
342 }
343
LogLineInternal(Level level,Component component,const std::string & str)344 void FastFileLogger::LogLineInternal(Level level, Component component, const std::string &str)
345 {
346 std::string prefix = GetPrefix(level, component);
347 stream_ << prefix << str << '\n';
348 }
349
350 #ifdef ENABLE_HILOG
LogLineInternal(Level level,Component component,const std::string & str)351 void HiLogger::LogLineInternal(Level level, Component component, const std::string &str)
352 {
353 std::string prefix = GetPrefix(level, component);
354 stream_ << prefix << str;
355 switch (level) {
356 case Level::DEBUG:
357 HILOG_DEBUG(LOG_CORE, "%{public}s", stream_.str().c_str());
358 break;
359 case Level::INFO:
360 HILOG_INFO(LOG_CORE, "%{public}s", stream_.str().c_str());
361 break;
362 case Level::ERROR:
363 HILOG_ERROR(LOG_CORE, "%{public}s", stream_.str().c_str());
364 break;
365 case Level::FATAL:
366 HILOG_FATAL(LOG_CORE, "%{public}s", stream_.str().c_str());
367 break;
368 case Level::WARNING:
369 HILOG_WARN(LOG_CORE, "%{public}s", stream_.str().c_str());
370 break;
371 default:
372 UNREACHABLE();
373 }
374 }
375 #endif
376
LogLineInternal(Level level,Component component,const std::string & str)377 void StderrLogger::LogLineInternal(Level level, Component component, const std::string &str)
378 {
379 std::string prefix = GetPrefix(level, component);
380 std::cerr << prefix << str << std::endl << std::flush;
381 }
382
SyncOutputResource()383 void FastFileLogger::SyncOutputResource()
384 {
385 stream_ << std::flush;
386 }
387
388 } // namespace panda
389