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)
8 * \file syslog_backend.cpp
9 * \author Andrey Semashev
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.
16 #ifndef BOOST_LOG_WITHOUT_SYSLOG
18 #include <boost/log/detail/config.hpp>
22 #include <boost/limits.hpp>
23 #include <boost/assert.hpp>
24 #include <boost/smart_ptr/weak_ptr.hpp>
25 #include <boost/smart_ptr/shared_ptr.hpp>
26 #include <boost/smart_ptr/make_shared_object.hpp>
27 #include <boost/throw_exception.hpp>
28 #if !defined(BOOST_LOG_NO_ASIO)
29 #include <boost/asio/buffer.hpp>
30 #include <boost/asio/socket_base.hpp>
31 #include <boost/asio/io_service.hpp>
32 #include <boost/asio/ip/udp.hpp>
33 #include <boost/asio/ip/address.hpp>
34 #include <boost/asio/ip/host_name.hpp>
36 #include <boost/system/error_code.hpp>
37 #include <boost/date_time/c_time.hpp>
38 #include <boost/log/sinks/syslog_backend.hpp>
39 #include <boost/log/sinks/syslog_constants.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>
47 #include "unique_ptr.hpp"
49 #ifdef BOOST_LOG_USE_NATIVE_SYSLOG
51 #endif // BOOST_LOG_USE_NATIVE_SYSLOG
53 #include <boost/log/detail/header.hpp>
57 BOOST_LOG_OPEN_NAMESPACE
63 //! The function constructs log record level from an integer
64 BOOST_LOG_API level
make_level(int lev
)
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
);
71 //! The function constructs log source facility from an integer
72 BOOST_LOG_API facility
make_facility(int fac
)
74 if ((static_cast< unsigned int >(fac
) & 7U) != 0
75 || static_cast< unsigned int >(fac
) > (23U * 8U))
77 BOOST_THROW_EXCEPTION(std::out_of_range("syslog facility code value is out of range"));
79 return static_cast< facility
>(fac
);
84 ////////////////////////////////////////////////////////////////////////////////
85 //! Syslog sink backend implementation
86 ////////////////////////////////////////////////////////////////////////////////
87 struct syslog_backend::implementation
89 #ifdef BOOST_LOG_USE_NATIVE_SYSLOG
91 #endif // BOOST_LOG_USE_NATIVE_SYSLOG
92 #if !defined(BOOST_LOG_NO_ASIO)
93 struct udp_socket_based
;
97 severity_mapper_type m_LevelMapper
;
99 //! Logging facility (portable or native, depending on the backend implementation)
100 const int m_Facility
;
103 explicit implementation(int facility
) :
107 //! Virtual destructor
108 virtual ~implementation() {}
110 //! The method sends the formatted message to the syslog host
111 virtual void send(syslog::level lev
, string_type
const& formatted_message
) = 0;
115 ////////////////////////////////////////////////////////////////////////////////
116 // Native syslog API support
117 ////////////////////////////////////////////////////////////////////////////////
119 #ifdef BOOST_LOG_USE_NATIVE_SYSLOG
121 BOOST_LOG_ANONYMOUS_NAMESPACE
{
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
>
128 class native_syslog_initializer
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
;
137 native_syslog_initializer(std::string
const& ident
, int facility
)
139 ::openlog((ident
.empty() ? static_cast< const char* >(NULL
) : ident
.c_str()), 0, facility
);
141 ~native_syslog_initializer()
146 static shared_ptr
< native_syslog_initializer
> get_instance(std::string
const& ident
, int facility
)
148 #if !defined(BOOST_LOG_NO_THREADS)
149 lock_guard
< mutex
> lock(mutex_holder::get());
151 static weak_ptr
< native_syslog_initializer
> instance
;
152 shared_ptr
< native_syslog_initializer
> p(instance
.lock());
155 p
= boost::make_shared
< native_syslog_initializer
>(ident
, facility
);
164 struct syslog_backend::implementation::native
:
165 public implementation
167 //! Reference to the syslog service initializer
168 const shared_ptr
< native_syslog_initializer
> m_pSyslogInitializer
;
171 native(syslog::facility
const& fac
, std::string
const& ident
) :
172 implementation(convert_facility(fac
)),
173 m_pSyslogInitializer(native_syslog_initializer::get_instance(ident
, this->m_Facility
))
177 //! The method sends the formatted message to the syslog host
178 void send(syslog::level lev
, string_type
const& formatted_message
)
183 case syslog::emergency
:
184 native_level
= LOG_EMERG
; break;
186 native_level
= LOG_ALERT
; break;
187 case syslog::critical
:
188 native_level
= LOG_CRIT
; break;
190 native_level
= LOG_ERR
; break;
191 case syslog::warning
:
192 native_level
= LOG_WARNING
; break;
194 native_level
= LOG_NOTICE
; break;
196 native_level
= LOG_DEBUG
; break;
198 native_level
= LOG_INFO
; break;
201 ::syslog(this->m_Facility
| native_level
, "%s", formatted_message
.c_str());
205 //! The function converts portable facility codes to the native codes
206 static int convert_facility(syslog::facility
const& fac
)
208 // POSIX does not specify anything except for LOG_USER and LOG_LOCAL*
210 #define LOG_KERN LOG_USER
213 #define LOG_DAEMON LOG_KERN
216 #define LOG_MAIL LOG_USER
219 #define LOG_AUTH LOG_DAEMON
222 #define LOG_SYSLOG LOG_DAEMON
225 #define LOG_LPR LOG_DAEMON
228 #define LOG_NEWS LOG_USER
231 #define LOG_UUCP LOG_USER
234 #define LOG_CRON LOG_DAEMON
237 #define LOG_AUTHPRIV LOG_AUTH
240 #define LOG_FTP LOG_DAEMON
243 static const int native_facilities
[24] =
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
];
280 #endif // BOOST_LOG_USE_NATIVE_SYSLOG
283 ////////////////////////////////////////////////////////////////////////////////
284 // Socket-based implementation
285 ////////////////////////////////////////////////////////////////////////////////
287 #if !defined(BOOST_LOG_NO_ASIO)
289 BOOST_LOG_ANONYMOUS_NAMESPACE
{
291 //! The shared UDP socket
292 struct syslog_udp_socket
295 //! The socket primitive
296 asio::ip::udp::socket m_Socket
;
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
) :
303 m_Socket
.open(protocol
);
304 m_Socket
.set_option(asio::socket_base::reuse_address(true));
305 m_Socket
.bind(local_address
);
307 //! The destructor closes the socket
310 boost::system::error_code ec
;
311 m_Socket
.shutdown(asio::socket_base::shutdown_both
, ec
);
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
);
319 syslog_udp_socket(syslog_udp_socket
const&);
320 syslog_udp_socket
& operator= (syslog_udp_socket
const&);
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
> >
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
;
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
;
336 #if !defined(BOOST_LOG_NO_THREADS)
337 //! A synchronization primitive to protect the host name resolver
339 //! The resolver is used to acquire connection endpoints
340 asio::ip::udp::resolver m_HostNameResolver
;
341 #endif // !defined(BOOST_LOG_NO_THREADS)
344 //! Default constructor
346 #if !defined(BOOST_LOG_NO_THREADS)
347 : m_HostNameResolver(m_IOService
)
348 #endif // !defined(BOOST_LOG_NO_THREADS)
350 boost::system::error_code err
;
351 m_LocalHostName
= asio::ip::host_name(err
);
353 //! Initializes the singleton instance
354 static void init_instance()
356 base_type::get_instance().reset(new syslog_udp_service());
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
)
364 std::time_t t
= std::time(NULL
);
366 std::tm
* time_stamp
= boost::date_time::c_time::localtime(&t
, &ts
);
368 // Month will have to be injected separately, as involving locale won't do here
369 static const char months
[12][4] =
371 "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
374 // The packet size is mandated in RFC3164, plus one for the terminating zero
376 int n
= boost::log::aux::snprintf
380 "<%d>%s %2d %02d:%02d:%02d %s %s",
382 months
[time_stamp
->tm_mon
],
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(asio::buffer(packet
, packet_size
), target
);
399 struct syslog_backend::implementation::udp_socket_based
:
400 public implementation
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
;
412 explicit udp_socket_based(syslog::facility
const& fac
, asio::ip::udp
const& protocol
) :
414 m_Protocol(protocol
),
415 m_pService(syslog_udp_service::get())
417 if (m_Protocol
== asio::ip::udp::v4())
419 m_TargetHost
= asio::ip::udp::endpoint(asio::ip::address_v4(0x7F000001), 514); // 127.0.0.1:514
424 asio::ip::address_v6::bytes_type addr
;
425 std::fill_n(addr
.data(), addr
.size() - 1, static_cast< unsigned char >(0));
426 addr
[addr
.size() - 1] = 1;
427 m_TargetHost
= asio::ip::udp::endpoint(asio::ip::address_v6(addr
), 514);
431 //! The method sends the formatted message to the syslog host
432 void send(syslog::level lev
, string_type
const& formatted_message
)
434 if (!m_pSocket
.get())
436 asio::ip::udp::endpoint any_local_address
;
437 m_pSocket
.reset(new syslog_udp_socket(m_pService
->m_IOService
, m_Protocol
, any_local_address
));
440 m_pSocket
->send_message(
441 this->m_Facility
| static_cast< int >(lev
),
442 m_pService
->m_LocalHostName
.c_str(),
444 formatted_message
.c_str());
448 #endif // !defined(BOOST_LOG_NO_ASIO)
450 ////////////////////////////////////////////////////////////////////////////////
451 // Sink backend implementation
452 ////////////////////////////////////////////////////////////////////////////////
453 BOOST_LOG_API
syslog_backend::syslog_backend()
455 construct(log::aux::empty_arg_list());
459 BOOST_LOG_API
syslog_backend::~syslog_backend()
464 //! The method installs the function object that maps application severity levels to Syslog levels
465 BOOST_LOG_API
void syslog_backend::set_severity_mapper(severity_mapper_type
const& mapper
)
467 m_pImpl
->m_LevelMapper
= mapper
;
470 //! The method writes the message to the sink
471 BOOST_LOG_API
void syslog_backend::consume(record_view
const& rec
, string_type
const& formatted_message
)
474 m_pImpl
->m_LevelMapper
.empty() ? syslog::info
: m_pImpl
->m_LevelMapper(rec
),
479 //! The method creates the backend implementation
480 BOOST_LOG_API
void syslog_backend::construct(syslog::facility fac
, syslog::impl_types use_impl
, ip_versions ip_version
, std::string
const& ident
)
482 #ifdef BOOST_LOG_USE_NATIVE_SYSLOG
483 if (use_impl
== syslog::native
)
485 typedef implementation::native native_impl
;
486 m_pImpl
= new native_impl(fac
, ident
);
489 #endif // BOOST_LOG_USE_NATIVE_SYSLOG
491 #if !defined(BOOST_LOG_NO_ASIO)
492 typedef implementation::udp_socket_based udp_socket_based_impl
;
496 m_pImpl
= new udp_socket_based_impl(fac
, asio::ip::udp::v4());
499 m_pImpl
= new udp_socket_based_impl(fac
, asio::ip::udp::v6());
502 BOOST_LOG_THROW_DESCR(setup_error
, "Incorrect IP version specified");
507 #if !defined(BOOST_LOG_NO_ASIO)
509 //! The method sets the local address which log records will be sent from.
510 BOOST_LOG_API
void syslog_backend::set_local_address(std::string
const& addr
, unsigned short port
)
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
))
516 char service_name
[std::numeric_limits
< int >::digits10
+ 3];
517 boost::log::aux::snprintf(service_name
, sizeof(service_name
), "%d", static_cast< int >(port
));
518 asio::ip::udp::resolver::query
q(
522 asio::ip::resolver_query_base::address_configured
| asio::ip::resolver_query_base::passive
);
523 asio::ip::udp::endpoint local_address
;
526 lock_guard
< mutex
> _(impl
->m_pService
->m_Mutex
);
527 local_address
= *impl
->m_pService
->m_HostNameResolver
.resolve(q
);
530 impl
->m_pSocket
.reset(new syslog_udp_socket(impl
->m_pService
->m_IOService
, impl
->m_Protocol
, local_address
));
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)
538 //! The method sets the local address which log records will be sent from.
539 BOOST_LOG_API
void syslog_backend::set_local_address(boost::asio::ip::address
const& addr
, unsigned short port
)
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
))
544 impl
->m_pSocket
.reset(new syslog_udp_socket(
545 impl
->m_pService
->m_IOService
, impl
->m_Protocol
, asio::ip::udp::endpoint(addr
, port
)));
549 //! The method sets the address of the remote host where log records will be sent to.
550 BOOST_LOG_API
void syslog_backend::set_target_address(std::string
const& addr
, unsigned short port
)
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
))
556 char service_name
[std::numeric_limits
< int >::digits10
+ 3];
557 boost::log::aux::snprintf(service_name
, sizeof(service_name
), "%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
;
562 lock_guard
< mutex
> _(impl
->m_pService
->m_Mutex
);
563 remote_address
= *impl
->m_pService
->m_HostNameResolver
.resolve(q
);
566 impl
->m_TargetHost
= remote_address
;
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)
574 //! The method sets the address of the remote host where log records will be sent to.
575 BOOST_LOG_API
void syslog_backend::set_target_address(boost::asio::ip::address
const& addr
, unsigned short port
)
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
))
580 impl
->m_TargetHost
= asio::ip::udp::endpoint(addr
, port
);
584 #endif // !defined(BOOST_LOG_NO_ASIO)
588 BOOST_LOG_CLOSE_NAMESPACE
// namespace log
592 #include <boost/log/detail/footer.hpp>
594 #endif // !defined(BOOST_LOG_WITHOUT_SYSLOG)