quill
Sink.h
1 
7 #pragma once
8 
9 #include "quill/core/Attributes.h"
10 #include "quill/core/LogLevel.h"
11 #include "quill/core/PatternFormatterOptions.h"
12 #include "quill/core/QuillError.h"
13 #include "quill/core/Spinlock.h"
14 #include "quill/filters/Filter.h"
15 
16 #include <algorithm>
17 #include <atomic>
18 #include <cstdint>
19 #include <memory>
20 #include <optional>
21 #include <string>
22 #include <string_view>
23 #include <utility>
24 #include <vector>
25 
26 QUILL_BEGIN_NAMESPACE
27 
28 namespace detail
29 {
30 class BackendWorker;
31 }
32 
34 QUILL_BEGIN_EXPORT
35 
36 class MacroMetadata;
37 class MetricMetadata;
38 class PatternFormatter;
39 
40 QUILL_END_EXPORT
41 
42 QUILL_BEGIN_EXPORT
46 class Sink
47 {
48 public:
53  explicit Sink(std::optional<PatternFormatterOptions> override_pattern_formatter_options = std::nullopt)
54  : _override_pattern_formatter_options(std::move(override_pattern_formatter_options))
55  {
56  }
57 
61  virtual ~Sink() = default;
62 
63  Sink(Sink const&) = delete;
64  Sink& operator=(Sink const&) = delete;
65 
71  void set_log_level_filter(LogLevel log_level)
72  {
73  _log_level.store(log_level, std::memory_order_relaxed);
74  }
75 
81  QUILL_NODISCARD LogLevel get_log_level_filter() const noexcept
82  {
83  return _log_level.load(std::memory_order_relaxed);
84  }
85 
91  void add_filter(std::unique_ptr<Filter> filter)
92  {
93  if (QUILL_UNLIKELY(!filter))
94  {
95  QUILL_THROW(QuillError{"Filter pointer is nullptr"});
96  }
97 
98  // Call the user-overridable name accessor before taking the lock.
99  std::string filter_name = filter->get_filter_name();
100 
101  // Lock and add this filter to our global collection
102  detail::LockGuard const lock{_global_filters_lock};
103 
104  // Check if the same filter already exists
105  auto const search_filter_it = std::find_if(_global_filters.cbegin(), _global_filters.cend(),
106  [&filter_name](RegisteredFilter const& elem_filter)
107  { return elem_filter.filter_name == filter_name; });
108 
109  if (QUILL_UNLIKELY(search_filter_it != _global_filters.cend()))
110  {
111  QUILL_THROW(QuillError{"Filter with the same name already exists"});
112  }
113 
114  _global_filters.emplace_back(std::move(filter_name), std::move(filter));
115 
116  // Indicate a new filter was added - here relaxed is okay as the spinlock will do acq-rel on destruction
117  _new_filter.store(true, std::memory_order_relaxed);
118  }
119 
120 protected:
137  QUILL_ATTRIBUTE_HOT virtual void write_log(
138  MacroMetadata const* log_metadata, uint64_t log_timestamp, std::string_view thread_id,
139  std::string_view thread_name, std::string const& process_id, std::string_view logger_name,
140  LogLevel log_level, std::string_view log_level_description, std::string_view log_level_short_code,
141  std::vector<std::pair<std::string, std::string>> const* named_args,
142  std::string_view log_message, std::string_view log_statement) = 0;
143 
151  QUILL_ATTRIBUTE_HOT virtual void write_metric(MetricMetadata const* /* metric_metadata */,
152  uint64_t /* log_timestamp */, std::string_view /* thread_id */,
153  std::string_view /* thread_name */,
154  std::string const& /* process_id */,
155  std::string_view /* logger_name */, double /* value */)
156  {
157  }
158 
162  QUILL_ATTRIBUTE_HOT virtual void flush_sink() = 0;
163 
170  QUILL_ATTRIBUTE_HOT virtual void run_periodic_tasks() noexcept {}
171 
185  QUILL_NODISCARD bool apply_all_filters(MacroMetadata const* log_metadata, uint64_t log_timestamp,
186  std::string_view thread_id, std::string_view thread_name,
187  std::string_view logger_name, LogLevel log_level,
188  std::string_view log_message, std::string_view log_statement)
189  {
190  if (log_level < _log_level.load(std::memory_order_relaxed))
191  {
192  return false;
193  }
194 
195  // Update our local collection of the filters
196  if (QUILL_UNLIKELY(_new_filter.exchange(false, std::memory_order_relaxed)))
197  {
198  // if there is a new filter we have to update
199  _local_filters.clear();
200 
201  detail::LockGuard const lock{_global_filters_lock};
202 
203  for (auto const& filter : _global_filters)
204  {
205  _local_filters.push_back(filter.filter.get());
206  }
207  }
208 
209  if (_local_filters.empty())
210  {
211  return true;
212  }
213  else
214  {
215  return std::all_of(_local_filters.begin(), _local_filters.end(),
216  [log_metadata, log_timestamp, thread_id, thread_name, logger_name,
217  log_level, log_message, log_statement](Filter* filter_elem)
218  {
219  return filter_elem->filter(log_metadata, log_timestamp, thread_id, thread_name,
220  logger_name, log_level, log_message, log_statement);
221  });
222  }
223  }
224 
225 private:
226  friend class detail::BackendWorker;
227 
228  struct RegisteredFilter
229  {
230  RegisteredFilter(std::string filter_name_arg, std::unique_ptr<Filter> filter_arg)
231  : filter_name(std::move(filter_name_arg)), filter(std::move(filter_arg))
232  {
233  }
234 
235  std::string filter_name;
236  std::unique_ptr<Filter> filter;
237  };
238 
240  std::optional<PatternFormatterOptions> _override_pattern_formatter_options; /* Set by the frontend and accessed by the backend to initialise PatternFormatter */
241  std::shared_ptr<PatternFormatter> _override_pattern_formatter; /* The backend thread will set this once */
242 
244  std::vector<Filter*> _local_filters;
245 
247  std::vector<RegisteredFilter> _global_filters;
248  detail::Spinlock _global_filters_lock;
250  std::atomic<bool> _new_filter{false};
251 
252  std::atomic<LogLevel> _log_level{LogLevel::TraceL3};
253 };
254 
255 QUILL_END_EXPORT
256 
257 QUILL_END_NAMESPACE
QUILL_NODISCARD bool apply_all_filters(MacroMetadata const *log_metadata, uint64_t log_timestamp, std::string_view thread_id, std::string_view thread_name, std::string_view logger_name, LogLevel log_level, std::string_view log_message, std::string_view log_statement)
Applies all registered filters to the log record.
Definition: Sink.h:185
Base class for sinks.
Definition: Sink.h:46
Definition: UserDefinedDirectFormatFuzzer.cpp:81
Captures and stores information about a logging event in compile time.
Definition: MacroMetadata.h:24
virtual QUILL_ATTRIBUTE_HOT void run_periodic_tasks() noexcept
Executes periodic tasks by the backend thread, providing an opportunity for the user to perform custo...
Definition: Sink.h:170
Base filter class.
Definition: Filter.h:28
virtual QUILL_ATTRIBUTE_HOT void write_metric(MetricMetadata const *, uint64_t, std::string_view, std::string_view, std::string const &, std::string_view, double)
Publishes a metric sample to the sink.
Definition: Sink.h:151
Definition: PatternFormatter.h:35
void add_filter(std::unique_ptr< Filter > filter)
Adds a new filter to the sink.
Definition: Sink.h:91
void set_log_level_filter(LogLevel log_level)
Sets a log level filter on the sink.
Definition: Sink.h:71
Setups a signal handler to handle fatal signals.
Definition: BackendManager.h:28
QUILL_NODISCARD LogLevel get_log_level_filter() const noexcept
Returns the current log level filter set on the sink.
Definition: Sink.h:81
Definition: Spinlock.h:18
MetricMetadata extends MacroMetadata so the existing header-encoding path can carry it in the macro_m...
Definition: Metric.h:35
custom exception
Definition: QuillError.h:47
Sink(std::optional< PatternFormatterOptions > override_pattern_formatter_options=std::nullopt)
Constructor Uses the default pattern formatter.
Definition: Sink.h:53
Definition: Spinlock.h:58
Definition: BackendWorker.h:80