A C++ library for logging very fast and without allocating.
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 
 
 

246 lignes
7.6 KiB

#pragma once
#ifdef __cplusplus
#include <vector>
#include <chrono>
#endif
/**
* @brief Describes what should happen if the allocated buffer is full and no logs can be written
*/
enum SL_ON_SINK_FULL {
SL_OVERFLOW = 1, /**< When the sink is full, we want to drop the oldest data even if it is not written yet */
SL_WAIT = 2 /**< When the sink is full, we want to wait for the next write. Do that if log keeping is critical */
};
/**
* @brief Describes what type of IO should be used to log your data
*/
enum SL_SINK_IO_TYPE {
SL_DIRECT_IO = 1, /**< Use IO that ensure data is written on disk as soon as possible. Useful for debugging. */
SL_MMAPED_IO = 2, /**< Use IO that ensures that logging data will survive a software crash by using a memory mapped file as buffer. You can later expunge that buffer to get the useful information out. */
SL_FASTEST_IO = 3, /**< Write with the highest throughput. */
SL_EXTERNAL_IO = 4 /**< Do not handle any IO. Writes to a memory mapped file and let another process handle the IO. */
};
/**
* @brief What type of buffer should we use?
*/
enum SL_BUFFER_TYPE {
SL_EXTERNAL = 1, /**< Use a provided memory mapped non-temporary file */
SL_SHARED = 2, /**< Use an automatically generated temporary file */
SL_INTERNAL = 3 /**< Use internal memory. Compatible only with the SL_SINK_IO_TYPE: SL_DIRECT_IO and SL_FASTEST_IO. */
};
/**
* @brief On what basis do we generate new files for logs?
*/
enum SL_ROLL_LOGS {
SL_BY_TIME = 1, /**< Will create a new log every n-hours */
SL_BY_SIZE = 2, /**< Will create a new log every n-KB */
SL_NEVER = 0 /**< Will never split the log. Use when doing external logging. */
};
#ifndef __cplusplus
typedef enum SL_ON_SINK_FULL SL_ON_SINK_FULL;
typedef enum SL_SINK_IO_TYPE SL_SINK_IO_TYPE;
typedef enum SL_BUFFER_TYPE SL_BUFFER_TYPE;
typedef enum SL_ROLL_LOGS SL_ROLL_LOGS;
#endif
#ifdef __cpp_concepts
/**
* @brief Represents the behaviour if the buffer would be overflowing.
*/
enum class overflow_response_t {
must_wait = 0, /**< Represents that on overflow, the writer should wait using the strategy provided wait function. */
must_overflow = 1, /**< Represents that on overflow, the writer should push the fence and keep on writing immediately, even if the log would get corrupt. */
must_drop = must_wait /**< UNUSED, may be used in the future to mean that the current write should be dropped. */
};
/**
* @brief This concept maps an overflow strategy.
*/
template<typename T>
concept OverflowStrategy = requires (T strategy) {
{T::on_overflow} -> std::same_as<const overflow_response_t&>;
{strategy.wait()};
};
/**
* @brief This concept represents something that should be writable and mapped to valid memory within the software.
*
* @details This is used for mapping allocated buffers made from buffer strategies.
*/
template<typename T>
concept BufferLike = requires (T buffer) {
{buffer.data()} -> std::same_as<char*>;
{buffer.size()} -> std::same_as<size_t>;
};
/**
* @brief This concept maps a buffer strategy.
*/
template<typename T>
concept BufferStrategy = requires (T strategy, std::string_view filename, size_t size) {
{strategy.build_buffer(filename, size)} -> BufferLike;
{strategy.build_buffer(filename, size)} -> std::same_as<typename T::buffer_type>;
};
/**
* @brief This concept maps a sink strategy.
*/
template<typename T>
concept SinkStrategy = requires (T strategy, int fd, std::string_view data) {
{strategy.write(fd, data)};
};
/**
* @brief This concept maps an output strategy.
*/
template<typename T>
concept OutputStrategy = requires (T strategy, std::string_view source, int fd) {
{strategy.chunk(source)} -> std::same_as<std::pair<std::string_view, std::string_view>>;
{strategy.on_write_completed_event(source, fd)} -> std::same_as<int>;
};
#endif
#ifdef __cplusplus
#ifndef __cpp_concepts
#define OverflowStrategy typename
#define BufferStrategy typename
#define SinkStrategy typename
#define OutputStrategy typename
#endif
/**
* @brief Represent a mapped buffer. You should generally not touch this as it is internal representation for strategies.
*/
struct mapped_buffer {
char* _data;
size_t _size;
/**
* @return the data pointer of the buffer
*/
[[nodiscard]] char* data() const { return _data; }
/**
* @return the size of the allocated buffer
*/
[[nodiscard]] size_t size() const { return _size; }
};
/**
* @brief a strategy type directing how buffers are allocated. This directs the buffer strategy to use a standard container to allocate.
*/
struct BufferStrategyInternal {
using buffer_type = std::vector<char>;
static_assert(BufferLike<buffer_type>);
buffer_type build_buffer(std::string_view, size_t);
};
/**
* @brief a strategy that employs a memory mapped buffer to manage its memory. Said buffer is expected to be read from the same process.
*/
struct BufferStrategyShared {
using buffer_type = mapped_buffer;
static_assert(BufferLike<buffer_type>);
buffer_type build_buffer(std::string_view, size_t);
};
/**
* @brief a strategy that employs a memory mapped buffer to manage its memory. Said buffer is expected to be read from a different process.
*/
struct BufferStrategyExternal {
using buffer_type = mapped_buffer;
static_assert(BufferLike<buffer_type>);
buffer_type build_buffer(std::string_view, size_t);
};
/* * ... * */
/**
* @brief a strategy that makes the sink flush at every write, making the IO to disk as soon as possible.
*/
struct SinkStrategyDirect {
void write(int fd, std::string_view data);
};
/**
* @brief a strategy that makes the sink write with the settings that have the highest throughput.
*/
struct SinkStrategyFastest {
void write(int fd, std::string_view data);
};
/**
* @brief a strategy that makes the sink write using memory mapped IO, making the IO always commit to system pages before returning.
*/
struct SinkStrategyMmaped {
void write(int fd, std::string_view data);
};
/**
* @brief a strategy that makes the sink do nothing, expecting another process to handle the process.
*/
struct SinkStrategyExternal {
void write(int fd, std::string_view data);
};
/* * ... * */
/**
* @brief a strategy that makes overflowing waiting. The waiting is handled by exponential backoff of factor 1.5
*/
struct OverflowStrategyWait {
static constexpr overflow_response_t on_overflow = overflow_response_t::must_wait;
void wait();
};
/**
* @brief a strategy that makes overflowing overwrite the data, possibly corrupting the generated log, without waiting.
*/
struct OverflowStrategyContinue {
static constexpr overflow_response_t on_overflow = overflow_response_t::must_overflow;
void wait();
};
/* * ... * */
/**
* @brief a strategy that controls how often new log files are generated. Logs are spaced by making them writable for only a certain amount of time.
*/
struct OutputStrategyTimed {
std::chrono::seconds interval;
std::string_view directory;
std::optional<std::chrono::time_point<std::chrono::file_clock> > last_change = std::nullopt;
std::pair<std::string_view, std::string_view> chunk(std::string_view);
int on_write_completed_event(std::string_view, int);
};
/**
* @brief a strategy that controls how often new log files are generated. Logs are spaced by making them at most one line longer that the amount of bytes specified.
*/
struct OutputStrategySized {
uint64_t interval;
std::string_view directory;
uint64_t written_bytes = 0;
std::pair<std::string_view, std::string_view> chunk(std::string_view);
int on_write_completed_event(std::string_view, int);
};
/**
* @brief a strategy that makes the logs be generated in the same file all the time.
*/
struct OutputStrategySimple {
std::pair<std::string_view, std::string_view> chunk(std::string_view);
int on_write_completed_event(std::string_view, int);
};
#endif