]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/logger.cc
rec: mention rust compiler in compiling docs
[thirdparty/pdns.git] / pdns / logger.cc
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 {
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 static Logger log("", LOG_DAEMON);
52 return log;
53 }
54
55 void Logger::log(const string& msg, Urgency u) noexcept
56 {
57 #ifndef RECURSOR
58 bool mustAccount(false);
59 #endif
60 if (u <= consoleUrgency) {
61 std::array<char, 50> buffer{};
62 buffer[0] = '\0';
63 if (d_timestamps) {
64 struct tm tm;
65 time_t t;
66 time(&t);
67 localtime_r(&t, &tm);
68 if (strftime(buffer.data(), buffer.size(), "%b %d %H:%M:%S ", &tm) == 0) {
69 buffer[0] = '\0';
70 }
71 }
72
73 string severity;
74 if (d_prefixed) {
75 switch (u) {
76 case All:
77 severity = "All";
78 break;
79 case Alert:
80 severity = "Alert";
81 break;
82 case Critical:
83 severity = "Critical";
84 break;
85 case Error:
86 severity = "Error";
87 break;
88 case Warning:
89 severity = "Warning";
90 break;
91 case Notice:
92 severity = "Notice";
93 break;
94 case Info:
95 severity = "Info";
96 break;
97 case Debug:
98 severity = "Debug";
99 break;
100 case None:
101 severity = "None";
102 break;
103 }
104 }
105
106 static std::mutex mutex;
107 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 ostringstream line;
112 line << buffer.data();
113 if (d_prefixed) {
114 line << "msg=" << std::quoted(msg) << " prio=" << std::quoted(severity) << endl;
115 }
116 else {
117 line << msg << endl;
118 }
119 clog << line.str() << std::flush;
120 #ifndef RECURSOR
121 mustAccount = true;
122 #endif
123 }
124 if (u <= d_loglevel && !d_disableSyslog) {
125 syslog(u, "%s", msg.c_str());
126 #ifndef RECURSOR
127 mustAccount = true;
128 #endif
129 }
130
131 #ifndef RECURSOR
132 if (mustAccount) {
133 try {
134 S.ringAccount("logmessages", msg);
135 }
136 catch (const runtime_error& e) {
137 cerr << e.what() << endl;
138 }
139 }
140 #endif
141 }
142
143 void Logger::setLoglevel(Urgency u)
144 {
145 d_loglevel = u;
146 }
147
148 void Logger::toConsole(Urgency u)
149 {
150 consoleUrgency = u;
151 }
152
153 void Logger::open()
154 {
155 if (opened)
156 closelog();
157 openlog(name.c_str(), flags, d_facility);
158 opened = true;
159 }
160
161 void Logger::setName(const string& _name)
162 {
163 name = _name;
164 open();
165 }
166
167 Logger::Logger(string n, int facility) :
168 name(std::move(n)), flags(LOG_PID | LOG_NDELAY), d_facility(facility), d_loglevel(Logger::None), consoleUrgency(Error), opened(false), d_disableSyslog(false)
169 {
170 open();
171 }
172
173 Logger& Logger::operator<<(Urgency u)
174 {
175 getPerThread().d_urgency = u;
176 return *this;
177 }
178
179 Logger::PerThread& Logger::getPerThread()
180 {
181 return t_perThread;
182 }
183
184 Logger& Logger::operator<<(const string& s)
185 {
186 PerThread& pt = getPerThread();
187 pt.d_output.append(s);
188 return *this;
189 }
190
191 Logger& Logger::operator<<(const char* s)
192 {
193 *this << string(s);
194 return *this;
195 }
196
197 Logger& Logger::operator<<(ostream& (&)(ostream&))
198 {
199 PerThread& pt = getPerThread();
200
201 log(pt.d_output, pt.d_urgency);
202 pt.d_output.clear();
203 pt.d_urgency = Info;
204 return *this;
205 }
206
207 Logger& Logger::operator<<(const DNSName& d)
208 {
209 *this << d.toLogString();
210
211 return *this;
212 }
213
214 Logger& Logger::operator<<(const ComboAddress& ca)
215 {
216 *this << ca.toLogString();
217 return *this;
218 }
219
220 void addTraceTS(const timeval& start, ostringstream& str)
221 {
222 const auto& content = str.str();
223 if (content.empty() || content.back() == '\n') {
224 timeval time{};
225 gettimeofday(&time, nullptr);
226 auto elapsed = time - start;
227 auto diff = elapsed.tv_sec * 1000000 + static_cast<time_t>(elapsed.tv_usec);
228 str << diff << ' ';
229 }
230 }