• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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