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