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