A C++ library for logging very fast and without allocating.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

246 lines
7.6 KiB

пре 2 година
пре 2 година
пре 2 година
пре 2 година
пре 2 година
пре 2 година
пре 2 година
пре 2 година
пре 2 година
пре 2 година
пре 2 година
пре 2 година
пре 2 година
пре 2 година
пре 2 година
пре 2 година
  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, std::string_view filename, size_t size) {
  76. {strategy.build_buffer(filename, size)} -> BufferLike;
  77. {strategy.build_buffer(filename, size)} -> std::same_as<typename T::buffer_type>;
  78. };
  79. /**
  80. * @brief This concept maps a sink strategy.
  81. */
  82. template<typename T>
  83. concept SinkStrategy = requires (T strategy, int fd, std::string_view data) {
  84. {strategy.write(fd, data)};
  85. };
  86. /**
  87. * @brief This concept maps an output strategy.
  88. */
  89. template<typename T>
  90. concept OutputStrategy = requires (T strategy, std::string_view source, int fd) {
  91. {strategy.chunk(source)} -> std::same_as<std::pair<std::string_view, std::string_view>>;
  92. {strategy.on_write_completed_event(source, fd)} -> std::same_as<int>;
  93. };
  94. #endif
  95. #ifdef __cplusplus
  96. #ifndef __cpp_concepts
  97. #define OverflowStrategy typename
  98. #define BufferStrategy typename
  99. #define SinkStrategy typename
  100. #define OutputStrategy typename
  101. #endif
  102. /**
  103. * @brief Represent a mapped buffer. You should generally not touch this as it is internal representation for strategies.
  104. */
  105. struct mapped_buffer {
  106. char* _data;
  107. size_t _size;
  108. /**
  109. * @return the data pointer of the buffer
  110. */
  111. [[nodiscard]] char* data() const { return _data; }
  112. /**
  113. * @return the size of the allocated buffer
  114. */
  115. [[nodiscard]] size_t size() const { return _size; }
  116. };
  117. /**
  118. * @brief a strategy type directing how buffers are allocated. This directs the buffer strategy to use a standard container to allocate.
  119. */
  120. struct BufferStrategyInternal {
  121. using buffer_type = std::vector<char>;
  122. static_assert(BufferLike<buffer_type>);
  123. buffer_type build_buffer(std::string_view, size_t);
  124. };
  125. /**
  126. * @brief a strategy that employs a memory mapped buffer to manage its memory. Said buffer is expected to be read from the same process.
  127. */
  128. struct BufferStrategyShared {
  129. using buffer_type = mapped_buffer;
  130. static_assert(BufferLike<buffer_type>);
  131. buffer_type build_buffer(std::string_view, size_t);
  132. };
  133. /**
  134. * @brief a strategy that employs a memory mapped buffer to manage its memory. Said buffer is expected to be read from a different process.
  135. */
  136. struct BufferStrategyExternal {
  137. using buffer_type = mapped_buffer;
  138. static_assert(BufferLike<buffer_type>);
  139. buffer_type build_buffer(std::string_view, size_t);
  140. };
  141. /* * ... * */
  142. /**
  143. * @brief a strategy that makes the sink flush at every write, making the IO to disk as soon as possible.
  144. */
  145. struct SinkStrategyDirect {
  146. void write(int fd, std::string_view data);
  147. };
  148. /**
  149. * @brief a strategy that makes the sink write with the settings that have the highest throughput.
  150. */
  151. struct SinkStrategyFastest {
  152. void write(int fd, std::string_view data);
  153. };
  154. /**
  155. * @brief a strategy that makes the sink write using memory mapped IO, making the IO always commit to system pages before returning.
  156. */
  157. struct SinkStrategyMmaped {
  158. void write(int fd, std::string_view data);
  159. };
  160. /**
  161. * @brief a strategy that makes the sink do nothing, expecting another process to handle the process.
  162. */
  163. struct SinkStrategyExternal {
  164. void write(int fd, std::string_view data);
  165. };
  166. /* * ... * */
  167. /**
  168. * @brief a strategy that makes overflowing waiting. The waiting is handled by exponential backoff of factor 1.5
  169. */
  170. struct OverflowStrategyWait {
  171. static constexpr overflow_response_t on_overflow = overflow_response_t::must_wait;
  172. void wait();
  173. };
  174. /**
  175. * @brief a strategy that makes overflowing overwrite the data, possibly corrupting the generated log, without waiting.
  176. */
  177. struct OverflowStrategyContinue {
  178. static constexpr overflow_response_t on_overflow = overflow_response_t::must_overflow;
  179. void wait();
  180. };
  181. /* * ... * */
  182. /**
  183. * @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.
  184. */
  185. struct OutputStrategyTimed {
  186. std::chrono::seconds interval;
  187. std::string_view directory;
  188. std::optional<std::chrono::time_point<std::chrono::file_clock> > last_change = std::nullopt;
  189. std::pair<std::string_view, std::string_view> chunk(std::string_view);
  190. int on_write_completed_event(std::string_view, int);
  191. };
  192. /**
  193. * @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.
  194. */
  195. struct OutputStrategySized {
  196. uint64_t interval;
  197. std::string_view directory;
  198. uint64_t written_bytes = 0;
  199. std::pair<std::string_view, std::string_view> chunk(std::string_view);
  200. int on_write_completed_event(std::string_view, int);
  201. };
  202. /**
  203. * @brief a strategy that makes the logs be generated in the same file all the time.
  204. */
  205. struct OutputStrategySimple {
  206. std::pair<std::string_view, std::string_view> chunk(std::string_view);
  207. int on_write_completed_event(std::string_view, int);
  208. };
  209. #endif