]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/libs/log/src/syslog_backend.cpp
add subtree-ish sources for 12.0.3
[ceph.git] / ceph / src / boost / libs / log / src / syslog_backend.cpp
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 <boost/log/detail/config.hpp>
19 #include <ctime>
20 #include <algorithm>
21 #include <stdexcept>
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>
35 #endif
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>
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
55 namespace boost {
56
57 BOOST_LOG_OPEN_NAMESPACE
58
59 namespace sinks {
60
61 namespace 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 ////////////////////////////////////////////////////////////////////////////////
87 struct 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
121 BOOST_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.empty() ? static_cast< const char* >(NULL) : ident.c_str()), 0, 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 >(ident, facility);
156 instance = p;
157 }
158 return p;
159 }
160 };
161
162 } // namespace
163
164 struct 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, 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(this->m_Facility | native_level, "%s", formatted_message.c_str());
202 }
203
204 private:
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
289 BOOST_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(local_address);
306 }
307 //! The destructor closes the socket
308 ~syslog_udp_socket()
309 {
310 boost::system::error_code ec;
311 m_Socket.shutdown(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(err);
352 }
353 //! Initializes the singleton instance
354 static void init_instance()
355 {
356 base_type::get_instance().reset(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, &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 packet,
379 sizeof(packet),
380 "<%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(asio::buffer(packet, packet_size), target);
394 }
395 }
396
397 } // namespace
398
399 struct 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(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);
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(new syslog_udp_socket(m_pService->m_IOService, m_Protocol, any_local_address));
438 }
439
440 m_pSocket->send_message(
441 this->m_Facility | static_cast< int >(lev),
442 m_pService->m_LocalHostName.c_str(),
443 m_TargetHost,
444 formatted_message.c_str());
445 }
446 };
447
448 #endif // !defined(BOOST_LOG_NO_ASIO)
449
450 ////////////////////////////////////////////////////////////////////////////////
451 // Sink backend implementation
452 ////////////////////////////////////////////////////////////////////////////////
453 BOOST_LOG_API syslog_backend::syslog_backend()
454 {
455 construct(log::aux::empty_arg_list());
456 }
457
458 //! Destructor
459 BOOST_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
465 BOOST_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
471 BOOST_LOG_API void syslog_backend::consume(record_view const& rec, string_type const& formatted_message)
472 {
473 m_pImpl->send(
474 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
480 BOOST_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.
510 BOOST_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(service_name, sizeof(service_name), "%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(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.
539 BOOST_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(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.
550 BOOST_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(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;
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.
575 BOOST_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
588 BOOST_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)