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 30namespace panda { 31 32Logger *Logger::logger = nullptr; 33thread_local int Logger::nesting = 0; 34 35#include <logger_impl_gen.inc> 36 37void 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 */ 95bool 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 */ 105void 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 */ 114void Logger::LogNestingDec() 115{ 116 nesting--; 117} 118#endif // NDEBUG 119 120auto 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 132os::memory::Mutex Logger::mutex; // NOLINT(fuchsia-statically-constructed-objects) 133FUNC_MOBILE_LOG_PRINT mlog_buf_print = nullptr; 134 135Logger::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 */ 155void 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 */ 186std::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 */ 194void 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 */ 233void 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 */ 252void 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 */ 270void 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 */ 288void 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 */ 311void 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 */ 321void 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 338void 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 344void 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 351void 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 377void 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 383void FastFileLogger::SyncOutputResource() 384{ 385 stream_ << std::flush; 386} 387 388} // namespace panda 389