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