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