Browse Source

Base project files added

main
Ludovic 'Archivist' Lagouardette 1 year ago
parent
commit
3b0a4f8353
11 changed files with 382 additions and 0 deletions
  1. +9
    -0
      CMakeLists.txt
  2. +8
    -0
      LibSnugLog/CMakeLists.txt
  3. +2
    -0
      LibSnugLog/include/disruptor.h
  4. +1
    -0
      LibSnugLog/include/registry.h
  5. +1
    -0
      LibSnugLog/include/sink.h
  6. +1
    -0
      LibSnugLog/include/source.h
  7. +77
    -0
      LibSnugLog/public_include/sl/register.h
  8. +37
    -0
      LibSnugLog/public_include/sl/strategies.h
  9. +245
    -0
      LibSnugLog/public_include/sl/transaction.h
  10. +1
    -0
      LibSnugLog/source/disruptor.cpp
  11. +0
    -0
      Tests/CMakeLists.txt

+ 9
- 0
CMakeLists.txt View File

@ -0,0 +1,9 @@
cmake_minimum_required(VERSION 3.23)
project(SnugLog)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED True)
add_subdirectory(LibSnugLog)
#add_subdirectory(Tests)

+ 8
- 0
LibSnugLog/CMakeLists.txt View File

@ -0,0 +1,8 @@
include_directories(./public_include)
include_directories(./include)
add_library(LibSnugLog
include/disruptor.h
source/disruptor.cpp include/sink.h include/registry.h include/source.h public_include/sl/strategies.h public_include/sl/register.h public_include/sl/transaction.h)

+ 2
- 0
LibSnugLog/include/disruptor.h View File

@ -0,0 +1,2 @@
#pragma once

+ 1
- 0
LibSnugLog/include/registry.h View File

@ -0,0 +1 @@
#pragma once

+ 1
- 0
LibSnugLog/include/sink.h View File

@ -0,0 +1 @@
#pragma once

+ 1
- 0
LibSnugLog/include/source.h View File

@ -0,0 +1 @@
#pragma once

+ 77
- 0
LibSnugLog/public_include/sl/register.h View File

@ -0,0 +1,77 @@
#pragma once
#ifdef __cplusplus
#include <cstdint>
#include <cstddef>
#else
#include <stdint.h>
#include <stddef.h>
#endif
#include "sl/strategies.h"
#ifdef __cplusplus
struct sl_buffer_strategy;
struct sl_sink_strategy;
struct sl_overflow_strategy;
struct sl_output_strategy;
namespace sl {
using buffer_strategy = sl_buffer_strategy;
using sink_strategy = sl_sink_strategy;
using overflow_strategy = sl_overflow_strategy;
using output_strategy = sl_output_strategy;
}
#else
typedef struct sl_buffer_strategy buffer_strategy;
typedef struct sl_sink_strategy sink_strategy;
typedef struct sl_overflow_strategy overflow_strategy;
typedef struct sl_output_strategy output_strategy;
#endif
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Constructs a buffer strategy from C
* @param strategy The type of strategy
* @param filename A file describing the strategy, ignored if SL_BUFFER_TYPE is SL_INTERNAL, optional if SL_BUFFER_TYPE is SL_SHARED
* @return a strategy token for building a logger
*/
sl_buffer_strategy* sl_create_buffer_strategy(SL_BUFFER_TYPE strategy, char *filename);
/**
* @brief Constructs a sink strategy for how to handle IO in the logger
* @param strategy The type of strategy
* @return a strategy token to build a logger
*/
sl_sink_strategy* sl_create_sink_strategy(SL_SINK_IO_TYPE strategy);
/**
* @brief Constructs an overflow strategy for how to handle the edge case of a full buffer
* @param strategy The type of strategy
* @return a strategy token to build a logger
*/
sl_overflow_strategy* sl_create_overflow_strategy(SL_ON_SINK_FULL strategy);
/**
* @brief Constructs a strategy that will direct where data is logged and how it will be split
* @param strategy The strategy to use
* @param strategy_parameter Either the amount of hours to keep in the same file, or the file size in kilobytes
* @param output_directory The directory where the logs will be written
* @return a strategy token to build a logger
*/
sl_output_strategy* sl_create_output_strategy(SL_ROLL_LOGS strategy, uint64_t strategy_parameter, char* output_directory);
/**
* @brief Constructs and registers a logger, readily available immediately
* @param internal_id The ID with which you want to refer to the logger to access it as fast as possible
* @param external_id The ID which will be part of the logs filename
* @param strategies Collect the 4 tokens and reunite them into the hero's sword that will slay every other logger
*/
void sl_register_logger(int internal_id, char* external_id, sl_buffer_strategy*, sl_sink_strategy*, sl_overflow_strategy*, sl_output_strategy*);
#ifdef __cplusplus
}
#endif

