Coverage Report

Created: 2025-08-28 07:00

/src/pdns/pdns/dnsdistdist/dolog.hh
Line
Count
Source (jump to first uncovered line)
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 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
#ifdef DNSDIST
85
namespace dnsdist::logging
86
{
87
class LoggingConfiguration
88
{
89
public:
90
  enum class TimeFormat
91
  {
92
    Numeric,
93
    ISO8601
94
  };
95
96
  static void setSyslog(bool value = true)
97
0
  {
98
0
    s_syslog = value;
99
0
  }
100
  static void setStructuredLogging(bool value = true, std::string levelPrefix = "")
101
0
  {
102
0
    s_structuredLogging = value;
103
0
    if (value) {
104
0
      s_structuredLevelPrefix = levelPrefix.empty() ? "prio" : std::move(levelPrefix);
105
0
    }
106
0
  }
107
  static void setLogTimestamps(bool value = true)
108
0
  {
109
0
    s_logTimestamps = value;
110
0
  }
111
  static void setStructuredTimeFormat(TimeFormat format)
112
0
  {
113
0
    s_structuredTimeFormat = format;
114
0
  }
115
  static void setVerboseStream(std::ofstream&& stream)
116
0
  {
117
0
    s_verboseStream = std::move(stream);
118
0
  }
119
  static bool getSyslog()
120
0
  {
121
0
    return s_syslog;
122
0
  }
123
  static bool getLogTimestamps()
124
0
  {
125
0
    return s_logTimestamps;
126
0
  }
127
  static std::optional<std::ofstream>& getVerboseStream()
128
0
  {
129
0
    return s_verboseStream;
130
0
  }
131
  static bool getStructuredLogging()
132
0
  {
133
0
    return s_structuredLogging;
134
0
  }
135
  static const std::string& getStructuredLoggingLevelPrefix()
136
0
  {
137
0
    return s_structuredLevelPrefix;
138
0
  }
139
140
  static TimeFormat getStructuredLoggingTimeFormat()
141
0
  {
142
0
    return s_structuredTimeFormat;
143
0
  }
144
145
private:
146
  static std::optional<std::ofstream> s_verboseStream;
147
  static std::string s_structuredLevelPrefix;
148
  static TimeFormat s_structuredTimeFormat;
149
  static bool s_structuredLogging;
150
  static bool s_logTimestamps;
151
  static bool s_syslog;
152
};
153
154
extern void logTime(std::ostream& stream);
155
}
156
#endif
157
158
inline void setSyslogFacility(int facility)
159
0
{
160
0
  /* we always call openlog() right away at startup */
161
0
  closelog();
162
0
  openlog("dnsdist", LOG_PID | LOG_NDELAY, facility);
163
0
}
164
165
namespace
166
{
167
inline const char* syslogLevelToStr(int level)
168
0
{
169
0
  static constexpr std::array levelStrs{
170
0
    "Emergency",
171
0
    "Alert",
172
0
    "Critical",
173
0
    "Error",
174
0
    "Warning",
175
0
    "Notice",
176
0
    "Info",
177
0
    "Debug"};
178
0
  return levelStrs.at(level);
179
0
}
Unexecuted instantiation: dnsdist-cache.cc:(anonymous namespace)::syslogLevelToStr(int)
Unexecuted instantiation: dnsdist-ecs.cc:(anonymous namespace)::syslogLevelToStr(int)
180
}
181
182
template <typename... Args>
183
void genlog(std::ostream& stream, [[maybe_unused]] int level, [[maybe_unused]] bool skipSyslog, const char* formatStr, const Args&... args)
184
{
185
  std::ostringstream str;
186
  dolog(str, formatStr, args...);
187
188
  auto output = str.str();
189
190
#ifdef DNSDIST
191
  if (!skipSyslog && dnsdist::logging::LoggingConfiguration::getSyslog()) {
192
    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg): syslog is what it is
193
    syslog(level, "%s", output.c_str());
194
  }
195
196
  if (dnsdist::logging::LoggingConfiguration::getLogTimestamps()) {
197
    dnsdist::logging::logTime(stream);
198
  }
199
200
  if (dnsdist::logging::LoggingConfiguration::getStructuredLogging()) {
201
    stream << dnsdist::logging::LoggingConfiguration::getStructuredLoggingLevelPrefix() << "=\"" << syslogLevelToStr(level) << "\" ";
202
    stream << "msg=" << std::quoted(output) << std::endl;
203
  }
204
  else {
205
    stream << output << std::endl;
206
  }
207
#else
208
  stream << output << std::endl;
209
#endif
210
}
211
212
template <typename... Args>
213
void verboselog(const char* formatStr, const Args&... args)
214
{
215
#ifdef DNSDIST
216
  if (auto& stream = dnsdist::logging::LoggingConfiguration::getVerboseStream()) {
217
    genlog(*stream, LOG_DEBUG, true, formatStr, args...);
218
  }
219
  else {
220
#endif /* DNSDIST */
221
    genlog(std::cout, LOG_DEBUG, false, formatStr, args...);
222
#ifdef DNSDIST
223
  }
224
#endif /* DNSDIST */
225
}
226
227
#ifdef DNSDIST
228
#include "dnsdist-configuration.hh"
229
230
#define vinfolog                                                          \
231
  if (dnsdist::configuration::getCurrentRuntimeConfiguration().d_verbose) \
232
  verboselog
233
#else
234
#define vinfolog \
235
  infolog
236
#endif
237
238
template <typename... Args>
239
void infolog(const char* formatStr, const Args&... args)
240
{
241
  genlog(std::cout, LOG_INFO, false, formatStr, args...);
242
}
243
244
template <typename... Args>
245
void warnlog(const char* formatStr, const Args&... args)
246
{
247
  genlog(std::cout, LOG_WARNING, false, formatStr, args...);
248
}
249
250
template <typename... Args>
251
void errlog(const char* formatStr, const Args&... args)
252
{
253
  genlog(std::cout, LOG_ERR, false, formatStr, args...);
254
}
255
256
#else // RECURSOR
257
#define vinfolog \
258
  if (false)     \
259
  infolog
260
261
template <typename... Args>
262
void infolog(const char* formatStr, const Args&... args)
263
{
264
  g_log << Logger::Info;
265
  dolog(g_log, formatStr, args...);
266
}
267
268
template <typename... Args>
269
void warnlog(const char* formatStr, const Args&... args)
270
{
271
  g_log << Logger::Warning;
272
  dolog(g_log, formatStr, args...);
273
}
274
275
template <typename... Args>
276
void errlog(const char* formatStr, const Args&... args)
277
{
278
  g_log << Logger::Error;
279
  dolog(g_log, formatStr, args...);
280
}
281
282
#endif