• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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