21#include "flow/error/error.hpp"
22#include <boost/move/make_unique.hpp>
33 Config* config,
const fs::path& log_path,
34 bool capture_rotate_signals_internally) :
46 m_throttling_now(
false),
47 m_throttling_active(
false),
50 m_serial_logger(boost::movelib::make_unique<Serial_file_logger>(get_logger(), m_config, log_path)),
66 m_signal_set(*(m_async_worker.task_engine()))
68 m_async_worker.start();
71 "Log-writing worker thread(s) have started around now; ready to work.");
74 log_flush_and_reopen(
false);
76 if (capture_rotate_signals_internally)
78 FLOW_LOG_INFO(
"Setting up internal log-rotate-reopening signal handler. "
79 "CAUTION! User program MUST avoid using non-boost::asio::signal_set signal handling! "
80 "If it does use non-boost.asio, behavior is undefined.");
83 m_signal_set.add(SIGHUP);
95 m_signal_set.async_wait([
this](
const Error_code& sys_err_code,
int sig_number)
97 on_rotate_signal(sys_err_code, sig_number);
127 assert((cfg.
m_hi_limit > 0) &&
"Per contract, hi_limit must be positive.");
136 if (prev_active != active)
139 "Config set: throttling feature active? [" << prev_active <<
"] => [" << active <<
"].");
157 const auto prev_throttling_now
159 std::memory_order_relaxed);
162 FLOW_LOG_INFO(
"Async_file_logger [" <<
this <<
"]: Config set: "
165 "throttling? = [" << prev_throttling_now <<
"] => [" <<
m_throttling_now <<
"]; "
166 "throttling feature active? = [" << active <<
"]. "
167 "Reminder: `throttling?` shall only be used if `throttling feature active?` is 1.");
183 FLOW_LOG_INFO(
"Async_file_logger [" <<
this <<
"]: Deleting. Worker thread will flush "
184 "output if possible; then we will proceed to shut down. Current mem-use of queued "
185 "log-requests is [" <<
m_pending_logs_sz <<
"]; if it is large, this might take some time.");
203 Synchronicity::S_ASYNC_AND_AWAIT_CONCURRENT_COMPLETION);
212 if (sys_err_code == boost::asio::error::operation_aborted)
219 "Internal log-rotate handler executed with signal number [" << sig_number <<
"].");
226 "Internal signal handler executed with an error indicator. Strange! "
227 "Ignoring and continuing other operation.");
264 const auto msg_sz = msg.size();
265 Log_request log_request{
new char[msg_sz], msg_sz, metadata,
267 memcpy(log_request.m_msg_copy, msg.data(), msg_sz);
277 const auto logs_sz =
mem_cost(log_request);
278 auto& throttling_begins = log_request.m_throttling_begins;
280 logs_sz_t pending_logs_sz;
281 logs_sz_t prev_pending_logs_sz;
293 && (pending_logs_sz >= limit) && (prev_pending_logs_sz < limit))
296 throttling_begins =
true;
299 if (throttling_begins)
311 "Async_file_logger [" <<
this <<
"]: "
312 "do_log() throttling algorithm: a message reached hi_limit; next message-to-be => likely dropped, "
313 "if feature active. Config: hi_limit [" << limit <<
"]. "
314 "Mem-use = [" << prev_pending_logs_sz <<
"] => [" << pending_logs_sz <<
"]; "
315 "throttling? = 1 (see above); throttling feature active? = [" << active <<
"]. "
316 "Reminder: `throttling?` shall only be used if `throttling feature active?` is 1. "
317 "Limit-triggering message's contents follow: [" << msg <<
"].");
323 "do_log() throttling algorithm: a message was processed; situation (reminder: beware concurrency): "
324 "Config: hi_limit [" << limit <<
"]. "
325 "Mem-use = [" << prev_pending_logs_sz <<
"] => [" << pending_logs_sz <<
"]; "
326 "throttling feature active? = [" <<
m_throttling_active.load(std::memory_order_relaxed) <<
"]. "
327 "Message's contents follow: [" << msg <<
"].");
335 auto really_log = [
this, log_request = std::move(log_request)]()
mutable
337 const auto metadata = log_request.m_metadata;
338 const String_view msg{log_request.m_msg_copy, log_request.m_msg_size};
343 const auto logs_sz =
mem_cost(log_request);
345 bool throttling_ends =
false;
347 logs_sz_t pending_logs_sz;
348 logs_sz_t prev_pending_logs_sz;
353 assert((prev_pending_logs_sz >= logs_sz) &&
"Bug? really_log() has no matching do_log()?");
358 if (pending_logs_sz == 0)
361 throttling_ends = (
m_throttling_now.exchange(
false, std::memory_order_relaxed) ==
true);
368 FLOW_LOG_INFO(
"Async_file_logger [" <<
this <<
"]: last pending message was logged; "
369 "next message-to-be => likely first one to *not* be dropped, if throttling feature active. "
370 "Config: hi_limit [" << limit <<
"]. "
371 "Mem-use = [" << prev_pending_logs_sz <<
"] => [" << pending_logs_sz <<
"]; "
372 "throttling? = 0 (see above); "
373 "throttling feature active? = [" <<
m_throttling_active.load(std::memory_order_relaxed) <<
"]. "
374 "Reminder: `throttling?` shall only be used if `throttling feature active?` is 1. "
375 "Queue-clearing message's contents follow: [" << msg <<
"].");
381 "really_log() throttling algorithm: last pending message was logged; "
382 "next message-to-be => likely first one to *not* be dropped, if throttling feature active. "
383 "Config: hi_limit [" << limit <<
"]. "
384 "Mem-use = [" << prev_pending_logs_sz <<
"] => 0; "
385 "throttling feature active? = [" <<
m_throttling_active.load(std::memory_order_relaxed) <<
"]. "
386 "Queue-clearing message is the one immediately following me in file. "
387 "Compare its time stamp to mine to see time lag due to queueing.");
393 "really_log() throttling algorithm: a message is about to be written to file; "
394 "situation (reminder: beware concurrency): Config: hi_limit [" << limit <<
"]. "
395 "Mem-use = [" << prev_pending_logs_sz <<
"] => [" << pending_logs_sz <<
"]; "
396 "throttling feature active? = [" <<
m_throttling_active.load(std::memory_order_relaxed) <<
"]. ");
397 "Message's contents follow: [" << msg <<
"].");
405 if (log_request.m_throttling_begins)
414 "Async_file_logger [" <<
this <<
"]: "
415 "really_log() throttling algorithm: The preceding message, when its log-request was "
416 "earlier enqueued, caused pending-logs RAM usage to exceed then-configured hi_limit. "
417 "If throttling feature was active, subsequent messages-to-be (log-requests) were dropped. "
418 "We only just got around to being able to log it (satisfy log-request) after all the "
419 "preceding ones in FIFO order. Nowadays: Config: hi_limit [" << limit <<
"]. "
420 "Mem-use = [" << prev_pending_logs_sz <<
"] => [" << pending_logs_sz <<
"]; "
421 "throttling feature active? = [" << active <<
"]. "
422 "Limit-crossing (in the past) message is the one immediately preceding the current one "
423 "you're reading in file. "
424 "Compare its time stamp to mine to see time lag due to queueing.");
430 delete[] log_request.m_msg_copy;
502 FLOW_LOG_INFO(
"Async_file_logger [" <<
this <<
"]: Non-worker (user) thread "
503 "requested [" << (async ?
"asynchronous" :
"synchronous") <<
"] file flush/close (if needed) and "
504 "re-open, such as for rotation or at initialization.");
507 async ? Synchronicity::S_ASYNC : Synchronicity::S_ASYNC_AND_AWAIT_CONCURRENT_COMPLETION);
512 if (!m_serial_logger->should_log(sev, component))
523 if (!m_throttling_active.load(std::memory_order_relaxed))
529 const auto throttled = m_throttling_now.load(std::memory_order_relaxed);
533 "should_log(sev=[" << sev <<
"]; component=[" << component.payload_enum_raw_value() <<
"]) "
534 "throttling algorithm situation (reminder: beware concurrency): "
535 "Throttling feature active? = 1; throttling? = [" << throttled <<
"].");
virtual void post(Task &&task, Synchronicity synchronicity=Synchronicity::S_ASYNC)
Cause the given Task (function) to execute within the worker thread as soon as the thread is free of ...
An implementation of Logger that logs messages to a given file-system path but never blocks any loggi...
flow::util::Lock_guard< Mutex > Lock_guard
Short-hand for Mutex lock.
async::Single_thread_task_loop m_async_worker
The thread (1-thread pool, technically) in charge of all m_serial_logger I/O operations including wri...
void on_rotate_signal(const Error_code &sys_err_code, int sig_number)
SIGHUP/equivalent handler for the optional feature capture_rotate_signals_internally in constructor.
std::atomic< bool > m_throttling_active
Whether the throttling-based-on-pending-logs-memory-used feature is currently active or not.
boost::movelib::unique_ptr< Serial_file_logger > m_serial_logger
This is the Logger doing all the real log-writing work (the one stored in Log_context is the logger-a...
static size_t mem_cost(const Log_request &log_request)
How much do_log() issuing the supplied Log_request shall contribute to m_pending_logs_sz.
Signal_set m_signal_set
Signal set which we may or may not be using to trap SIGHUP in order to auto-fire m_serial_logger->log...
size_t m_pending_logs_sz
Estimate of how much RAM is being used by storing do_log() requests' data (message itself,...
bool throttling_active() const
Whether the throttling feature is currently in effect.
Mutex m_throttling_mutex
Protects throttling algorithm data that require coherence among themselves: m_throttling_cfg,...
static size_t deep_size(const Log_request &val)
Estimate of memory footprint of the given value, including memory allocated on its behalf – but exclu...
std::atomic< bool > m_throttling_now
Contains the output of the always-on throttling algorithm; namely true if currently should_log() shal...
Throttling_cfg m_throttling_cfg
See Throttling_cfg. Protected by m_throttling_mutex.
void do_log(Msg_metadata *metadata, util::String_view msg) override
Implements interface method by asynchronously logging the message and some subset of the metadata in ...
void log_flush_and_reopen(bool async=true)
Causes the log at the file-system path to be flushed/closed (if needed) and re-opened; this will happ...
Throttling_cfg throttling_cfg() const
Accessor returning a copy of the current set of throttling knobs.
bool should_log(Sev sev, const Component &component) const override
Implements interface method by returning true if the severity and component (which is allowed to be n...
~Async_file_logger() override
Flushes out anything buffered, returns resources/closes output file(s); then returns.
Async_file_logger(Logger *backup_logger_ptr, Config *config, const fs::path &log_path, bool capture_rotate_signals_internally)
Constructs logger to subsequently log to the given file-system path.
bool logs_asynchronously() const override
Implements interface method by returning true, indicating that this Logger may need the contents of *...
A light-weight class, each object storing a component payload encoding an enum value from enum type o...
Class used to configure the filtering and logging behavior of Loggers; its use in your custom Loggers...
Convenience class that simply stores a Logger and/or Component passed into a constructor; and returns...
Interface that the user should implement, passing the implementing Logger into logging classes (Flow'...
#define FLOW_ERROR_SYS_ERROR_LOG_WARNING()
Logs a warning about the (often errno-based or from a library) error code in sys_err_code.
#define FLOW_LOG_INFO(ARG_stream_fragment)
Logs an INFO message into flow::log::Logger *get_logger() with flow::log::Component get_log_component...
#define FLOW_LOG_WARNING(ARG_stream_fragment)
Logs a WARNING message into flow::log::Logger *get_logger() with flow::log::Component get_log_compone...
#define FLOW_LOG_WITH_CHECKING(ARG_sev, ARG_stream_fragment)
Logs a message of the specified severity into flow::log::Logger *get_logger() with flow::log::Compone...
#define FLOW_LOG_SET_CONTEXT(ARG_logger_ptr, ARG_component_payload)
For the rest of the block within which this macro is instantiated, causes all FLOW_LOG_....
Synchronicity
Enumeration indicating the manner in which asio_exec_ctx_post(), and various boost....
Function< void()> Task
Short-hand for a task that can be posted for execution by a Concurrent_task_loop or flow::util::Task_...
Flow module providing logging functionality.
size_t deep_size(const Msg_metadata &val)
Estimate of memory footprint of the given value, including memory allocated on its behalf – but exclu...
Sev
Enumeration containing one of several message severity levels, ordered from highest to lowest.
@ S_WARNING
Message indicates a "bad" condition that is not frequent enough to be of severity Sev::S_TRACE.
@ S_INFO
Message indicates a not-"bad" condition that is not frequent enough to be of severity Sev::S_TRACE.
std::string ostream_op_string(T const &... ostream_args)
Equivalent to ostream_op_to_string() but returns a new string by value instead of writing to the call...
Basic_string_view< char > String_view
Commonly used char-based Basic_string_view. See its doc header.
boost::system::error_code Error_code
Short-hand for a boost.system error code (which basically encapsulates an integer/enum error code and...
Flow_log_component
The flow::log::Component payload enumeration comprising various log components used by Flow's own int...
In addition to the task object (function) itself, these are the data placed onto the queue of m_async...
size_t m_msg_size
Number of characters in m_msg_copy pointee string.
Msg_metadata * m_metadata
Pointer to array of characters comprising a copy of msg passed to do_log(). We must delete it.
Controls behavior of the throttling algorithm as described in Async_file_logger doc header Throttling...
static constexpr uint64_t S_HI_LIMIT_DEFAULT
Value of Async_file_logger(...).throttling_cfg().m_hi_limit: default/initial value of m_hi_limit.
uint64_t m_hi_limit
The throttling algorithm will go from Not-Throttling to Throttling state if and only if the current m...