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 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 
26 #include <mutex>
27 
28 #include "logger.hh"
29 #include "misc.hh"
30 #ifndef RECURSOR
31 #include "statbag.hh"
32 extern StatBag S;
33 #endif
34 #include "namespaces.hh"
35 
36 thread_local Logger::PerThread Logger::t_perThread;
37 
getLogger()38 Logger& getLogger()
39 {
40   /* Since the Logger can be called very early, we need to make sure
41      that the relevant parts are initialized no matter what, which is tricky
42      because we can't easily control the initialization order, especially with
43      built-in backends.
44      t_perThread is thread_local, so it will be initialized when first accessed,
45      but we need to make sure that the object itself is initialized, and making
46      it a function-level static variable achieves that, because it will be
47      initialized the first time we enter this function at the very last.
48   */
49   static Logger log("", LOG_DAEMON);
50   return log;
51 }
52 
log(const string & msg,Urgency u)53 void Logger::log(const string &msg, Urgency u) noexcept
54 {
55 #ifndef RECURSOR
56   bool mustAccount(false);
57 #endif
58   if(u<=consoleUrgency) {
59     char buffer[50] = "";
60     if (d_timestamps) {
61       struct tm tm;
62       time_t t;
63       time(&t);
64       localtime_r(&t, &tm);
65       strftime(buffer,sizeof(buffer),"%b %d %H:%M:%S ", &tm);
66     }
67 
68     string prefix;
69     if (d_prefixed) {
70       switch(u) {
71         case All:
72           prefix = "[all] ";
73           break;
74         case Alert:
75           prefix = "[ALERT] ";
76           break;
77         case Critical:
78           prefix = "[CRITICAL] ";
79           break;
80         case Error:
81           prefix = "[ERROR] ";
82           break;
83         case Warning:
84           prefix = "[WARNING] ";
85           break;
86         case Notice:
87           prefix = "[NOTICE] ";
88           break;
89         case Info:
90           prefix = "[INFO] ";
91           break;
92         case Debug:
93           prefix = "[DEBUG] ";
94           break;
95         case None:
96           prefix = "[none] ";
97           break;
98       }
99     }
100 
101     static std::mutex m;
102     std::lock_guard<std::mutex> l(m); // the C++-2011 spec says we need this, and OSX actually does
103     clog << string(buffer) + prefix + msg <<endl;
104 #ifndef RECURSOR
105     mustAccount=true;
106 #endif
107   }
108   if( u <= d_loglevel && !d_disableSyslog ) {
109     syslog(u,"%s",msg.c_str());
110 #ifndef RECURSOR
111     mustAccount=true;
112 #endif
113   }
114 
115 #ifndef RECURSOR
116   if(mustAccount) {
117       try {
118         S.ringAccount("logmessages",msg);
119       }
120       catch (const runtime_error& e) {
121         cerr << e.what() << endl;
122       }
123   }
124 #endif
125 }
126 
setLoglevel(Urgency u)127 void Logger::setLoglevel( Urgency u )
128 {
129   d_loglevel = u;
130 }
131 
132 
toConsole(Urgency u)133 void Logger::toConsole(Urgency u)
134 {
135   consoleUrgency=u;
136 }
137 
open()138 void Logger::open()
139 {
140   if(opened)
141     closelog();
142   openlog(name.c_str(),flags,d_facility);
143   opened=true;
144 }
145 
setName(const string & _name)146 void Logger::setName(const string &_name)
147 {
148   name=_name;
149   open();
150 }
151 
Logger(string n,int facility)152 Logger::Logger(string n, int facility) :
153   name(std::move(n)), flags(LOG_PID|LOG_NDELAY), d_facility(facility), d_loglevel(Logger::None),
154   consoleUrgency(Error), opened(false), d_disableSyslog(false)
155 {
156   open();
157 
158 }
159 
operator <<(Urgency u)160 Logger& Logger::operator<<(Urgency u)
161 {
162   getPerThread().d_urgency=u;
163   return *this;
164 }
165 
getPerThread()166 Logger::PerThread& Logger::getPerThread()
167 {
168   return t_perThread;
169 }
170 
operator <<(const string & s)171 Logger& Logger::operator<<(const string &s)
172 {
173   PerThread& pt = getPerThread();
174   pt.d_output.append(s);
175   return *this;
176 }
177 
operator <<(const char * s)178 Logger& Logger::operator<<(const char *s)
179 {
180   *this<<string(s);
181   return *this;
182 }
183 
operator <<(ostream & (&)(ostream &))184 Logger& Logger::operator<<(ostream & (&)(ostream &))
185 {
186   PerThread& pt = getPerThread();
187 
188   log(pt.d_output, pt.d_urgency);
189   pt.d_output.clear();
190   pt.d_urgency=Info;
191   return *this;
192 }
193 
operator <<(const DNSName & d)194 Logger& Logger::operator<<(const DNSName &d)
195 {
196   *this<<d.toLogString();
197 
198   return *this;
199 }
200 
operator <<(const ComboAddress & ca)201 Logger& Logger::operator<<(const ComboAddress &ca)
202 {
203   *this<<ca.toLogString();
204   return *this;
205 }
206 
207