Fix memory leak
[platform/core/appfw/pkgmgr-info.git] / src / logging.hh
1 // Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
2 // Use of this source code is governed by a apache 2.0 license that can be
3 // found in the LICENSE file.
4
5 #ifndef LOGGING_HH_
6 #define LOGGING_HH_
7
8 #include <dlog.h>
9
10 #ifndef PROJECT_TAG
11 #define PROJECT_TAG "PKGMGR_INFO"
12 #endif
13
14 #ifdef LOG
15 #undef LOG
16 #endif
17
18 #include <cassert>
19 #include <climits>
20 #include <cstdio>
21 #include <cstring>
22 #include <iomanip>
23 #include <iostream>
24 #include <memory>
25 #include <sstream>
26 #include <string>
27 #include <vector>
28
29 #ifndef __FILENAME__
30 #define __FILENAME__                                                           \
31     (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
32 #endif
33
34 namespace utils {
35
36 enum class LogLevel {
37   LOG_ERROR,
38   LOG_WARNING,
39   LOG_INFO,
40   LOG_DEBUG,
41 };
42
43 log_priority LogLevelToPriority(LogLevel level);
44
45 template<LogLevel> struct LogTag;
46 template<> struct LogTag<LogLevel::LOG_ERROR> {
47   static constexpr const char* value = "\033[1;31m| ERROR   |\033[0m";
48 };
49 template<> struct LogTag<LogLevel::LOG_WARNING> {
50   static constexpr const char* value = "\033[1;33m| WARNING |\033[0m";
51 };
52 template<> struct LogTag<LogLevel::LOG_INFO>  {
53   static constexpr const char* value = "\033[1;32m| INFO    |\033[0m";
54 };
55 template<> struct LogTag<LogLevel::LOG_DEBUG> {
56   static constexpr const char* value = "\033[0m| DEBUG   |\033[0m";
57 };
58
59 template <class charT, class traits = std::char_traits<charT>>
60 class StringStream : private std::basic_ostringstream<charT, traits> {
61  public:
62   using std::basic_ostringstream<charT, traits>::str;
63
64   template <class T>
65   StringStream&  operator<<(const T& value) {
66     static_cast<std::basic_ostringstream<charT, traits> &>(*this) << value;
67     return *this;
68   }
69 };
70
71 // Interface class for logging backends. The custom LogBackend which wants
72 // log using LOG() macro should be implement following interface.
73 class ILogBackend {
74  public:
75   virtual void WriteLog(LogLevel level, const std::string& tag,
76       const std::string& logstr) = 0;
77 };
78
79 class DLogBackend : public ILogBackend {
80  public:
81   void WriteLog(LogLevel level, const std::string& tag,
82       const std::string& logstr) override {
83     dlog_print(LogLevelToPriority(level), tag.c_str(), "%s",
84         Escape(logstr).c_str());
85   }
86
87  private:
88   // Since LogCatcher passes input to dlog_print(), the input which contains
89   // format string(such as %d, %n) can cause unexpected result.
90   // This is simple function to escape '%'.
91   // NOTE: Is there any gorgeous way instead of this?
92   std::string Escape(const std::string& str) const {
93     std::string escaped = std::string(str);
94     size_t start_pos = 0;
95     std::string from = "%";
96     std::string to = "%%";
97     while ((start_pos = escaped.find(from, start_pos)) != std::string::npos) {
98       escaped.replace(start_pos, from.length(), to);
99       start_pos += to.length();
100     }
101     return escaped;
102   }
103 };
104
105 class LogCore {
106  public:
107   // Do not call this function at destructor of global object
108   static LogCore& GetCore() {
109     static LogCore core;
110     return core;
111   }
112
113   void AddLogBackend(std::shared_ptr<ILogBackend> backend) {
114     backend_list_.emplace_back(backend);
115   }
116
117   void Log(LogLevel level, const std::string& tag, const std::string& log) {
118     for (auto backend : backend_list_)
119       backend->WriteLog(level, tag, log);
120   }
121
122  private:
123   LogCore() {
124     // add default dlog backend
125     AddLogBackend(std::shared_ptr<ILogBackend>(new DLogBackend()));
126   }
127   ~LogCore() = default;
128   LogCore(const LogCore&) = delete;
129   LogCore& operator=(const LogCore&) = delete;
130
131   std::vector<std::shared_ptr<ILogBackend>> backend_list_;
132 };
133
134 class LogCatcher {
135  public:
136   LogCatcher(LogLevel level, const char* tag)
137     : level_(level), tag_(tag) { }
138
139   void operator&(const StringStream<char>& str) const {
140     LogCore::GetCore().Log(level_, tag_, str.str());
141   }
142
143  private:
144   LogLevel level_;
145   std::string tag_;
146 };
147
148 }  // namespace utils
149
150
151 inline static const constexpr char* __tag_for_logging() {
152   return "";
153 }
154
155 inline static const constexpr char* __tag_for_project() {
156   return PROJECT_TAG;
157 }
158
159 // To be defined in class namespace if user want different log tag for given
160 // scope
161 #define SCOPE_LOG_TAG(TAG)                                                     \
162   inline static const constexpr char* __tag_for_logging() {                    \
163     return #TAG;                                                               \
164   }                                                                            \
165
166 // Simple logging macro of following usage:
167 //   LOG(LEVEL) << object_1 << object_2 << object_n;
168 //     where:
169 //       LEVEL = ERROR | WARNING | INFO | DEBUG
170 #define LOG(LEVEL)                                                             \
171     ::utils::LogCatcher(                                                       \
172       ::utils::LogLevel::LOG_ ## LEVEL, __tag_for_project())                   \
173       & ::utils::StringStream<char>()                                          \
174       << std::string(::utils::LogTag<::utils::LogLevel::LOG_ ## LEVEL>::value) \
175       << " " << std::setw(25) << std::left << __tag_for_logging()              \
176       << " : " << std::setw(36)                                                \
177       << (std::string(__FILENAME__) + ":" + std::to_string(__LINE__)).c_str()  \
178       << std::setw(0) << " : "                                                 \
179
180 #endif  // LOGGING_HH_