quill
SinkManager.h
1 
7 #pragma once
8 
9 #include "quill/core/Attributes.h"
10 #include "quill/core/QuillError.h"
11 #include "quill/core/Spinlock.h"
12 
13 #include <algorithm>
14 #include <cstdint>
15 #include <memory>
16 #include <string>
17 #include <type_traits>
18 #include <vector>
19 
20 QUILL_BEGIN_NAMESPACE
21 
23 class FileSink;
24 class Sink;
25 
26 namespace detail
27 {
29 {
30 private:
31  struct SinkInfo
32  {
33  explicit SinkInfo() = default;
34  SinkInfo(std::string sid, std::weak_ptr<Sink> sptr)
35  : sink_id(static_cast<std::string&&>(sid)), sink_ptr(static_cast<std::weak_ptr<Sink>&&>(sptr)) {};
36 
37  std::string sink_id;
38  std::weak_ptr<Sink> sink_ptr;
39  };
40 
41 public:
42  SinkManager(SinkManager const&) = delete;
43  SinkManager& operator=(SinkManager const&) = delete;
44 
45  /***/
46  QUILL_EXPORT static SinkManager& instance() noexcept
47  {
48  static SinkManager instance;
49  return instance;
50  }
51 
52  /***/
53  QUILL_NODISCARD std::shared_ptr<Sink> get_sink(std::string const& sink_name) const
54  {
55  // The sinks are used by the backend thread, so after their creation we want to avoid mutating their member variables.
56  LockGuard const lock{_spinlock};
57 
58  std::shared_ptr<Sink> sink = _find_sink(sink_name);
59 
60  if (QUILL_UNLIKELY(!sink))
61  {
62  QUILL_THROW(QuillError{"Sink with name \"" + sink_name + "\" does not exist"});
63  }
64 
65  return sink;
66  }
67 
68  /***/
69  template <typename TSink, typename... Args>
70  std::shared_ptr<Sink> create_or_get_sink(std::string const& sink_name, Args&&... args)
71  {
72  static_assert(std::is_base_of_v<Sink, TSink>, "TSink must derive from Sink");
73 
74  // The sinks are used by the backend thread, so after their creation we want to avoid mutating their member variables.
75  LockGuard const lock{_spinlock};
76 
77  std::shared_ptr<Sink> sink = _find_sink(sink_name);
78 
79  if (!sink)
80  {
81  if constexpr (std::disjunction_v<std::is_same<FileSink, TSink>, std::is_base_of<FileSink, TSink>>)
82  {
83  sink = std::make_shared<TSink>(sink_name, static_cast<Args&&>(args)...);
84  }
85  else
86  {
87  sink = std::make_shared<TSink>(static_cast<Args&&>(args)...);
88  }
89 
90  _insert_sink(sink_name, sink);
91  }
92 
93  return sink;
94  }
95 
96  /***/
97  uint32_t cleanup_unused_sinks()
98  {
99  // this needs to take a lock each time. The backend logging thread should be carefully call
100  // it only when needed
101  LockGuard const lock{_spinlock};
102 
103  uint32_t cnt{0};
104  for (auto it = _sinks.begin(); it != _sinks.end();)
105  {
106  if (it->sink_ptr.expired())
107  {
108  it = _sinks.erase(it);
109  ++cnt;
110  }
111  else
112  {
113  ++it;
114  }
115  }
116 
117  return cnt;
118  }
119 
120 private:
121  SinkManager() = default;
122  ~SinkManager() = default;
123 
124  /***/
125  void _insert_sink(std::string const& sink_name, std::shared_ptr<Sink> const& sink)
126  {
127  auto search_it =
128  std::lower_bound(_sinks.begin(), _sinks.end(), sink_name,
129  [](SinkInfo const& elem, std::string const& b) { return elem.sink_id < b; });
130 
131  _sinks.insert(search_it, SinkInfo{sink_name, sink});
132  }
133 
134  /***/
135  QUILL_NODISCARD std::shared_ptr<Sink> _find_sink(std::string const& target) const noexcept
136  {
137  std::shared_ptr<Sink> sink;
138 
139  auto search_it =
140  std::lower_bound(_sinks.begin(), _sinks.end(), target,
141  [](SinkInfo const& elem, std::string const& b) { return elem.sink_id < b; });
142 
143  if (search_it != std::end(_sinks) && search_it->sink_id == target)
144  {
145  sink = search_it->sink_ptr.lock();
146  }
147 
148  return sink;
149  }
150 
151 private:
152  std::vector<SinkInfo> _sinks;
153  mutable Spinlock _spinlock;
154 };
155 } // namespace detail
156 
157 QUILL_END_NAMESPACE
FileSink Writes the log messages to a file.
Definition: FileSink.h:214
Base class for sinks.
Definition: Sink.h:40
Setups a signal handler to handle fatal signals.
Definition: BackendManager.h:24
Definition: Spinlock.h:18
custom exception
Definition: QuillError.h:45
Definition: Spinlock.h:58
Definition: SinkManager.h:28