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