+ 37
- 0
LibSnugLog/public_include/sl/strategies.h View File

@ -0,0 +1,37 @@
#pragma once
/**
* @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. */
};

+ 245
- 0
LibSnugLog/public_include/sl/transaction.h View File

@ -0,0 +1,245 @@
#pragma once
#pragma once
#ifdef __cplusplus
#include <cstdint>
#include <cstddef>
#include <iterator>
#else
#include <stdint.h>
#include <stddef.h>
#endif
#ifdef __cplusplus
struct sl_log_transaction;
struct sl_log_transaction_internals;
namespace sl {
using cstyle_log_transaction = sl_log_transaction;
using cstyle_log_transaction_internals = sl_log_transaction_internals;
/**
* @brief This class represents a logging transaction
* @details
* This class has the following guarantees:
* - Can be built on the stack safely
* - This class never allocates using the system allocator
* - This class does not perform any system call
* - Finalization cannot happen twice from the public interface
* - This class may pad the log by up to <tt>std::hardware_destructive_interference</tt> bytes
* - This class is both thread-safe and multiprocessing-safe to finalize
*
* @details
* This class expects the finalization to occur in the same thread that wrote any data to the log.
* @pre
* This class expects the logging buffer to be at least 2 binary orders of magnitude bigger than the largest log
* message. It is recommended that it is at least 6 binary orders of magnitude bigger than the largest log.
* @post
* This class leaves the log in a valid state in accordance to the defined log strategies.
*/
class log_transaction {
char* local_start;
size_t local_size;
char* buffer_start;
size_t buffer_size;
void* buffer_initiator_page;
bool is_finalized = false;
void finalize_impl();
/**
* @brief A forward iterator to the current log transaction.
*/
class iterator {
using iterator_category = std::forward_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = char;
using pointer = char*;
using reference = char&;
log_transaction& owner_ref;
size_t index;
public:
explicit iterator(log_transaction& parent, size_t position = 0)
: owner_ref(parent)
, index(((parent.buffer_start - parent.local_start) + position)%parent.buffer_size)
{}
iterator& operator++() {
index+=1;
index%=owner_ref.buffer_size;
return *this;
}
iterator operator++(int) {
auto cpy = *this;
index+=1;
index%=owner_ref.buffer_size;
return cpy;
}
difference_type operator-(iterator other) const {
size_t forward, backward;
forward = owner_ref.buffer_size - index;
forward += other.index;
backward = other.index - index;
return static_cast<difference_type>(std::min(forward, backward));
}
reference operator*() {
return owner_ref.buffer_start[index];
}
};
public:
/**
* @brief Starts a transaction. You have to then write your logs between the start and the end of the
* transaction, then finalize it.
* @details
* Guarantees are as follow:
* - This constructor never allocates using the system allocator
* - This constructor does not perform any system call
* - This constructor may pad the log by up to <tt>std::hardware_destructive_interference</tt> bytes
* - This constructor is both thread-safe and multiprocessing-safe
*
* @pre
* We expect the provided <tt>internal_id</tt> to be valid and registered, if it is not, the transaction may look for a
* log named "UNHANDLED_ERRORS" and log there instead silently.
* @post
* A started transaction MUST be finalized. The log can, upon initiation of the transaction, no longer be
* pushed to its sink past the current sequence point of the transaction.
*
* @post
* This class will finalize an un-finalized transaction
* @param internal_id the logger id where the transaction is to happen
* @param log_size the amount of bytes to write in the transaction.
*/
explicit log_transaction(int internal_id, size_t log_size);
log_transaction(const log_transaction&) = delete;
log_transaction(log_transaction&&) = delete;
void operator=(const log_transaction&) = delete;
void operator=(log_transaction&&) = delete;
/**
* @brief Unsafely accesses the underlying buffer
* @param index The index to the buffer to access
* @return a reference to an underlying <tt>char</tt>, undefined behaviour is the index is invalid
*/
char& operator[](size_t index) {
return buffer_start[((buffer_start - local_start) + index)%buffer_size];
}
/**
* @brief Obtains a forward iterator to the start of the writable range
* @return a non-descript iterator to the beginning of the range
*/
iterator begin() {
return iterator(*this, 0);
}
/**
* @brief Obtains a forward iterator to the end of the writable range
* @return a non-descript iterator to one-past-the-end of the range
*/
iterator end() {
return iterator(*this, local_size);
}
/**
* @brief Finalizes the transaction
* @details
* Finalizes a transaction, makes access to the finalized buffer undefined behaviour. If <tt>NDEBUG</tt> is
* defined, accesses will lead to a near-nullptr dereference.
*
* @details
* You are NOT protected against erroneous double finalization in debug mode. You are protected against double
* finalization out of debug mode. This protection and these erroneous states do not interact with the
* destructor, which will finalize transactions if not finalized yet.
*/
void finalize();
/**
* @see{void finalize()}
*/
~log_transaction(){
if(!is_finalized) finalize_impl();
}
};
}
#else
typedef struct sl_log_transaction log_transaction;
typedef struct sl_log_transaction_internals log_transaction_internals;
#endif
/**
* @brief Represents a transaction in the log. Must be finalized for proper operation of the log.
*/
struct sl_log_transaction {
char* begin; /**< The start of the given writable span */
char* end; /**< 1 element past the end of the writable span */
sl_log_transaction_internals* internals; /** Internal, do not touch */
};
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Starts a transaction. You have to then write your logs between the start and the end of the transaction,
* then finalize it.
* @details
* Guarantees are as follow:
* - This function never allocates using the system allocator
* - This function does not perform any system call
* - This function may need to pad the log to be operable by C easily
* - This function is both thread-safe and multiprocessing-safe
*
* @pre
* We expect the provided internal_id to be valid and registered, if it is not, the transaction may look for a log
* named "UNHANDLED_ERRORS" and log there instead silently.
* @post
* A started transaction MUST be finalized. The log can, upon initiation of the transaction, no longer be pushed to
* its sink past the current sequence point of the transaction.
* @param internal_id the logger id where the transaction is to happen
* @param log_size the amount of bytes to write in the transaction.
* @return a transaction of at least log_size bytes
*/
sl_log_transaction sl_start_log_transaction(int internal_id, size_t log_size);
/**
* @brief Finalizes a transaction.
* @details
* Be cautious. Finalizing a finalized transaction should NEVER be done. It may wait forever, screw up your log, and
* clog your log queue forever.
* @pre
* The transaction to finalize must never have been finalized before. If it has been finalized before, the
* finalization may never terminate.
* @post
* All data written in the transaction is thenceforth scheduled to be written. The transaction state is now moot
* and should not be accessed further, and should possibly be cleared.
* @param target the transaction to finalize.
*/
void sl_finalize_log_transaction(sl_log_transaction target);
/**
* @brief Clears a transaction object. This is not required but may help prevent bugs.
* @param to_clear
*/
inline void sl_clear_transaction(sl_log_transaction* to_clear) {
#ifdef NDEBUG
do {} while(false);
#else
#ifdef __cplusplus
to_clear->begin = nullptr;
to_clear->end = nullptr;
to_clear->internals = nullptr;
#else
to_clear->begin = NULL;
to_clear->end = NULL;
to_clear->internals = NULL;
#endif
#endif
}
#ifdef __cplusplus
}
#endif

+ 1
- 0
LibSnugLog/source/disruptor.cpp View File

@ -0,0 +1 @@

+ 0
- 0
Tests/CMakeLists.txt View File


Loading…
Cancel
Save