1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2020 The Bitcoin Core developers
3 // Distributed under the MIT software license, see the accompanying
4 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 
6 #ifndef BITCOIN_LOGGING_H
7 #define BITCOIN_LOGGING_H
8 
9 #include <fs.h>
10 #include <tinyformat.h>
11 #include <threadsafety.h>
12 #include <util/string.h>
13 
14 #include <atomic>
15 #include <cstdint>
16 #include <list>
17 #include <mutex>
18 #include <string>
19 #include <vector>
20 
21 static const bool DEFAULT_LOGTIMEMICROS = false;
22 static const bool DEFAULT_LOGIPS        = false;
23 static const bool DEFAULT_LOGTIMESTAMPS = true;
24 static const bool DEFAULT_LOGTHREADNAMES = false;
25 static const bool DEFAULT_LOGSOURCELOCATIONS = false;
26 extern const char * const DEFAULT_DEBUGLOGFILE;
27 
28 extern bool fLogIPs;
29 
30 struct LogCategory {
31     std::string category;
32     bool active;
33 };
34 
35 namespace BCLog {
36     enum LogFlags : uint32_t {
37         NONE        = 0,
38         NET         = (1 <<  0),
39         TOR         = (1 <<  1),
40         MEMPOOL     = (1 <<  2),
41         HTTP        = (1 <<  3),
42         BENCH       = (1 <<  4),
43         ZMQ         = (1 <<  5),
44         WALLETDB    = (1 <<  6),
45         RPC         = (1 <<  7),
46         ESTIMATEFEE = (1 <<  8),
47         ADDRMAN     = (1 <<  9),
48         SELECTCOINS = (1 << 10),
49         REINDEX     = (1 << 11),
50         CMPCTBLOCK  = (1 << 12),
51         RAND        = (1 << 13),
52         PRUNE       = (1 << 14),
53         PROXY       = (1 << 15),
54         MEMPOOLREJ  = (1 << 16),
55         LIBEVENT    = (1 << 17),
56         COINDB      = (1 << 18),
57         QT          = (1 << 19),
58         LEVELDB     = (1 << 20),
59         VALIDATION  = (1 << 21),
60         I2P         = (1 << 22),
61         IPC         = (1 << 23),
62         ALL         = ~(uint32_t)0,
63     };
64 
65     class Logger
66     {
67     private:
68         mutable StdMutex m_cs; // Can not use Mutex from sync.h because in debug mode it would cause a deadlock when a potential deadlock was detected
69 
70         FILE* m_fileout GUARDED_BY(m_cs) = nullptr;
71         std::list<std::string> m_msgs_before_open GUARDED_BY(m_cs);
72         bool m_buffering GUARDED_BY(m_cs) = true; //!< Buffer messages before logging can be started.
73 
74         /**
75          * m_started_new_line is a state variable that will suppress printing of
76          * the timestamp when multiple calls are made that don't end in a
77          * newline.
78          */
79         std::atomic_bool m_started_new_line{true};
80 
81         /** Log categories bitfield. */
82         std::atomic<uint32_t> m_categories{0};
83 
84         std::string LogTimestampStr(const std::string& str);
85 
86         /** Slots that connect to the print signal */
GUARDED_BY(m_cs)87         std::list<std::function<void(const std::string&)>> m_print_callbacks GUARDED_BY(m_cs) {};
88 
89     public:
90         bool m_print_to_console = false;
91         bool m_print_to_file = false;
92 
93         bool m_log_timestamps = DEFAULT_LOGTIMESTAMPS;
94         bool m_log_time_micros = DEFAULT_LOGTIMEMICROS;
95         bool m_log_threadnames = DEFAULT_LOGTHREADNAMES;
96         bool m_log_sourcelocations = DEFAULT_LOGSOURCELOCATIONS;
97 
98         fs::path m_file_path;
99         std::atomic<bool> m_reopen_file{false};
100 
101         /** Send a string to the log output */
102         void LogPrintStr(const std::string& str, const std::string& logging_function, const std::string& source_file, const int source_line);
103 
104         /** Returns whether logs will be written to any output */
Enabled()105         bool Enabled() const
106         {
107             StdLockGuard scoped_lock(m_cs);
108             return m_buffering || m_print_to_console || m_print_to_file || !m_print_callbacks.empty();
109         }
110 
111         /** Connect a slot to the print signal and return the connection */
PushBackCallback(std::function<void (const std::string &)> fun)112         std::list<std::function<void(const std::string&)>>::iterator PushBackCallback(std::function<void(const std::string&)> fun)
113         {
114             StdLockGuard scoped_lock(m_cs);
115             m_print_callbacks.push_back(std::move(fun));
116             return --m_print_callbacks.end();
117         }
118 
119         /** Delete a connection */
DeleteCallback(std::list<std::function<void (const std::string &)>>::iterator it)120         void DeleteCallback(std::list<std::function<void(const std::string&)>>::iterator it)
121         {
122             StdLockGuard scoped_lock(m_cs);
123             m_print_callbacks.erase(it);
124         }
125 
126         /** Start logging (and flush all buffered messages) */
127         bool StartLogging();
128         /** Only for testing */
129         void DisconnectTestLogger();
130 
131         void ShrinkDebugFile();
132 
GetCategoryMask()133         uint32_t GetCategoryMask() const { return m_categories.load(); }
134 
135         void EnableCategory(LogFlags flag);
136         bool EnableCategory(const std::string& str);
137         void DisableCategory(LogFlags flag);
138         bool DisableCategory(const std::string& str);
139 
140         bool WillLogCategory(LogFlags category) const;
141         /** Returns a vector of the log categories */
142         std::vector<LogCategory> LogCategoriesList() const;
143         /** Returns a string with the log categories */
LogCategoriesString()144         std::string LogCategoriesString() const
145         {
146             return Join(LogCategoriesList(), ", ", [&](const LogCategory& i) { return i.category; });
147         };
148 
149         bool DefaultShrinkDebugFile() const;
150     };
151 
152 } // namespace BCLog
153 
154 BCLog::Logger& LogInstance();
155 
156 /** Return true if log accepts specified category */
LogAcceptCategory(BCLog::LogFlags category)157 static inline bool LogAcceptCategory(BCLog::LogFlags category)
158 {
159     return LogInstance().WillLogCategory(category);
160 }
161 
162 /** Return true if str parses as a log category and set the flag */
163 bool GetLogCategory(BCLog::LogFlags& flag, const std::string& str);
164 
165 // Be conservative when using LogPrintf/error or other things which
166 // unconditionally log to debug.log! It should not be the case that an inbound
167 // peer can fill up a user's disk with debug.log entries.
168 
169 template <typename... Args>
LogPrintf_(const std::string & logging_function,const std::string & source_file,const int source_line,const char * fmt,const Args &...args)170 static inline void LogPrintf_(const std::string& logging_function, const std::string& source_file, const int source_line, const char* fmt, const Args&... args)
171 {
172     if (LogInstance().Enabled()) {
173         std::string log_msg;
174         try {
175             log_msg = tfm::format(fmt, args...);
176         } catch (tinyformat::format_error& fmterr) {
177             /* Original format string will have newline so don't add one here */
178             log_msg = "Error \"" + std::string(fmterr.what()) + "\" while formatting log message: " + fmt;
179         }
180         LogInstance().LogPrintStr(log_msg, logging_function, source_file, source_line);
181     }
182 }
183 
184 #define LogPrintf(...) LogPrintf_(__func__, __FILE__, __LINE__, __VA_ARGS__)
185 
186 // Use a macro instead of a function for conditional logging to prevent
187 // evaluating arguments when logging for the category is not enabled.
188 #define LogPrint(category, ...)              \
189     do {                                     \
190         if (LogAcceptCategory((category))) { \
191             LogPrintf(__VA_ARGS__);          \
192         }                                    \
193     } while (0)
194 
195 #endif // BITCOIN_LOGGING_H
196