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