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