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 #ifndef PANDA_LIBPANDABASE_UTILS_LOGGER_H_ 17 #define PANDA_LIBPANDABASE_UTILS_LOGGER_H_ 18 19 #include "macros.h" 20 #include "os/error.h" 21 #include "os/mutex.h" 22 #include "os/thread.h" 23 24 #include <array> 25 #include <cstdint> 26 27 #include <bitset> 28 #include <fstream> 29 #include <map> 30 #include <string> 31 #include <sstream> 32 33 #include <atomic> 34 35 namespace panda { 36 37 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 38 #define LOG_COMPONENT_ELEM(D, NAME, STR) D(NAME, NAME, STR) 39 40 using FUNC_MOBILE_LOG_PRINT = int (*)(int, int, const char *, const char *, const char *); 41 constexpr int LOG_ID_MAIN = 0; 42 extern FUNC_MOBILE_LOG_PRINT mlog_buf_print; 43 44 namespace base_options { 45 class Options; 46 } // namespace base_options 47 48 class Logger { 49 public: 50 #include <logger_enum_gen.h> 51 52 using ComponentMask = std::bitset<Component::LAST>; 53 54 enum PandaLog2MobileLog : int { 55 UNKNOWN = 0, 56 DEFAULT, 57 VERBOSE, 58 DEBUG, 59 INFO, 60 WARN, 61 ERROR, 62 FATAL, 63 SILENT, 64 }; 65 66 class Buffer { 67 public: 68 constexpr static size_t BUFFER_SIZE = 4096; 69 70 public: Buffer()71 Buffer() : buffer {} {} 72 73 public: data()74 const char *data() const noexcept 75 { 76 return buffer.data(); 77 } data()78 char *data() noexcept 79 { 80 return buffer.data(); 81 } 82 83 public: size()84 constexpr size_t size() const noexcept 85 { 86 return BUFFER_SIZE; 87 } 88 89 public: 90 // always overwrites buffer data 91 Buffer &printf(const char *format, ...); 92 93 public: 94 friend std::ostream &operator<<(std::ostream &os, const Buffer &b) 95 { 96 return os << b.data(); 97 } 98 99 private: 100 std::array<char, BUFFER_SIZE> buffer; 101 }; 102 103 class Message { 104 public: Message(Level level,Component component,bool print_system_error)105 Message(Level level, Component component, bool print_system_error) 106 : level_(level), component_(component), print_system_error_(print_system_error) 107 { 108 #ifndef NDEBUG 109 Logger::LogNestingInc(); 110 #endif 111 } 112 113 ~Message(); 114 GetStream()115 std::ostream &GetStream() 116 { 117 return stream_; 118 } 119 120 private: 121 Level level_; 122 Component component_; 123 bool print_system_error_; 124 std::ostringstream stream_; 125 126 NO_COPY_SEMANTIC(Message); 127 NO_MOVE_SEMANTIC(Message); 128 }; 129 130 static void Initialize(const base_options::Options &options); 131 132 static void InitializeFileLogging(const std::string &log_file, Level level, ComponentMask component_mask, 133 bool is_fast_logging = false); 134 135 static void InitializeStdLogging(Level level, ComponentMask component_mask); 136 137 static void InitializeDummyLogging(Level level = Level::DEBUG, ComponentMask component_mask = 0); 138 139 static void Destroy(); 140 SetMobileLogPrintEntryPointByPtr(void * mlog_buf_print_ptr)141 static void SetMobileLogPrintEntryPointByPtr(void *mlog_buf_print_ptr) 142 { 143 mlog_buf_print = reinterpret_cast<FUNC_MOBILE_LOG_PRINT>(mlog_buf_print_ptr); 144 } 145 146 static uint32_t GetLevelNumber(Logger::Level level); 147 WriteMobileLog(Level level,const char * component,const char * message)148 void WriteMobileLog(Level level, const char *component, const char *message) 149 { 150 if (mlog_buf_print == nullptr || !is_mlog_opened_) { 151 return; 152 } 153 PandaLog2MobileLog mlog_level = PandaLog2MobileLog::UNKNOWN; 154 switch (level) { 155 case Level::DEBUG: 156 mlog_level = PandaLog2MobileLog::DEBUG; 157 break; 158 case Level::INFO: 159 mlog_level = PandaLog2MobileLog::INFO; 160 break; 161 case Level::ERROR: 162 mlog_level = PandaLog2MobileLog::ERROR; 163 break; 164 case Level::FATAL: 165 mlog_level = PandaLog2MobileLog::FATAL; 166 break; 167 case Level::WARNING: 168 mlog_level = PandaLog2MobileLog::WARN; 169 break; 170 default: 171 UNREACHABLE(); 172 } 173 std::string panda_component = "Ark " + std::string(component); 174 mlog_buf_print(LOG_ID_MAIN, mlog_level, panda_component.c_str(), "%s", message); 175 } 176 IsLoggingOn(Level level,Component component)177 static bool IsLoggingOn(Level level, Component component) 178 { 179 return IsInitialized() && level <= logger->level_ && 180 (logger->component_mask_.test(component) || level == Level::FATAL); 181 } 182 IsLoggingOnOrAbort(Level level,Component component)183 static bool IsLoggingOnOrAbort(Level level, Component component) 184 { 185 if (IsLoggingOn(level, component)) { 186 return true; 187 } 188 189 if (level == Level::FATAL) { 190 std::abort(); 191 } 192 193 return false; 194 } 195 196 #ifndef NDEBUG 197 static void LogNestingInc(); 198 static void LogNestingDec(); 199 static bool IsMessageSuppressed([[maybe_unused]] Level level, [[maybe_unused]] Component component); 200 #endif 201 202 static void Log(Level level, Component component, const std::string &str); 203 Sync()204 static void Sync() 205 { 206 if (IsInitialized()) { 207 logger->SyncOutputResource(); 208 } 209 } 210 211 static Level LevelFromString(std::string_view s); 212 213 static ComponentMask ComponentMaskFromString(std::string_view s); 214 215 static std::string StringfromDfxComponent(LogDfxComponent dfx_component); 216 SetLevel(Level level)217 static void SetLevel(Level level) 218 { 219 ASSERT(IsInitialized()); 220 logger->level_ = level; 221 } 222 GetLevel()223 static Level GetLevel() 224 { 225 ASSERT(IsInitialized()); 226 return logger->level_; 227 } 228 EnableComponent(Component component)229 static void EnableComponent(Component component) 230 { 231 ASSERT(IsInitialized()); 232 logger->component_mask_.set(component); 233 } 234 EnableComponent(ComponentMask component)235 static void EnableComponent(ComponentMask component) 236 { 237 ASSERT(IsInitialized()); 238 logger->component_mask_ |= component; 239 } 240 DisableComponent(Component component)241 static void DisableComponent(Component component) 242 { 243 ASSERT(IsInitialized()); 244 logger->component_mask_.reset(component); 245 } 246 ResetComponentMask()247 static void ResetComponentMask() 248 { 249 ASSERT(IsInitialized()); 250 logger->component_mask_.reset(); 251 } 252 SetMobileLogOpenFlag(bool is_mlog_opened)253 static void SetMobileLogOpenFlag(bool is_mlog_opened) 254 { 255 ASSERT(IsInitialized()); 256 logger->is_mlog_opened_ = is_mlog_opened; 257 } 258 259 static bool IsInLevelList(std::string_view s); 260 261 static bool IsInComponentList(std::string_view s); 262 263 static void ProcessLogLevelFromString(std::string_view s); 264 265 static void ProcessLogComponentsFromString(std::string_view s); 266 IsInitialized()267 static bool IsInitialized() 268 { 269 return logger != nullptr; 270 } 271 272 protected: Logger(Level level,ComponentMask component_mask)273 Logger(Level level, ComponentMask component_mask) 274 : level_(level), 275 component_mask_(component_mask) 276 #ifndef NDEBUG 277 , 278 // Means all the LOGs are allowed just as usual 279 nested_allowed_level_(Level::LAST) 280 #endif 281 { 282 } 283 Logger(Level level,ComponentMask component_mask,Level nested_allowed_level)284 Logger(Level level, ComponentMask component_mask, [[maybe_unused]] Level nested_allowed_level) 285 : level_(level), 286 component_mask_(component_mask) 287 #ifndef NDEBUG 288 , 289 nested_allowed_level_(nested_allowed_level) 290 #endif 291 { 292 } 293 294 virtual void LogLineInternal(Level level, Component component, const std::string &str) = 0; 295 296 /** 297 * Flushes all the output buffers of LogLineInternal to the output resources 298 * Sometimes nothinig shall be done, if LogLineInternal flushes everything by itself statelessl 299 */ 300 virtual void SyncOutputResource() = 0; 301 302 virtual ~Logger() = default; 303 304 static Logger *logger; 305 306 static os::memory::Mutex mutex; 307 308 static thread_local int nesting; 309 310 private: 311 Level level_; 312 ComponentMask component_mask_; 313 #ifndef NDEBUG 314 // These are utilized by Fast* logger types. 315 // For every thread, we trace events of staring shifting to a log (<<) and finishing doing it, 316 // incrementing a log invocation depth variable bound to a thread, or decrementing it correspondingly. 317 // Such variables we're doing as thread-local. 318 // All the LOGs with levels < nested_allowed_level_ are only allowed to have depth of log == 1 319 Level nested_allowed_level_; // Log level to suppress LOG triggering within << to another LOG 320 #endif 321 bool is_mlog_opened_ {true}; 322 323 NO_COPY_SEMANTIC(Logger); 324 NO_MOVE_SEMANTIC(Logger); 325 }; 326 327 static Logger::ComponentMask LoggerComponentMaskAll = ~Logger::ComponentMask(); 328 329 class FileLogger : public Logger { 330 protected: FileLogger(std::ofstream && stream,Level level,ComponentMask component_mask)331 FileLogger(std::ofstream &&stream, Level level, ComponentMask component_mask) 332 : Logger(level, component_mask), stream_(std::forward<std::ofstream>(stream)) 333 { 334 } 335 336 void LogLineInternal(Level level, Component component, const std::string &str) override; SyncOutputResource()337 void SyncOutputResource() override {} 338 339 ~FileLogger() override = default; 340 341 NO_COPY_SEMANTIC(FileLogger); 342 NO_MOVE_SEMANTIC(FileLogger); 343 344 private: 345 std::ofstream stream_; 346 347 friend Logger; 348 }; 349 350 class FastFileLogger : public Logger { 351 protected: 352 // Uses advanced Logger constructor, so we tell to suppress all nested messages below WARNING severity FastFileLogger(std::ofstream && stream,Level level,ComponentMask component_mask)353 FastFileLogger(std::ofstream &&stream, Level level, ComponentMask component_mask) 354 : Logger(level, component_mask, Logger::Level::WARNING), stream_(std::forward<std::ofstream>(stream)) 355 { 356 } 357 358 void LogLineInternal(Level level, Component component, const std::string &str) override; 359 void SyncOutputResource() override; 360 361 ~FastFileLogger() override = default; 362 363 NO_COPY_SEMANTIC(FastFileLogger); 364 NO_MOVE_SEMANTIC(FastFileLogger); 365 366 private: 367 std::ofstream stream_; 368 369 friend Logger; 370 }; 371 372 class StderrLogger : public Logger { 373 private: StderrLogger(Level level,ComponentMask component_mask)374 StderrLogger(Level level, ComponentMask component_mask) : Logger(level, component_mask) {} 375 376 void LogLineInternal(Level level, Component component, const std::string &str) override; SyncOutputResource()377 void SyncOutputResource() override {} 378 379 friend Logger; 380 381 ~StderrLogger() override = default; 382 383 NO_COPY_SEMANTIC(StderrLogger); 384 NO_MOVE_SEMANTIC(StderrLogger); 385 }; 386 387 class DummyLogger : public Logger { 388 private: DummyLogger(Level level,ComponentMask component_mask)389 DummyLogger(Level level, ComponentMask component_mask) : Logger(level, component_mask) {} 390 LogLineInternal(Level level,Component component,const std::string & str)391 void LogLineInternal([[maybe_unused]] Level level, [[maybe_unused]] Component component, 392 [[maybe_unused]] const std::string &str) override 393 { 394 } 395 SyncOutputResource()396 void SyncOutputResource() override {} 397 398 friend Logger; 399 400 ~DummyLogger() override = default; 401 402 NO_COPY_SEMANTIC(DummyLogger); 403 NO_MOVE_SEMANTIC(DummyLogger); 404 }; 405 406 class DummyStream { 407 public: 408 explicit operator bool() const 409 { 410 return true; 411 } 412 }; 413 414 template <class T> 415 DummyStream operator<<(DummyStream s, [[maybe_unused]] const T &v) 416 { 417 return s; 418 } 419 420 class LogOnceHelper { 421 public: IsFirstCall()422 bool IsFirstCall() 423 { 424 flag_ >>= 1U; 425 return flag_ != 0; 426 } 427 428 private: 429 uint8_t flag_ = 0x03; 430 }; 431 432 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 433 #define LOG_ONCE_HELPER() static LogOnceHelper MERGE_WORDS(log_once_helper, __LINE__); 434 435 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 436 #define LOG_ONCE(level, component) \ 437 LOG_ONCE_HELPER() \ 438 MERGE_WORDS(log_once_helper, __LINE__).IsFirstCall() && LOG(level, component) 439 440 #ifndef NDEBUG 441 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 442 #define _LOG_SUPPRESSION_CHECK(level, component) \ 443 !panda::Logger::IsMessageSuppressed(panda::Logger::Level::level, panda::Logger::Component::component) 444 #else 445 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 446 #define _LOG_SUPPRESSION_CHECK(level, component) true 447 #endif 448 449 // Explicit namespace is specified to allow using the logger out of panda namespace. 450 // For example, in the main function. 451 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 452 #define _LOG(level, component, p) \ 453 panda::Logger::IsLoggingOnOrAbort(panda::Logger::Level::level, panda::Logger::Component::component) && \ 454 _LOG_SUPPRESSION_CHECK(level, component) && \ 455 panda::Logger::Message(panda::Logger::Level::level, panda::Logger::Component::component, p).GetStream() 456 457 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 458 #define LOG(level, component) _LOG_##level(component, false) 459 460 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 461 #define GET_LOG_STREAM(level, component) \ 462 panda::Logger::Message(panda::Logger::Level::level, panda::Logger::Component::component, false).GetStream() 463 464 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 465 #define PLOG(level, component) _LOG_##level(component, true) 466 467 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 468 #define LOG_IF(cond, level, component) (cond) && LOG(level, component) 469 470 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 471 #define PLOG_IF(cond, level, component) (cond) && PLOG(level, component) 472 473 #ifndef NDEBUG 474 475 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 476 #define _LOG_DEBUG(component, p) _LOG(DEBUG, component, p) 477 478 #else 479 480 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 481 #define _LOG_DEBUG(component, p) false && panda::DummyStream() 482 483 #endif 484 485 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 486 #define _LOG_INFO(component, p) _LOG(INFO, component, p) 487 488 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 489 #define _LOG_WARNING(component, p) _LOG(WARNING, component, p) 490 491 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 492 #define _LOG_ERROR(component, p) _LOG(ERROR, component, p) 493 494 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 495 #define _LOG_FATAL(component, p) _LOG(FATAL, component, p) 496 497 } // namespace panda 498 499 #endif // PANDA_LIBPANDABASE_UTILS_LOGGER_H_ 500