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