1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. |
2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) |
3 | |
4 | #pragma once |
5 | |
6 | #include <spdlog/sinks/base_sink.h> |
7 | #include <spdlog/details/null_mutex.h> |
8 | #include <spdlog/details/synchronous_factory.h> |
9 | |
10 | #include <array> |
11 | #include <string> |
12 | #include <syslog.h> |
13 | |
14 | namespace spdlog { |
15 | namespace sinks { |
16 | /** |
17 | * Sink that write to syslog using the `syscall()` library call. |
18 | */ |
19 | template<typename Mutex> |
20 | class syslog_sink : public base_sink<Mutex> |
21 | { |
22 | |
23 | public: |
24 | syslog_sink(std::string ident, int syslog_option, int syslog_facility, bool enable_formatting) |
25 | : enable_formatting_{enable_formatting} |
26 | , syslog_levels_{{/* spdlog::level::trace */ LOG_DEBUG, |
27 | /* spdlog::level::debug */ LOG_DEBUG, |
28 | /* spdlog::level::info */ LOG_INFO, |
29 | /* spdlog::level::warn */ LOG_WARNING, |
30 | /* spdlog::level::err */ LOG_ERR, |
31 | /* spdlog::level::critical */ LOG_CRIT, |
32 | /* spdlog::level::off */ LOG_INFO}} |
33 | , ident_{std::move(ident)} |
34 | { |
35 | // set ident to be program name if empty |
36 | ::openlog(ident_.empty() ? nullptr : ident_.c_str(), syslog_option, syslog_facility); |
37 | } |
38 | |
39 | ~syslog_sink() override |
40 | { |
41 | ::closelog(); |
42 | } |
43 | |
44 | syslog_sink(const syslog_sink &) = delete; |
45 | syslog_sink &operator=(const syslog_sink &) = delete; |
46 | |
47 | protected: |
48 | void sink_it_(const details::log_msg &msg) override |
49 | { |
50 | string_view_t payload; |
51 | memory_buf_t formatted; |
52 | if (enable_formatting_) |
53 | { |
54 | base_sink<Mutex>::formatter_->format(msg, formatted); |
55 | payload = string_view_t(formatted.data(), formatted.size()); |
56 | } |
57 | else |
58 | { |
59 | payload = msg.payload; |
60 | } |
61 | |
62 | size_t length = payload.size(); |
63 | // limit to max int |
64 | if (length > static_cast<size_t>(std::numeric_limits<int>::max())) |
65 | { |
66 | length = static_cast<size_t>(std::numeric_limits<int>::max()); |
67 | } |
68 | |
69 | ::syslog(syslog_prio_from_level(msg), "%.*s" , static_cast<int>(length), payload.data()); |
70 | } |
71 | |
72 | void flush_() override {} |
73 | bool enable_formatting_ = false; |
74 | |
75 | private: |
76 | using levels_array = std::array<int, 7>; |
77 | levels_array syslog_levels_; |
78 | // must store the ident because the man says openlog might use the pointer as |
79 | // is and not a string copy |
80 | const std::string ident_; |
81 | |
82 | // |
83 | // Simply maps spdlog's log level to syslog priority level. |
84 | // |
85 | int syslog_prio_from_level(const details::log_msg &msg) const |
86 | { |
87 | return syslog_levels_.at(static_cast<levels_array::size_type>(msg.level)); |
88 | } |
89 | }; |
90 | |
91 | using syslog_sink_mt = syslog_sink<std::mutex>; |
92 | using syslog_sink_st = syslog_sink<details::null_mutex>; |
93 | } // namespace sinks |
94 | |
95 | // Create and register a syslog logger |
96 | template<typename Factory = spdlog::synchronous_factory> |
97 | inline std::shared_ptr<logger> syslog_logger_mt(const std::string &logger_name, const std::string &syslog_ident = "" , int syslog_option = 0, |
98 | int syslog_facility = LOG_USER, bool enable_formatting = false) |
99 | { |
100 | return Factory::template create<sinks::syslog_sink_mt>(logger_name, syslog_ident, syslog_option, syslog_facility, enable_formatting); |
101 | } |
102 | |
103 | template<typename Factory = spdlog::synchronous_factory> |
104 | inline std::shared_ptr<logger> syslog_logger_st(const std::string &logger_name, const std::string &syslog_ident = "" , int syslog_option = 0, |
105 | int syslog_facility = LOG_USER, bool enable_formatting = false) |
106 | { |
107 | return Factory::template create<sinks::syslog_sink_st>(logger_name, syslog_ident, syslog_option, syslog_facility, enable_formatting); |
108 | } |
109 | } // namespace spdlog |
110 | |