]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/logging.hh
Merge pull request #14020 from omoerbeek/rec-compiling-rust-dcos
[thirdparty/pdns.git] / pdns / logging.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
23 #pragma once
24
25 #include "config.h"
26
27 #ifdef RECURSOR
28
29 #include <map>
30 #include <memory>
31 #include <string>
32 #include <sstream>
33 #include <boost/optional.hpp>
34
35 #include "logr.hh"
36 #include "dnsname.hh"
37 #include "iputils.hh"
38
39 namespace Logging
40 {
41
42 struct Entry
43 {
44 boost::optional<std::string> name; // name parts joined with '.'
45 std::string message; // message as send to log call
46 boost::optional<std::string> error; // error if .Error() was called
47 struct timeval d_timestamp; // time of entry generation
48 std::map<std::string, std::string> values; // key-value pairs
49 size_t level; // level at which this was logged
50 Logr::Priority d_priority; // (syslog) priority)
51 };
52
53 // Warning: some meta-programming is going on. We define helper
54 // templates that can be used to see if specific string output
55 // functions are available. If so, we use those instead of << into an
56 // ostringstream. Note that this decision happpens compile time.
57 // Some hints taken from https://www.cppstories.com/2019/07/detect-overload-from-chars/
58 // (I could not get function templates with enabled_if<> to work in this case)
59 //
60 // Default: std::string(T) is not available
61 template <typename T, typename = void>
62 struct is_to_string_available : std::false_type
63 {
64 };
65
66 // If std::string(T) is available this template is used
67 template <typename T>
68 struct is_to_string_available<T, std::void_t<decltype(std::to_string(std::declval<T>()))>> : std::true_type
69 {
70 };
71
72 // Same mechanism for t.toLogString() and t.toStructuredLogString()
73 template <typename T, typename = void>
74 struct is_toLogString_available : std::false_type
75 {
76 };
77
78 template <typename T>
79 struct is_toLogString_available<T, std::void_t<decltype(std::declval<T>().toLogString())>> : std::true_type
80 {
81 };
82
83 template <typename T, typename = void>
84 struct is_toStructuredLogString_available : std::false_type
85 {
86 };
87
88 template <typename T>
89 struct is_toStructuredLogString_available<T, std::void_t<decltype(std::declval<T>().toStructuredLogString())>> : std::true_type
90 {
91 };
92
93 template <typename T, typename = void>
94 struct is_toString_available : std::false_type
95 {
96 };
97
98 template <typename T>
99 struct is_toString_available<T, std::void_t<decltype(std::declval<T>().toString())>> : std::true_type
100 {
101 };
102
103 const char* toTimestampStringMilli(const struct timeval& tval, std::array<char, 64>& buf, const std::string& format = "%s");
104
105 template <typename T>
106 struct Loggable : public Logr::Loggable
107 {
108 const T& _t;
109 Loggable(const T& v) :
110 _t(v)
111 {
112 }
113 std::string to_string() const
114 {
115 if constexpr (std::is_same_v<T, std::string>) {
116 return _t;
117 }
118 else if constexpr (is_toStructuredLogString_available<T>::value) {
119 return _t.toStructuredLogString();
120 }
121 else if constexpr (is_toLogString_available<T>::value) {
122 return _t.toLogString();
123 }
124 else if constexpr (is_toString_available<T>::value) {
125 return _t.toString();
126 }
127 else if constexpr (is_to_string_available<T>::value) {
128 return std::to_string(_t);
129 }
130 else {
131 std::ostringstream oss;
132 oss << _t;
133 return oss.str();
134 }
135 }
136 };
137
138 template <typename T>
139 struct IterLoggable : public Logr::Loggable
140 {
141 const T& _t1;
142 const T& _t2;
143 IterLoggable(const T& v1, const T& v2) :
144 _t1(v1), _t2(v2)
145 {
146 }
147 std::string to_string() const
148 {
149 std::ostringstream oss;
150 bool first = true;
151 for (auto i = _t1; i != _t2; i++) {
152 if (!first) {
153 oss << ' ';
154 }
155 else {
156 first = false;
157 }
158 oss << *i;
159 }
160 return oss.str();
161 }
162 };
163
164 typedef void (*EntryLogger)(const Entry&);
165
166 class Logger : public Logr::Logger, public std::enable_shared_from_this<const Logger>
167 {
168 public:
169 bool enabled(Logr::Priority) const override;
170
171 void info(const std::string& msg) const override;
172 void info(Logr::Priority, const std::string& msg) const override;
173 void error(int err, const std::string& msg) const override;
174 void error(const std::string& err, const std::string& msg) const override;
175 void error(Logr::Priority, int err, const std::string& msg) const override;
176 void error(Logr::Priority, const std::string& err, const std::string& msg) const override;
177
178 std::shared_ptr<Logr::Logger> v(size_t level) const override;
179 std::shared_ptr<Logr::Logger> withValues(const std::map<std::string, std::string>& values) const override;
180 virtual std::shared_ptr<Logr::Logger> withName(const std::string& name) const override;
181
182 static std::shared_ptr<Logger> create(EntryLogger callback);
183 static std::shared_ptr<Logger> create(EntryLogger callback, const std::string& name);
184
185 Logger(EntryLogger callback);
186 Logger(EntryLogger callback, boost::optional<std::string> name);
187 Logger(std::shared_ptr<const Logger> parent, boost::optional<std::string> name, size_t verbosity, size_t lvl, EntryLogger callback);
188 virtual ~Logger();
189
190 size_t getVerbosity() const;
191 void setVerbosity(size_t verbosity);
192
193 private:
194 void logMessage(const std::string& msg, boost::optional<const std::string> err) const;
195 void logMessage(const std::string& msg, Logr::Priority p, boost::optional<const std::string> err) const;
196 std::shared_ptr<const Logger> getptr() const;
197
198 std::shared_ptr<const Logger> _parent{nullptr};
199 EntryLogger _callback;
200 boost::optional<std::string> _name;
201 std::map<std::string, std::string> _values;
202 // current Logger's level. the higher the more verbose.
203 size_t _level{0};
204 // verbosity settings. messages with level higher's than verbosity won't appear
205 size_t _verbosity{0};
206 };
207 }
208
209 extern std::shared_ptr<Logging::Logger> g_slog;
210
211 // Prefer structured logging? Since Recursor 5.1.0, we always do. We keep a const, to allow for
212 // step-by-step removal of old style logging code (for recursor-only code). Note that code shared
213 // with auth still uses old-style, so the SLOG calls should remain for shared code.
214 constexpr bool g_slogStructured = true;
215
216 // A helper macro to switch between old-style logging and new-style (structured logging)
217 // A typical use:
218 //
219 // SLOG(g_log<<Logger::Warning<<"Unable to parse configuration file '"<<configname<<"'"<<endl,
220 // startupLog->error("No such file", "Unable to parse configuration file", "config_file", Logging::Loggable(configname));
221 //
222 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
223 #define SLOG(oldStyle, slogCall) \
224 do { \
225 slogCall; \
226 } while (0)
227
228 #else // No structured logging (e.g. auth)
229 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
230 #define SLOG(oldStyle, slogCall) \
231 do { \
232 oldStyle; \
233 } while (0)
234 #endif // RECURSOR