1 // Module: Log4CPLUS
2 // File: syslogappender.cxx
3 // Created: 6/2001
4 // Author: Tad E. Smith
5 //
6 // Copyright 2001-2013 Tad E. Smith
7 //
8 // Licensed under the Apache License, Version 2.0 (the "License");
9 // you may not use this file except in compliance with the License.
10 // You may obtain a copy of the License at
11 //
12 // http://www.apache.org/licenses/LICENSE-2.0
13 //
14 // Unless required by applicable law or agreed to in writing, software
15 // distributed under the License is distributed on an "AS IS" BASIS,
16 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 // See the License for the specific language governing permissions and
18 // limitations under the License.
19
20 #include <log4cplus/syslogappender.h>
21 #include <log4cplus/streams.h>
22 #include <log4cplus/helpers/loglog.h>
23 #include <log4cplus/helpers/property.h>
24 #include <log4cplus/helpers/stringhelper.h>
25 #include <log4cplus/spi/loggingevent.h>
26 #include <log4cplus/internal/internal.h>
27 #include <log4cplus/internal/env.h>
28 #include <log4cplus/thread/syncprims-pub-impl.h>
29 #include <cstring>
30
31 #if defined (LOG4CPLUS_HAVE_SYSLOG_H)
32 #include <syslog.h>
33
34 #else // LOG4CPLUS_HAVE_SYSLOG_H
35
36 // The following bits were derived from SUSv4 documentation and
37 // RFC5424 document.
38
39 // priority codes
40 #define LOG_EMERG 0
41 #define LOG_ALERT 1
42 #define LOG_CRIT 2
43 #define LOG_ERR 3
44 #define LOG_WARNING 4
45 #define LOG_NOTICE 5
46 #define LOG_INFO 6
47 #define LOG_DEBUG 7
48
49 // facility codes
50 #define LOG_KERN (0 << 3)
51 #define LOG_USER (1 << 3)
52 #define LOG_MAIL (2 << 3)
53 #define LOG_DAEMON (3 << 3)
54 #define LOG_AUTH (4 << 3)
55 #define LOG_SYSLOG (5 << 3)
56 #define LOG_LPR (6 << 3)
57 #define LOG_NEWS (7 << 3)
58 #define LOG_UUCP (8 << 3)
59 #define LOG_CRON (9 << 3)
60 #define LOG_AUTHPRIV (10 << 3)
61 #define LOG_FTP (11 << 3)
62 #define LOG_NTP (12 << 3)
63 #define LOG_SECURITY (13 << 3)
64 #define LOG_CONSOLE (14 << 3)
65 // (15 << 3) is missing here
66 #define LOG_LOCAL0 (16 << 3)
67 #define LOG_LOCAL1 (17 << 3)
68 #define LOG_LOCAL2 (18 << 3)
69 #define LOG_LOCAL3 (19 << 3)
70 #define LOG_LOCAL4 (20 << 3)
71 #define LOG_LOCAL5 (21 << 3)
72 #define LOG_LOCAL6 (22 << 3)
73 #define LOG_LOCAL7 (23 << 3)
74
75 #endif // LOG4CPLUS_HAVE_SYSLOG_H
76
77
78 namespace log4cplus
79 {
80
81 namespace
82 {
83
84 static
85 const char*
useIdent(const std::string & string)86 useIdent (const std::string& string)
87 {
88 if (string.empty ())
89 return 0;
90 else
91 return string.c_str ();
92 }
93
94
95 #ifdef LOG_USER
96 int const fallback_facility = LOG_USER;
97
98 #else
99 int const fallback_facility = 0;
100
101 #endif
102
103
104 static
105 int
parseFacility(const tstring & text)106 parseFacility (const tstring& text)
107 {
108 if (text.empty ())
109 return fallback_facility;
110 #ifdef LOG_AUTH
111 else if (text == LOG4CPLUS_TEXT ("auth"))
112 return LOG_AUTH;
113 #endif
114 #ifdef LOG_AUTHPRIV
115 else if (text == LOG4CPLUS_TEXT ("authpriv"))
116 return LOG_AUTHPRIV;
117 #endif
118 #ifdef LOG_CONSOLE
119 else if (text == LOG4CPLUS_TEXT ("console"))
120 return LOG_CONSOLE;
121 #endif
122 #ifdef LOG_CRON
123 else if (text == LOG4CPLUS_TEXT ("cron"))
124 return LOG_CRON;
125 #endif
126 #ifdef LOG_DAEMON
127 else if (text == LOG4CPLUS_TEXT ("daemon"))
128 return LOG_DAEMON;
129 #endif
130 #ifdef LOG_FTP
131 else if (text == LOG4CPLUS_TEXT ("ftp"))
132 return LOG_FTP;
133 #endif
134 #ifdef LOG_KERN
135 else if (text == LOG4CPLUS_TEXT ("kern"))
136 return LOG_KERN;
137 #endif
138 #ifdef LOG_LOCAL0
139 else if (text == LOG4CPLUS_TEXT ("local0"))
140 return LOG_LOCAL0;
141 #endif
142 #ifdef LOG_LOCAL1
143 else if (text == LOG4CPLUS_TEXT ("local1"))
144 return LOG_LOCAL1;
145 #endif
146 #ifdef LOG_LOCAL2
147 else if (text == LOG4CPLUS_TEXT ("local2"))
148 return LOG_LOCAL2;
149 #endif
150 #ifdef LOG_LOCAL3
151 else if (text == LOG4CPLUS_TEXT ("local3"))
152 return LOG_LOCAL3;
153 #endif
154 #ifdef LOG_LOCAL4
155 else if (text == LOG4CPLUS_TEXT ("local4"))
156 return LOG_LOCAL4;
157 #endif
158 #ifdef LOG_LOCAL5
159 else if (text == LOG4CPLUS_TEXT ("local5"))
160 return LOG_LOCAL5;
161 #endif
162 #ifdef LOG_LOCAL6
163 else if (text == LOG4CPLUS_TEXT ("local6"))
164 return LOG_LOCAL6;
165 #endif
166 #ifdef LOG_LOCAL7
167 else if (text == LOG4CPLUS_TEXT ("local7"))
168 return LOG_LOCAL7;
169 #endif
170 #ifdef LOG_LPR
171 else if (text == LOG4CPLUS_TEXT ("lpr"))
172 return LOG_LPR;
173 #endif
174 #ifdef LOG_MAIL
175 else if (text == LOG4CPLUS_TEXT ("mail"))
176 return LOG_MAIL;
177 #endif
178 #ifdef LOG_NEWS
179 else if (text == LOG4CPLUS_TEXT ("news"))
180 return LOG_NEWS;
181 #endif
182 #ifdef LOG_NTP
183 else if (text == LOG4CPLUS_TEXT ("ntp"))
184 return LOG_NTP;
185 #endif
186 #ifdef LOG_SECURITY
187 else if (text == LOG4CPLUS_TEXT ("security"))
188 return LOG_SECURITY;
189 #endif
190 #ifdef LOG_SYSLOG
191 else if (text == LOG4CPLUS_TEXT ("syslog"))
192 return LOG_SYSLOG;
193 #endif
194 #ifdef LOG_USER
195 else if (text == LOG4CPLUS_TEXT ("user"))
196 return LOG_USER;
197 #endif
198 #ifdef LOG_UUCP
199 else if (text == LOG4CPLUS_TEXT ("uucp"))
200 return LOG_UUCP;
201 #endif
202 else
203 {
204 // Unknown facility.
205 tstring msg (LOG4CPLUS_TEXT ("Unknown syslog facility: "));
206 msg += text;
207 helpers::getLogLog ().error (msg);
208
209 return fallback_facility;
210 }
211 }
212
213
214 } // namespace
215
216
217 ///////////////////////////////////////////////////////////////////////////////
218 // SysLogAppender ctors and dtor
219 ///////////////////////////////////////////////////////////////////////////////
220
221 #if defined (LOG4CPLUS_HAVE_SYSLOG_H)
SysLogAppender(const tstring & id)222 SysLogAppender::SysLogAppender(const tstring& id)
223 : ident(id)
224 , facility (0)
225 , appendFunc (&SysLogAppender::appendLocal)
226 , port (0)
227 // Store std::string form of ident as member of SysLogAppender so
228 // the address of the c_str() result remains stable for openlog &
229 // co to use even if we use wstrings.
230 , identStr(LOG4CPLUS_TSTRING_TO_STRING (id) )
231 , hostname (helpers::getHostname (true))
232 {
233 ::openlog(useIdent(identStr), 0, 0);
234 }
235
236 #endif
237
238
SysLogAppender(const helpers::Properties & properties)239 SysLogAppender::SysLogAppender(const helpers::Properties & properties)
240 : Appender(properties)
241 , facility (0)
242 , appendFunc (0)
243 , port (0)
244 , hostname (helpers::getHostname (true))
245 {
246 ident = properties.getProperty( LOG4CPLUS_TEXT("ident") );
247 facility = parseFacility (
248 helpers::toLower (
249 properties.getProperty (LOG4CPLUS_TEXT ("facility"))));
250 identStr = LOG4CPLUS_TSTRING_TO_STRING (ident);
251
252 host = properties.getProperty (LOG4CPLUS_TEXT ("host"));
253 if (host.empty ())
254 {
255 #if defined (LOG4CPLUS_HAVE_SYSLOG_H)
256 appendFunc = &SysLogAppender::appendLocal;
257 ::openlog(useIdent(identStr), 0, 0);
258
259 #else
260 helpers::getLogLog ().error (
261 LOG4CPLUS_TEXT ("SysLogAppender")
262 LOG4CPLUS_TEXT ("- local syslog not available"), true);
263
264 #endif
265 }
266 else
267 {
268 if (! properties.getInt (port, LOG4CPLUS_TEXT ("port")))
269 port = 514;
270
271 appendFunc = &SysLogAppender::appendRemote;
272 syslogSocket = helpers::Socket (host, port, true);
273 }
274 }
275
276
SysLogAppender(const tstring & id,const tstring & h,int p,const tstring & f)277 SysLogAppender::SysLogAppender(const tstring& id, const tstring & h,
278 int p, const tstring & f)
279 : ident (id)
280 , facility (parseFacility (helpers::toLower (f)))
281 , appendFunc (&SysLogAppender::appendRemote)
282 , host (h)
283 , port (p)
284 , syslogSocket (host, port, true)
285 // Store std::string form of ident as member of SysLogAppender so
286 // the address of the c_str() result remains stable for openlog &
287 // co to use even if we use wstrings.
288 , identStr(LOG4CPLUS_TSTRING_TO_STRING (id) )
289 , hostname (helpers::getHostname (true))
290 { }
291
292
~SysLogAppender()293 SysLogAppender::~SysLogAppender()
294 {
295 destructorImpl();
296 }
297
298
299
300 ///////////////////////////////////////////////////////////////////////////////
301 // SysLogAppender public methods
302 ///////////////////////////////////////////////////////////////////////////////
303
304 void
close()305 SysLogAppender::close()
306 {
307 helpers::getLogLog().debug(
308 LOG4CPLUS_TEXT("Entering SysLogAppender::close()..."));
309 thread::MutexGuard guard (access_mutex);
310
311 if (host.empty ())
312 {
313 #if defined (LOG4CPLUS_HAVE_SYSLOG_H)
314 ::closelog();
315 #endif
316 }
317 else
318 syslogSocket.close ();
319
320 closed = true;
321 }
322
323
324
325 ///////////////////////////////////////////////////////////////////////////////
326 // SysLogAppender protected methods
327 ///////////////////////////////////////////////////////////////////////////////
328
329 int
getSysLogLevel(const LogLevel & ll) const330 SysLogAppender::getSysLogLevel(const LogLevel& ll) const
331 {
332 if(ll < INFO_LOG_LEVEL /* || ll < DEBUG_LOG_LEVEL*/) {
333 return LOG_DEBUG;
334 }
335 else if(ll < WARN_LOG_LEVEL) {
336 return LOG_INFO;
337 }
338 else if(ll < ERROR_LOG_LEVEL) {
339 return LOG_WARNING;
340 }
341 else if(ll < FATAL_LOG_LEVEL) {
342 return LOG_ERR;
343 }
344 else if(ll == FATAL_LOG_LEVEL) {
345 return LOG_CRIT;
346 }
347
348 return LOG_ALERT; // ll > FATAL_LOG_LEVEL
349 }
350
351
352 // This method does not need to be locked since it is called by
353 // doAppend() which performs the locking
354 void
append(const spi::InternalLoggingEvent & event)355 SysLogAppender::append(const spi::InternalLoggingEvent& event)
356 {
357 (this->*appendFunc) (event);
358 }
359
360
361 #if defined (LOG4CPLUS_HAVE_SYSLOG_H)
362 void
appendLocal(const spi::InternalLoggingEvent & event)363 SysLogAppender::appendLocal(const spi::InternalLoggingEvent& event)
364 {
365 int const level = getSysLogLevel(event.getLogLevel());
366 internal::appender_sratch_pad & appender_sp = internal::get_appender_sp ();
367 detail::clear_tostringstream (appender_sp.oss);
368 layout->formatAndAppend(appender_sp.oss, event);
369 appender_sp.str = appender_sp.oss.str ();
370 ::syslog(facility | level, "%s",
371 LOG4CPLUS_TSTRING_TO_STRING(appender_sp.str).c_str());
372 }
373
374 #endif
375
376
377 tstring const SysLogAppender::remoteTimeFormat (
378 LOG4CPLUS_TEXT ("%Y-%m-%dT%H:%M:%S.%qZ"));
379
380
381 void
appendRemote(const spi::InternalLoggingEvent & event)382 SysLogAppender::appendRemote(const spi::InternalLoggingEvent& event)
383 {
384 int const level = getSysLogLevel(event.getLogLevel());
385 internal::appender_sratch_pad & appender_sp = internal::get_appender_sp ();
386 detail::clear_tostringstream (appender_sp.oss);
387
388 appender_sp.oss
389 // PRI
390 << LOG4CPLUS_TEXT ('<') << (level | facility) << LOG4CPLUS_TEXT ('>')
391 // VERSION
392 << 1
393 // TIMESTAMP
394 << LOG4CPLUS_TEXT (' ')
395 << event.getTimestamp ().getFormattedTime (remoteTimeFormat, true)
396 // HOSTNAME
397 << LOG4CPLUS_TEXT (' ') << hostname
398 // APP-NAME
399 << LOG4CPLUS_TEXT (' ') << ident
400 // PROCID
401 << LOG4CPLUS_TEXT (' ') << internal::get_process_id ()
402 // MSGID
403 << LOG4CPLUS_TEXT (' ') << event.getLoggerName ()
404 // STRUCTURED-DATA
405 // no structured data, it could be whole MDC
406 << LOG4CPLUS_TEXT (" - ");
407
408 // MSG
409 layout->formatAndAppend (appender_sp.oss, event);
410
411 LOG4CPLUS_TSTRING_TO_STRING (appender_sp.oss.str ())
412 .swap (appender_sp.chstr);
413
414 bool ret = syslogSocket.write (appender_sp.chstr);
415 if (! ret)
416 {
417 helpers::getLogLog ().warn (
418 LOG4CPLUS_TEXT ("SysLogAppender::appendRemote")
419 LOG4CPLUS_TEXT ("- socket write failed"));
420 syslogSocket = helpers::Socket (host, port, true);
421 }
422 }
423
424
425 } // namespace log4cplus
426