Coverage Report

Created: 2025-06-13 06:28

/src/pdns/pdns/logger.cc
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
#include <ostream>
23
#ifdef HAVE_CONFIG_H
24
#include "config.h"
25
#endif
26
27
#include <iomanip>
28
#include <mutex>
29
30
#include "logger.hh"
31
#include "misc.hh"
32
#ifndef RECURSOR
33
#include "statbag.hh"
34
extern StatBag S;
35
#endif
36
#include "namespaces.hh"
37
38
thread_local Logger::PerThread Logger::t_perThread;
39
40
Logger& getLogger()
41
0
{
42
  /* Since the Logger can be called very early, we need to make sure
43
     that the relevant parts are initialized no matter what, which is tricky
44
     because we can't easily control the initialization order, especially with
45
     built-in backends.
46
     t_perThread is thread_local, so it will be initialized when first accessed,
47
     but we need to make sure that the object itself is initialized, and making
48
     it a function-level static variable achieves that, because it will be
49
     initialized the first time we enter this function at the very last.
50
  */
51
0
  static Logger log("", LOG_DAEMON);
52
0
  return log;
53
0
}
54
55
void Logger::log(const string& msg, Urgency u) noexcept
56
0
{
57
0
#ifndef RECURSOR
58
0
  bool mustAccount(false);
59
0
#endif
60
0
  if (u <= consoleUrgency) {
61
0
    std::array<char, 50> buffer{};
62
0
    buffer[0] = '\0';
63
0
    if (d_timestamps) {
64
0
      struct tm tm;
65
0
      time_t t;
66
0
      time(&t);
67
0
      localtime_r(&t, &tm);
68
0
      if (strftime(buffer.data(), buffer.size(), "%b %d %H:%M:%S ", &tm) == 0) {
69
0
        buffer[0] = '\0';
70
0
      }
71
0
    }
72
73
0
    string severity;
74
0
    if (d_prefixed) {
75
0
      switch (u) {
76
0
      case All:
77
0
        severity = "All";
78
0
        break;
79
0
      case Alert:
80
0
        severity = "Alert";
81
0
        break;
82
0
      case Critical:
83
0
        severity = "Critical";
84
0
        break;
85
0
      case Error:
86
0
        severity = "Error";
87
0
        break;
88
0
      case Warning:
89
0
        severity = "Warning";
90
0
        break;
91
0
      case Notice:
92
0
        severity = "Notice";
93
0
        break;
94
0
      case Info:
95
0
        severity = "Info";
96
0
        break;
97
0
      case Debug:
98
0
        severity = "Debug";
99
0
        break;
100
0
      case None:
101
0
        severity = "None";
102
0
        break;
103
0
      }
104
0
    }
105
106
0
    static std::mutex mutex;
107
0
    std::lock_guard<std::mutex> lock(mutex); // the C++-2011 spec says we need this, and OSX actually does
108
109
    // To avoid issuing multiple syscalls, we write the complete line to clog with a single << call.
110
    // For that we need a buffer allocated, we might want to use writev(2) one day to avoid that.
111
0
    ostringstream line;
112
0
    line << buffer.data();
113
0
    if (d_prefixed) {
114
0
      line << "msg=" << std::quoted(msg) << " prio=" << std::quoted(severity) << endl;
115
0
    }
116
0
    else {
117
0
      line << msg << endl;
118
0
    }
119
0
    clog << line.str() << std::flush;
120
0
#ifndef RECURSOR
121
0
    mustAccount = true;
122
0
#endif
123
0
  }
124
0
  if (u <= d_loglevel && !d_disableSyslog) {
125
0
    syslog(u, "%s", msg.c_str());
126
0
#ifndef RECURSOR
127
0
    mustAccount = true;
128
0
#endif
129
0
  }
130
131
0
#ifndef RECURSOR
132
0
  if (mustAccount) {
133
0
    try {
134
0
      S.ringAccount("logmessages", msg);
135
0
    }
136
0
    catch (const runtime_error& e) {
137
0
      cerr << e.what() << endl;
138
0
    }
139
0
  }
140
0
#endif
141
0
}
142
143
void Logger::setLoglevel(Urgency u)
144
0
{
145
0
  d_loglevel = u;
146
0
}
147
148
void Logger::toConsole(Urgency u)
149
0
{
150
0
  consoleUrgency = u;
151
0
}
152
153
void Logger::open()
154
0
{
155
0
  if (opened)
156
0
    closelog();
157
0
  openlog(name.c_str(), flags, d_facility);
158
0
  opened = true;
159
0
}
160
161
void Logger::setName(const string& _name)
162
0
{
163
0
  name = _name;
164
0
  open();
165
0
}
166
167
Logger::Logger(string n, int facility) :
168
0
  name(std::move(n)), flags(LOG_PID | LOG_NDELAY), d_facility(facility)
169
0
{
170
0
  open();
171
0
}
172
173
Logger& Logger::operator<<(Urgency u)
174
0
{
175
0
  getPerThread().d_urgency = u;
176
0
  return *this;
177
0
}
178
179
Logger::PerThread& Logger::getPerThread()
180
0
{
181
0
  return t_perThread;
182
0
}
183
184
Logger& Logger::operator<<(const string& s)
185
0
{
186
0
  PerThread& pt = getPerThread();
187
0
  pt.d_output.append(s);
188
0
  return *this;
189
0
}
190
191
Logger& Logger::operator<<(const char* s)
192
0
{
193
0
  *this << string(s);
194
0
  return *this;
195
0
}
196
197
Logger& Logger::operator<<(ostream& (&)(ostream&))
198
0
{
199
0
  PerThread& pt = getPerThread();
200
201
0
  log(pt.d_output, pt.d_urgency);
202
0
  pt.d_output.clear();
203
0
  pt.d_urgency = Info;
204
0
  return *this;
205
0
}
206
207
Logger& Logger::operator<<(const DNSName& d)
208
0
{
209
0
  *this << d.toLogString();
210
211
0
  return *this;
212
0
}
213
214
#if defined(PDNS_AUTH)
215
Logger& Logger::operator<<(const ZoneName& zone)
216
0
{
217
0
  *this << zone.toLogString();
218
219
0
  return *this;
220
0
}
221
#endif
222
223
Logger& Logger::operator<<(const ComboAddress& ca)
224
0
{
225
0
  *this << ca.toLogString();
226
0
  return *this;
227
0
}
228
229
Logger& Logger::operator<<(const SockaddrWrapper& sockaddr)
230
0
{
231
0
  *this << sockaddr.toString();
232
0
  return *this;
233
0
}
234
235
void addTraceTS(const timeval& start, ostringstream& str)
236
0
{
237
0
  const auto& content = str.str();
238
0
  if (content.empty() || content.back() == '\n') {
239
0
    timeval time{};
240
0
    gettimeofday(&time, nullptr);
241
0
    auto elapsed = time - start;
242
0
    auto diff = elapsed.tv_sec * 1000000 + static_cast<time_t>(elapsed.tv_usec);
243
0
    str << diff << ' ';
244
0
  }
245
0
}