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