1/*
2 * Copyright Andrey Semashev 2007 - 2015.
3 * Distributed under the Boost Software License, Version 1.0.
4 * (See accompanying file LICENSE_1_0.txt or copy at
5 * http://www.boost.org/LICENSE_1_0.txt)
6 */
7/*!
8 * \file syslog_backend.cpp
9 * \author Andrey Semashev
10 * \date 08.01.2008
11 *
12 * \brief This header is the Boost.Log library implementation, see the library documentation
13 * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html.
14 */
15
16#ifndef BOOST_LOG_WITHOUT_SYSLOG
17
18#include "windows_version.hpp"
19#include <boost/log/detail/config.hpp>
20#include <ctime>
21#include <algorithm>
22#include <stdexcept>
23#include <boost/limits.hpp>
24#include <boost/assert.hpp>
25#include <boost/smart_ptr/weak_ptr.hpp>
26#include <boost/smart_ptr/shared_ptr.hpp>
27#include <boost/smart_ptr/make_shared_object.hpp>
28#include <boost/throw_exception.hpp>
29#if !defined(BOOST_LOG_NO_ASIO)
30#include <boost/asio/buffer.hpp>
31#include <boost/asio/socket_base.hpp>
32#include <boost/asio/io_service.hpp>
33#include <boost/asio/ip/udp.hpp>
34#include <boost/asio/ip/address.hpp>
35#include <boost/asio/ip/host_name.hpp>
36#endif
37#include <boost/system/error_code.hpp>
38#include <boost/date_time/c_time.hpp>
39#include <boost/log/sinks/syslog_backend.hpp>
40#include <boost/log/detail/singleton.hpp>
41#include <boost/log/detail/snprintf.hpp>
42#include <boost/log/exceptions.hpp>
43#if !defined(BOOST_LOG_NO_THREADS)
44#include <boost/thread/locks.hpp>
45#include <boost/thread/mutex.hpp>
46#endif
47#include "unique_ptr.hpp"
48
49#ifdef BOOST_LOG_USE_NATIVE_SYSLOG
50#include <syslog.h>
51#endif // BOOST_LOG_USE_NATIVE_SYSLOG
52
53#include <boost/log/detail/header.hpp>
54
55namespace boost {
56
57BOOST_LOG_OPEN_NAMESPACE
58
59namespace sinks {
60
61namespace syslog {
62
63 //! The function constructs log record level from an integer
64 BOOST_LOG_API level make_level(int lev)
65 {
66 if (static_cast< unsigned int >(lev) >= 8)
67 BOOST_THROW_EXCEPTION(std::out_of_range("syslog level value is out of range"));
68 return static_cast< level >(lev);
69 }
70
71 //! The function constructs log source facility from an integer
72 BOOST_LOG_API facility make_facility(int fac)
73 {
74 if ((static_cast< unsigned int >(fac) & 7U) != 0
75 || static_cast< unsigned int >(fac) > (23U * 8U))
76 {
77 BOOST_THROW_EXCEPTION(std::out_of_range("syslog facility code value is out of range"));
78 }
79 return static_cast< facility >(fac);
80 }
81
82} // namespace syslog
83
84////////////////////////////////////////////////////////////////////////////////
85//! Syslog sink backend implementation
86////////////////////////////////////////////////////////////////////////////////
87struct syslog_backend::implementation
88{
89#ifdef BOOST_LOG_USE_NATIVE_SYSLOG
90 struct native;
91#endif // BOOST_LOG_USE_NATIVE_SYSLOG
92#if !defined(BOOST_LOG_NO_ASIO)
93 struct udp_socket_based;
94#endif
95
96 //! Level mapper
97 severity_mapper_type m_LevelMapper;
98
99 //! Logging facility (portable or native, depending on the backend implementation)
100 const int m_Facility;
101
102 //! Constructor
103 explicit implementation(int facility) :
104 m_Facility(facility)
105 {
106 }
107 //! Virtual destructor
108 virtual ~implementation() {}
109
110 //! The method sends the formatted message to the syslog host
111 virtual void send(syslog::level lev, string_type const& formatted_message) = 0;
112};
113
114
115////////////////////////////////////////////////////////////////////////////////
116// Native syslog API support
117////////////////////////////////////////////////////////////////////////////////
118
119#ifdef BOOST_LOG_USE_NATIVE_SYSLOG
120
121BOOST_LOG_ANONYMOUS_NAMESPACE {
122
123 //! Syslog service initializer (implemented as a weak singleton)
124#if !defined(BOOST_LOG_NO_THREADS)
125 class native_syslog_initializer :
126 private log::aux::lazy_singleton< native_syslog_initializer, mutex >
127#else
128 class native_syslog_initializer
129#endif
130 {
131#if !defined(BOOST_LOG_NO_THREADS)
132 friend class log::aux::lazy_singleton< native_syslog_initializer, mutex >;
133 typedef log::aux::lazy_singleton< native_syslog_initializer, mutex > mutex_holder;
134#endif
135
136 public:
137 native_syslog_initializer(std::string const& ident, int facility)
138 {
139 ::openlog(ident: (ident.empty() ? static_cast< const char* >(NULL) : ident.c_str()), option: 0, facility: facility);
140 }
141 ~native_syslog_initializer()
142 {
143 ::closelog();
144 }
145
146 static shared_ptr< native_syslog_initializer > get_instance(std::string const& ident, int facility)
147 {
148#if !defined(BOOST_LOG_NO_THREADS)
149 lock_guard< mutex > lock(mutex_holder::get());
150#endif
151 static weak_ptr< native_syslog_initializer > instance;
152 shared_ptr< native_syslog_initializer > p(instance.lock());
153 if (!p)
154 {
155 p = boost::make_shared< native_syslog_initializer >(args: ident, args&: facility);
156 instance = p;
157 }
158 return p;
159 }
160 };
161
162} // namespace
163
164struct syslog_backend::implementation::native :
165 public implementation
166{
167 //! Reference to the syslog service initializer
168 const shared_ptr< native_syslog_initializer > m_pSyslogInitializer;
169
170 //! Constructor
171 native(syslog::facility const& fac, std::string const& ident) :
172 implementation(convert_facility(fac)),
173 m_pSyslogInitializer(native_syslog_initializer::get_instance(ident, facility: this->m_Facility))
174 {
175 }
176
177 //! The method sends the formatted message to the syslog host
178 void send(syslog::level lev, string_type const& formatted_message)
179 {
180 int native_level;
181 switch (lev)
182 {
183 case syslog::emergency:
184 native_level = LOG_EMERG; break;
185 case syslog::alert:
186 native_level = LOG_ALERT; break;
187 case syslog::critical:
188 native_level = LOG_CRIT; break;
189 case syslog::error:
190 native_level = LOG_ERR; break;
191 case syslog::warning:
192 native_level = LOG_WARNING; break;
193 case syslog::notice:
194 native_level = LOG_NOTICE; break;
195 case syslog::debug:
196 native_level = LOG_DEBUG; break;
197 default:
198 native_level = LOG_INFO; break;
199 }
200
201 ::syslog(pri: this->m_Facility | native_level, fmt: "%s", formatted_message.c_str());
202 }
203
204private:
205 //! The function converts portable facility codes to the native codes
206 static int convert_facility(syslog::facility const& fac)
207 {
208 // POSIX does not specify anything except for LOG_USER and LOG_LOCAL*
209 #ifndef LOG_KERN
210 #define LOG_KERN LOG_USER
211 #endif
212 #ifndef LOG_DAEMON
213 #define LOG_DAEMON LOG_KERN
214 #endif
215 #ifndef LOG_MAIL
216 #define LOG_MAIL LOG_USER
217 #endif
218 #ifndef LOG_AUTH
219 #define LOG_AUTH LOG_DAEMON
220 #endif
221 #ifndef LOG_SYSLOG
222 #define LOG_SYSLOG LOG_DAEMON
223 #endif
224 #ifndef LOG_LPR
225 #define LOG_LPR LOG_DAEMON
226 #endif
227 #ifndef LOG_NEWS
228 #define LOG_NEWS LOG_USER
229 #endif
230 #ifndef LOG_UUCP
231 #define LOG_UUCP LOG_USER
232 #endif
233 #ifndef LOG_CRON
234 #define LOG_CRON LOG_DAEMON
235 #endif
236 #ifndef LOG_AUTHPRIV
237 #define LOG_AUTHPRIV LOG_AUTH
238 #endif
239 #ifndef LOG_FTP
240 #define LOG_FTP LOG_DAEMON
241 #endif
242
243 static const int native_facilities[24] =
244 {
245 LOG_KERN,
246 LOG_USER,
247 LOG_MAIL,
248 LOG_DAEMON,
249 LOG_AUTH,
250 LOG_SYSLOG,
251 LOG_LPR,
252 LOG_NEWS,
253 LOG_UUCP,
254 LOG_CRON,
255 LOG_AUTHPRIV,
256 LOG_FTP,
257
258 // reserved values
259 LOG_USER,
260 LOG_USER,
261 LOG_USER,
262 LOG_USER,
263
264 LOG_LOCAL0,
265 LOG_LOCAL1,
266 LOG_LOCAL2,
267 LOG_LOCAL3,
268 LOG_LOCAL4,
269 LOG_LOCAL5,
270 LOG_LOCAL6,
271 LOG_LOCAL7
272 };
273
274 std::size_t n = static_cast< unsigned int >(fac) / 8U;
275 BOOST_ASSERT(n < sizeof(native_facilities) / sizeof(*native_facilities));
276 return native_facilities[n];
277 }
278};
279
280#endif // BOOST_LOG_USE_NATIVE_SYSLOG
281
282
283////////////////////////////////////////////////////////////////////////////////
284// Socket-based implementation
285////////////////////////////////////////////////////////////////////////////////
286
287#if !defined(BOOST_LOG_NO_ASIO)
288
289BOOST_LOG_ANONYMOUS_NAMESPACE {
290
291 //! The shared UDP socket
292 struct syslog_udp_socket
293 {
294 private:
295 //! The socket primitive
296 asio::ip::udp::socket m_Socket;
297
298 public:
299 //! The constructor creates a socket bound to the specified local address and port
300 explicit syslog_udp_socket(asio::io_service& service, asio::ip::udp const& protocol, asio::ip::udp::endpoint const& local_address) :
301 m_Socket(service)
302 {
303 m_Socket.open(protocol);
304 m_Socket.set_option(asio::socket_base::reuse_address(true));
305 m_Socket.bind(endpoint: local_address);
306 }
307 //! The destructor closes the socket
308 ~syslog_udp_socket()
309 {
310 boost::system::error_code ec;
311 m_Socket.shutdown(what: asio::socket_base::shutdown_both, ec);
312 m_Socket.close(ec);
313 }
314
315 //! The method sends the syslog message to the specified endpoint
316 void send_message(int pri, const char* local_host_name, asio::ip::udp::endpoint const& target, const char* message);
317
318 private:
319 syslog_udp_socket(syslog_udp_socket const&);
320 syslog_udp_socket& operator= (syslog_udp_socket const&);
321 };
322
323 //! The class contains the UDP service for syslog sockets to function
324 class syslog_udp_service :
325 public log::aux::lazy_singleton< syslog_udp_service, shared_ptr< syslog_udp_service > >
326 {
327 friend class log::aux::lazy_singleton< syslog_udp_service, shared_ptr< syslog_udp_service > >;
328 typedef log::aux::lazy_singleton< syslog_udp_service, shared_ptr< syslog_udp_service > > base_type;
329
330 public:
331 //! The core IO service instance
332 asio::io_service m_IOService;
333 //! The local host name to put into log message
334 std::string m_LocalHostName;
335
336#if !defined(BOOST_LOG_NO_THREADS)
337 //! A synchronization primitive to protect the host name resolver
338 mutex m_Mutex;
339 //! The resolver is used to acquire connection endpoints
340 asio::ip::udp::resolver m_HostNameResolver;
341#endif // !defined(BOOST_LOG_NO_THREADS)
342
343 private:
344 //! Default constructor
345 syslog_udp_service()
346#if !defined(BOOST_LOG_NO_THREADS)
347 : m_HostNameResolver(m_IOService)
348#endif // !defined(BOOST_LOG_NO_THREADS)
349 {
350 boost::system::error_code err;
351 m_LocalHostName = asio::ip::host_name(ec&: err);
352 }
353 //! Initializes the singleton instance
354 static void init_instance()
355 {
356 base_type::get_instance().reset(p: new syslog_udp_service());
357 }
358 };
359
360 //! The method sends the syslog message to the specified endpoint
361 void syslog_udp_socket::send_message(
362 int pri, const char* local_host_name, asio::ip::udp::endpoint const& target, const char* message)
363 {
364 std::time_t t = std::time(NULL);
365 std::tm ts;
366 std::tm* time_stamp = boost::date_time::c_time::localtime(t: &t, result: &ts);
367
368 // Month will have to be injected separately, as involving locale won't do here
369 static const char months[12][4] =
370 {
371 "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
372 };
373
374 // The packet size is mandated in RFC3164, plus one for the terminating zero
375 char packet[1025];
376 int n = boost::log::aux::snprintf
377 (
378 s: packet,
379 maxlen: sizeof(packet),
380 format: "<%d> %s % 2d %02d:%02d:%02d %s %s",
381 pri,
382 months[time_stamp->tm_mon],
383 time_stamp->tm_mday,
384 time_stamp->tm_hour,
385 time_stamp->tm_min,
386 time_stamp->tm_sec,
387 local_host_name,
388 message
389 );
390 if (n > 0)
391 {
392 std::size_t packet_size = static_cast< std::size_t >(n) >= sizeof(packet) ? sizeof(packet) - 1u : static_cast< std::size_t >(n);
393 m_Socket.send_to(buffers: asio::buffer(data&: packet, max_size_in_bytes: packet_size), destination: target);
394 }
395 }
396
397} // namespace
398
399struct syslog_backend::implementation::udp_socket_based :
400 public implementation
401{
402 //! Protocol to be used
403 asio::ip::udp m_Protocol;
404 //! Pointer to the list of sockets
405 shared_ptr< syslog_udp_service > m_pService;
406 //! Pointer to the socket being used
407 log::aux::unique_ptr< syslog_udp_socket > m_pSocket;
408 //! The target host to send packets to
409 asio::ip::udp::endpoint m_TargetHost;
410
411 //! Constructor
412 explicit udp_socket_based(syslog::facility const& fac, asio::ip::udp const& protocol) :
413 implementation(fac),
414 m_Protocol(protocol),
415 m_pService(syslog_udp_service::get())
416 {
417 if (m_Protocol == asio::ip::udp::v4())
418 {
419 m_TargetHost = asio::ip::udp::endpoint(asio::ip::address_v4(0x7F000001), 514); // 127.0.0.1:514
420 }
421 else
422 {
423 // ::1, port 514
424 asio::ip::address_v6::bytes_type addr;
425 std::fill_n(first: addr.data(), n: addr.size() - 1, value: static_cast< unsigned char >(0));
426 addr[addr.size() - 1] = 1;
427 m_TargetHost = asio::ip::udp::endpoint(asio::ip::address_v6(addr), 514);
428 }
429 }
430
431 //! The method sends the formatted message to the syslog host
432 void send(syslog::level lev, string_type const& formatted_message)
433 {
434 if (!m_pSocket.get())
435 {
436 asio::ip::udp::endpoint any_local_address;
437 m_pSocket.reset(p: new syslog_udp_socket(m_pService->m_IOService, m_Protocol, any_local_address));
438 }
439
440 m_pSocket->send_message(
441 pri: this->m_Facility | static_cast< int >(lev),
442 local_host_name: m_pService->m_LocalHostName.c_str(),
443 target: m_TargetHost,
444 message: formatted_message.c_str());
445 }
446};
447
448#endif // !defined(BOOST_LOG_NO_ASIO)
449
450////////////////////////////////////////////////////////////////////////////////
451// Sink backend implementation
452////////////////////////////////////////////////////////////////////////////////
453BOOST_LOG_API syslog_backend::syslog_backend()
454{
455 construct(args: log::aux::empty_arg_list());
456}
457
458//! Destructor
459BOOST_LOG_API syslog_backend::~syslog_backend()
460{
461 delete m_pImpl;
462}
463
464//! The method installs the function object that maps application severity levels to Syslog levels
465BOOST_LOG_API void syslog_backend::set_severity_mapper(severity_mapper_type const& mapper)
466{
467 m_pImpl->m_LevelMapper = mapper;
468}
469
470//! The method writes the message to the sink
471BOOST_LOG_API void syslog_backend::consume(record_view const& rec, string_type const& formatted_message)
472{
473 m_pImpl->send(
474 lev: m_pImpl->m_LevelMapper.empty() ? syslog::info : m_pImpl->m_LevelMapper(rec),
475 formatted_message);
476}
477
478
479//! The method creates the backend implementation
480BOOST_LOG_API void syslog_backend::construct(syslog::facility fac, syslog::impl_types use_impl, ip_versions ip_version, std::string const& ident)
481{
482#ifdef BOOST_LOG_USE_NATIVE_SYSLOG
483 if (use_impl == syslog::native)
484 {
485 typedef implementation::native native_impl;
486 m_pImpl = new native_impl(fac, ident);
487 return;
488 }
489#endif // BOOST_LOG_USE_NATIVE_SYSLOG
490
491#if !defined(BOOST_LOG_NO_ASIO)
492 typedef implementation::udp_socket_based udp_socket_based_impl;
493 switch (ip_version)
494 {
495 case v4:
496 m_pImpl = new udp_socket_based_impl(fac, asio::ip::udp::v4());
497 break;
498 case v6:
499 m_pImpl = new udp_socket_based_impl(fac, asio::ip::udp::v6());
500 break;
501 default:
502 BOOST_LOG_THROW_DESCR(setup_error, "Incorrect IP version specified");
503 }
504#endif
505}
506
507#if !defined(BOOST_LOG_NO_ASIO)
508
509//! The method sets the local address which log records will be sent from.
510BOOST_LOG_API void syslog_backend::set_local_address(std::string const& addr, unsigned short port)
511{
512#if !defined(BOOST_LOG_NO_THREADS)
513 typedef implementation::udp_socket_based udp_socket_based_impl;
514 if (udp_socket_based_impl* impl = dynamic_cast< udp_socket_based_impl* >(m_pImpl))
515 {
516 char service_name[std::numeric_limits< int >::digits10 + 3];
517 boost::log::aux::snprintf(s: service_name, maxlen: sizeof(service_name), format: "%d", static_cast< int >(port));
518 asio::ip::udp::resolver::query q(
519 impl->m_Protocol,
520 addr,
521 service_name,
522 asio::ip::resolver_query_base::address_configured | asio::ip::resolver_query_base::passive);
523 asio::ip::udp::endpoint local_address;
524
525 {
526 lock_guard< mutex > _(impl->m_pService->m_Mutex);
527 local_address = *impl->m_pService->m_HostNameResolver.resolve(q);
528 }
529
530 impl->m_pSocket.reset(p: new syslog_udp_socket(impl->m_pService->m_IOService, impl->m_Protocol, local_address));
531 }
532#else
533 // Boost.ASIO requires threads for the host name resolver,
534 // so without threads we simply assume the string already contains IP address
535 set_local_address(boost::asio::ip::address::from_string(addr), port);
536#endif // !defined(BOOST_LOG_NO_THREADS)
537}
538//! The method sets the local address which log records will be sent from.
539BOOST_LOG_API void syslog_backend::set_local_address(boost::asio::ip::address const& addr, unsigned short port)
540{
541 typedef implementation::udp_socket_based udp_socket_based_impl;
542 if (udp_socket_based_impl* impl = dynamic_cast< udp_socket_based_impl* >(m_pImpl))
543 {
544 impl->m_pSocket.reset(p: new syslog_udp_socket(
545 impl->m_pService->m_IOService, impl->m_Protocol, asio::ip::udp::endpoint(addr, port)));
546 }
547}
548
549//! The method sets the address of the remote host where log records will be sent to.
550BOOST_LOG_API void syslog_backend::set_target_address(std::string const& addr, unsigned short port)
551{
552#if !defined(BOOST_LOG_NO_THREADS)
553 typedef implementation::udp_socket_based udp_socket_based_impl;
554 if (udp_socket_based_impl* impl = dynamic_cast< udp_socket_based_impl* >(m_pImpl))
555 {
556 char service_name[std::numeric_limits< int >::digits10 + 3];
557 boost::log::aux::snprintf(s: service_name, maxlen: sizeof(service_name), format: "%d", static_cast< int >(port));
558 asio::ip::udp::resolver::query q(impl->m_Protocol, addr, service_name, asio::ip::resolver_query_base::address_configured);
559 asio::ip::udp::endpoint remote_address;
560
561 {
562 lock_guard< mutex > _(impl->m_pService->m_Mutex);
563 remote_address = *impl->m_pService->m_HostNameResolver.resolve(q);
564 }
565
566 impl->m_TargetHost = remote_address;
567 }
568#else
569 // Boost.ASIO requires threads for the host name resolver,
570 // so without threads we simply assume the string already contains IP address
571 set_target_address(boost::asio::ip::address::from_string(addr), port);
572#endif // !defined(BOOST_LOG_NO_THREADS)
573}
574//! The method sets the address of the remote host where log records will be sent to.
575BOOST_LOG_API void syslog_backend::set_target_address(boost::asio::ip::address const& addr, unsigned short port)
576{
577 typedef implementation::udp_socket_based udp_socket_based_impl;
578 if (udp_socket_based_impl* impl = dynamic_cast< udp_socket_based_impl* >(m_pImpl))
579 {
580 impl->m_TargetHost = asio::ip::udp::endpoint(addr, port);
581 }
582}
583
584#endif // !defined(BOOST_LOG_NO_ASIO)
585
586} // namespace sinks
587
588BOOST_LOG_CLOSE_NAMESPACE // namespace log
589
590} // namespace boost
591
592#include <boost/log/detail/footer.hpp>
593
594#endif // !defined(BOOST_LOG_WITHOUT_SYSLOG)
595

source code of boost/libs/log/src/syslog_backend.cpp