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