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

  1. #pragma once
  2. #ifdef __cplusplus
  3. #include <vector>
  4. #include <chrono>
  5. #endif
  6. /**
  7. * @brief Describes what should happen if the allocated buffer is full and no logs can be written
  8. */
  9. enum SL_ON_SINK_FULL {
  10. SL_OVERFLOW = 1, /**< When the sink is full, we want to drop the oldest data even if it is not written yet */
  11. SL_WAIT = 2 /**< When the sink is full, we want to wait for the next write. Do that if log keeping is critical */
  12. };
  13. /**
  14. * @brief Describes what type of IO should be used to log your data
  15. */
  16. enum SL_SINK_IO_TYPE {
  17. SL_DIRECT_IO = 1, /**< Use IO that ensure data is written on disk as soon as possible. Useful for debugging. */
  18. 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. */
  19. SL_FASTEST_IO = 3, /**< Write with the highest throughput. */
  20. SL_EXTERNAL_IO = 4 /**< Do not handle any IO. Writes to a memory mapped file and let another process handle the IO. */
  21. };
  22. /**
  23. * @brief What type of buffer should we use?
  24. */
  25. enum SL_BUFFER_TYPE {
  26. SL_EXTERNAL = 1, /**< Use a provided memory mapped non-temporary file */
  27. SL_SHARED = 2, /**< Use an automatically generated temporary file */
  28. SL_INTERNAL = 3 /**< Use internal memory. Compatible only with the SL_SINK_IO_TYPE: SL_DIRECT_IO and SL_FASTEST_IO. */
  29. };
  30. /**
  31. * @brief On what basis do we generate new files for logs?
  32. */
  33. enum SL_ROLL_LOGS {
  34. SL_BY_TIME = 1, /**< Will create a new log every n-hours */
  35. SL_BY_SIZE = 2, /**< Will create a new log every n-KB */
  36. SL_NEVER = 0 /**< Will never split the log. Use when doing external logging. */
  37. };
  38. #ifndef __cplusplus
  39. typedef enum SL_ON_SINK_FULL SL_ON_SINK_FULL;
  40. typedef enum SL_SINK_IO_TYPE SL_SINK_IO_TYPE;
  41. typedef enum SL_BUFFER_TYPE SL_BUFFER_TYPE;
  42. typedef enum SL_ROLL_LOGS SL_ROLL_LOGS;
  43. #endif
  44. #ifdef __cpp_concepts
  45. /**
  46. * @brief Represents the behaviour if the buffer would be overflowing.
  47. */
  48. enum class overflow_response_t {
  49. must_wait = 0, /**< Represents that on overflow, the writer should wait using the strategy provided wait function. */
  50. 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. */
  51. must_drop = must_wait /**< UNUSED, may be used in the future to mean that the current write should be dropped. */
  52. };
  53. /**
  54. * @brief This concept maps an overflow strategy.
  55. */
  56. template<typename T>
  57. concept OverflowStrategy = requires (T strategy) {
  58. {T::on_overflow} -> std::same_as<const overflow_response_t&>;
  59. {strategy.wait()};
  60. };
  61. /**
  62. * @brief This concept represents something that should be writable and mapped to valid memory within the software.
  63. *
  64. * @details This is used for mapping allocated buffers made from buffer strategies.
  65. */
  66. template<typename T>
  67. concept BufferLike = requires (T buffer) {
  68. {buffer.data()} -> std::same_as<char*>;
  69. {buffer.size()} -> std::same_as<size_t>;
  70. };
  71. /**
  72. * @brief This concept maps a buffer strategy.
  73. */
  74. template<typename T>
  75. concept BufferStrategy = requires (T strategy, size_t size) {
  76. {strategy.build_buffer(size)} -> BufferLike;
  77. };
  78. /**
  79. * @brief This concept maps a sink strategy.
  80. */
  81. template<typename T>
  82. concept SinkStrategy = requires (T strategy, int fd, std::string_view data) {
  83. {strategy.write(fd, data)};
  84. };
  85. /**
  86. * @brief This concept maps an output strategy.
  87. */
  88. template<typename T>
  89. concept OutputStrategy = requires (T strategy, std::string_view source, int fd) {
  90. {strategy.chunk(source)} -> std::same_as<std::pair<std::string_view, std::string_view>>;
  91. {strategy.on_write_completed_event(source, fd)} -> std::same_as<int>;
  92. };
  93. #endif
  94. #ifdef __cplusplus
  95. #ifndef __cpp_concepts
  96. #define OverflowStrategy typename
  97. #define BufferStrategy typename
  98. #define SinkStrategy typename
  99. #define OutputStrategy typename
  100. #endif
  101. /**
  102. * @brief Represent a mapped buffer. You should generally not touch this as it is internal representation for strategies.
  103. */
  104. struct mapped_buffer {
  105. char* _data;
  106. size_t _size;
  107. /**
  108. * @return the data pointer of the buffer
  109. */
  110. [[nodiscard]] char* data() const { return _data; }
  111. /**
  112. * @return the size of the allocated buffer
  113. */
  114. [[nodiscard]] size_t size() const { return _size; }
  115. };
  116. struct BufferStrategyInternal {
  117. using buffer_type = std::vector<char>;
  118. static_assert(BufferLike<buffer_type>);
  119. buffer_type build_buffer(size_t);
  120. };
  121. struct BufferStrategyShared {
  122. using buffer_type = mapped_buffer;
  123. static_assert(BufferLike<buffer_type>);
  124. buffer_type build_buffer(size_t);
  125. };
  126. struct BufferStrategyExternal {
  127. using buffer_type = mapped_buffer;
  128. static_assert(BufferLike<buffer_type>);
  129. buffer_type build_buffer(size_t);
  130. };
  131. struct SinkStrategyDirect {
  132. void write(int fd, std::string_view data);
  133. };
  134. struct SinkStrategyFastest {
  135. void write(int fd, std::string_view data);
  136. };
  137. struct SinkStrategyMmaped {
  138. void write(int fd, std::string_view data);
  139. };
  140. struct SinkStrategyExternal {
  141. void write(int fd, std::string_view data);
  142. };
  143. struct OverflowStrategyWait {
  144. static constexpr overflow_response_t on_overflow = overflow_response_t::must_wait;
  145. void wait();
  146. };
  147. struct OverflowStrategyContinue {
  148. static constexpr overflow_response_t on_overflow = overflow_response_t::must_overflow;
  149. void wait();
  150. };
  151. struct OutputStrategyTimed {
  152. std::chrono::seconds interval;
  153. std::chrono::time_point<std::chrono::file_clock> last_change;
  154. std::pair<std::string_view, std::string_view> chunk(std::string_view);
  155. int on_write_completed_event(std::string_view, int);
  156. };
  157. struct OutputStrategySized {
  158. uint64_t interval;
  159. uint64_t written_bytes;
  160. std::pair<std::string_view, std::string_view> chunk(std::string_view);
  161. int on_write_completed_event(std::string_view, int);
  162. };
  163. struct OutputStrategySimple {
  164. std::pair<std::string_view, std::string_view> chunk(std::string_view);
  165. int on_write_completed_event(std::string_view, int);
  166. };
  167. #endif