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.
 
 
 

195 lignes
5.8 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, size_t size) {
{strategy.build_buffer(size)} -> BufferLike;
};
/**
* @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; }
};
struct BufferStrategyInternal {
using buffer_type = std::vector<char>;
static_assert(BufferLike<buffer_type>);
buffer_type build_buffer(size_t);
};
struct BufferStrategyShared {
using buffer_type = mapped_buffer;
static_assert(BufferLike<buffer_type>);
buffer_type build_buffer(size_t);
};
struct BufferStrategyExternal {
using buffer_type = mapped_buffer;
static_assert(BufferLike<buffer_type>);
buffer_type build_buffer(size_t);
};
struct SinkStrategyDirect {
void write(int fd, std::string_view data);
};
struct SinkStrategyFastest {
void write(int fd, std::string_view data);
};
struct SinkStrategyMmaped {
void write(int fd, std::string_view data);
};
struct SinkStrategyExternal {
void write(int fd, std::string_view data);
};
struct OverflowStrategyWait {
static constexpr overflow_response_t on_overflow = overflow_response_t::must_wait;
void wait();
};
struct OverflowStrategyContinue {
static constexpr overflow_response_t on_overflow = overflow_response_t::must_overflow;
void wait();
};
struct OutputStrategyTimed {
std::chrono::seconds interval;
std::chrono::time_point<std::chrono::file_clock> last_change;
std::pair<std::string_view, std::string_view> chunk(std::string_view);
int on_write_completed_event(std::string_view, int);
};
struct OutputStrategySized {
uint64_t interval;
uint64_t written_bytes;
std::pair<std::string_view, std::string_view> chunk(std::string_view);
int on_write_completed_event(std::string_view, int);
};
struct OutputStrategySimple {
std::pair<std::string_view, std::string_view> chunk(std::string_view);
int on_write_completed_event(std::string_view, int);
};
#endif