]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/dolog.hh
rec: mention rust compiler in compiling docs
[thirdparty/pdns.git] / pdns / dolog.hh
1 /*
2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * In addition, for the avoidance of any doubt, permission is granted to
10 * link this program with OpenSSL and to (re)distribute the binaries
11 * produced as the result of such linking.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
22 #pragma once
23 #include <array>
24 #include <fstream>
25 #include <iomanip>
26 #include <iostream>
27 #include <optional>
28 #include <sstream>
29 #include "config.h"
30 #if !defined(RECURSOR)
31 #include <syslog.h>
32 #else
33 #include "logger.hh"
34 #endif // RECURSOR
35
36 /* This file is intended not to be metronome specific, and is simple example of C++2011
37 variadic templates in action.
38
39 The goal is rapid easy to use logging to console & syslog.
40
41 Usage:
42 string address="localhost";
43 vinfolog("Got TCP connection from %s", remote);
44 infolog("Bound to %s port %d", address, port);
45 warnlog("Query took %d milliseconds", 1232.4); // yes, %d
46 errlog("Unable to bind to %s: %s", ca.toStringWithPort(), strerr(errno));
47
48 Will log to stdout. Will syslog in any case with LOG_INFO,
49 LOG_WARNING, LOG_ERR respectively. If g_verbose=false, vinfolog is a noop.
50 More generically, dolog(someiostream, "Hello %s", stream) will log to someiostream
51
52 This will happily print a string to %d! Doesn't do further format processing.
53 */
54 template <typename O>
55 inline void dolog(O& outputStream, const char* str)
56 {
57 outputStream << str;
58 }
59
60 template <typename O, typename T, typename... Args>
61 void dolog(O& outputStream, const char* formatStr, T value, const Args&... args)
62 {
63 while (*formatStr) {
64 if (*formatStr == '%') {
65 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
66 if (*(formatStr + 1) == '%') {
67 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
68 ++formatStr;
69 }
70 else {
71 outputStream << value;
72 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
73 formatStr += 2;
74 dolog(outputStream, formatStr, args...);
75 return;
76 }
77 }
78 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
79 outputStream << *formatStr++;
80 }
81 }
82
83 #if !defined(RECURSOR)
84 // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
85 extern bool g_verbose;
86
87 #ifdef DNSDIST
88 namespace dnsdist::logging
89 {
90 class LoggingConfiguration
91 {
92 public:
93 enum class TimeFormat
94 {
95 Numeric,
96 ISO8601
97 };
98
99 static void setVerbose(bool value = true)
100 {
101 g_verbose = value;
102 }
103 static void setSyslog(bool value = true)
104 {
105 s_syslog = value;
106 }
107 static void setStructuredLogging(bool value = true, std::string levelPrefix = "")
108 {
109 s_structuredLogging = value;
110 if (value) {
111 s_structuredLevelPrefix = levelPrefix.empty() ? "prio" : std::move(levelPrefix);
112 }
113 }
114 static void setLogTimestamps(bool value = true)
115 {
116 s_logTimestamps = value;
117 }
118 static void setStructuredTimeFormat(TimeFormat format)
119 {
120 s_structuredTimeFormat = format;
121 }
122 static void setVerboseStream(std::ofstream&& stream)
123 {
124 s_verboseStream = std::move(stream);
125 }
126 static bool getVerbose()
127 {
128 return g_verbose;
129 }
130 static bool getSyslog()
131 {
132 return s_syslog;
133 }
134 static bool getLogTimestamps()
135 {
136 return s_logTimestamps;
137 }
138 static std::optional<std::ofstream>& getVerboseStream()
139 {
140 return s_verboseStream;
141 }
142 static bool getStructuredLogging()
143 {
144 return s_structuredLogging;
145 }
146 static const std::string& getStructuredLoggingLevelPrefix()
147 {
148 return s_structuredLevelPrefix;
149 }
150
151 static TimeFormat getStructuredLoggingTimeFormat()
152 {
153 return s_structuredTimeFormat;
154 }
155
156 private:
157 static std::optional<std::ofstream> s_verboseStream;
158 static std::string s_structuredLevelPrefix;
159 static TimeFormat s_structuredTimeFormat;
160 static bool s_structuredLogging;
161 static bool s_logTimestamps;
162 static bool s_syslog;
163 };
164
165 extern void logTime(std::ostream& stream);
166 }
167 #endif
168
169 inline void setSyslogFacility(int facility)
170 {
171 /* we always call openlog() right away at startup */
172 closelog();
173 openlog("dnsdist", LOG_PID | LOG_NDELAY, facility);
174 }
175
176 namespace
177 {
178 inline const char* syslogLevelToStr(int level)
179 {
180 static constexpr std::array levelStrs{
181 "Emergency",
182 "Alert",
183 "Critical",
184 "Error",
185 "Warning",
186 "Notice",
187 "Info",
188 "Debug"};
189 return levelStrs.at(level);
190 }
191 }
192
193 template <typename... Args>
194 void genlog(std::ostream& stream, [[maybe_unused]] int level, [[maybe_unused]] bool skipSyslog, const char* formatStr, const Args&... args)
195 {
196 std::ostringstream str;
197 dolog(str, formatStr, args...);
198
199 auto output = str.str();
200
201 #ifdef DNSDIST
202 if (!skipSyslog && dnsdist::logging::LoggingConfiguration::getSyslog()) {
203 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg): syslog is what it is
204 syslog(level, "%s", output.c_str());
205 }
206
207 if (dnsdist::logging::LoggingConfiguration::getLogTimestamps()) {
208 dnsdist::logging::logTime(stream);
209 }
210
211 if (dnsdist::logging::LoggingConfiguration::getStructuredLogging()) {
212 stream << dnsdist::logging::LoggingConfiguration::getStructuredLoggingLevelPrefix() << "=\"" << syslogLevelToStr(level) << "\" ";
213 stream << "msg=" << std::quoted(output) << std::endl;
214 }
215 else {
216 stream << output << std::endl;
217 }
218 #else
219 stream << output << std::endl;
220 #endif
221 }
222
223 template <typename... Args>
224 void verboselog(const char* formatStr, const Args&... args)
225 {
226 #ifdef DNSDIST
227 if (auto& stream = dnsdist::logging::LoggingConfiguration::getVerboseStream()) {
228 genlog(*stream, LOG_DEBUG, true, formatStr, args...);
229 }
230 else {
231 #endif /* DNSDIST */
232 genlog(std::cout, LOG_DEBUG, false, formatStr, args...);
233 #ifdef DNSDIST
234 }
235 #endif /* DNSDIST */
236 }
237
238 #define vinfolog \
239 if (g_verbose) \
240 verboselog
241
242 template <typename... Args>
243 void infolog(const char* formatStr, const Args&... args)
244 {
245 genlog(std::cout, LOG_INFO, false, formatStr, args...);
246 }
247
248 template <typename... Args>
249 void warnlog(const char* formatStr, const Args&... args)
250 {
251 genlog(std::cout, LOG_WARNING, false, formatStr, args...);
252 }
253
254 template <typename... Args>
255 void errlog(const char* formatStr, const Args&... args)
256 {
257 genlog(std::cout, LOG_ERR, false, formatStr, args...);
258 }
259
260 #else // RECURSOR
261 #define g_verbose 0
262 #define vinfolog \
263 if (g_verbose) \
264 infolog
265
266 template <typename... Args>
267 void infolog(const char* formatStr, const Args&... args)
268 {
269 g_log << Logger::Info;
270 dolog(g_log, formatStr, args...);
271 }
272
273 template <typename... Args>
274 void warnlog(const char* formatStr, const Args&... args)
275 {
276 g_log << Logger::Warning;
277 dolog(g_log, formatStr, args...);
278 }
279
280 template <typename... Args>
281 void errlog(const char* formatStr, const Args&... args)
282 {
283 g_log << Logger::Error;
284 dolog(g_log, formatStr, args...);
285 }
286
287 #endif