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