blob: 4eca13fbb0e9373d493c062cdafa668778c83508 [file] [log] [blame]
Alexei Strots01395492023-03-20 13:59:56 -07001#ifndef AOS_EVENTS_LOGGING_LOG_BACKEND_H_
2#define AOS_EVENTS_LOGGING_LOG_BACKEND_H_
3
4#include <fcntl.h>
5#include <sys/types.h>
6#include <sys/uio.h>
7
8#include <memory>
9#include <string>
10#include <vector>
11
12#include "absl/types/span.h"
Philipp Schrader790cb542023-07-05 21:06:52 -070013
Alexei Strotsf08b8fb2023-04-21 19:46:08 -070014#include "aos/events/logging/buffer_encoder.h"
Alexei Strots01395492023-03-20 13:59:56 -070015#include "aos/time/time.h"
16
17namespace aos::logger {
18
19class WriteStats {
20 public:
21 // The maximum time for a single write call, or 0 if none have been performed.
22 std::chrono::nanoseconds max_write_time() const { return max_write_time_; }
23 // The number of bytes in the longest write call, or -1 if none have been
24 // performed.
25 int max_write_time_bytes() const { return max_write_time_bytes_; }
26 // The number of buffers in the longest write call, or -1 if none have been
27 // performed.
28 int max_write_time_messages() const { return max_write_time_messages_; }
29 // The total time spent in write calls.
30 std::chrono::nanoseconds total_write_time() const {
31 return total_write_time_;
32 }
Maxwell Gumleyd26e6292024-04-24 10:45:07 -060033
34 // The total time spent encoding.
35 std::chrono::nanoseconds total_encode_duration() const {
36 return total_encode_duration_;
37 }
38
Alexei Strots01395492023-03-20 13:59:56 -070039 // The total number of writes which have been performed.
40 int total_write_count() const { return total_write_count_; }
41 // The total number of messages which have been written.
42 int total_write_messages() const { return total_write_messages_; }
43 // The total number of bytes which have been written.
44 int total_write_bytes() const { return total_write_bytes_; }
45
46 void ResetStats() {
47 max_write_time_ = std::chrono::nanoseconds::zero();
48 max_write_time_bytes_ = -1;
49 max_write_time_messages_ = -1;
50 total_write_time_ = std::chrono::nanoseconds::zero();
51 total_write_count_ = 0;
52 total_write_messages_ = 0;
53 total_write_bytes_ = 0;
Maxwell Gumleyd26e6292024-04-24 10:45:07 -060054 total_encode_duration_ = std::chrono::nanoseconds::zero();
Alexei Strots01395492023-03-20 13:59:56 -070055 }
56
Maxwell Gumleyd26e6292024-04-24 10:45:07 -060057 void UpdateStats(std::chrono::nanoseconds duration, ssize_t written,
Alexei Strots01395492023-03-20 13:59:56 -070058 int iovec_size) {
59 if (duration > max_write_time_) {
60 max_write_time_ = duration;
61 max_write_time_bytes_ = written;
62 max_write_time_messages_ = iovec_size;
63 }
64 total_write_time_ += duration;
65 ++total_write_count_;
66 total_write_messages_ += iovec_size;
67 total_write_bytes_ += written;
68 }
69
Maxwell Gumleyd26e6292024-04-24 10:45:07 -060070 // Update our total_encode_duration_ stat. This needs to be a separate
71 // function from UpdateStats because it's called from a different level in the
72 // stack.
73 void UpdateEncodeDuration(std::chrono::nanoseconds duration) {
74 total_encode_duration_ += duration;
75 }
76
Alexei Strots01395492023-03-20 13:59:56 -070077 private:
78 std::chrono::nanoseconds max_write_time_ = std::chrono::nanoseconds::zero();
79 int max_write_time_bytes_ = -1;
80 int max_write_time_messages_ = -1;
81 std::chrono::nanoseconds total_write_time_ = std::chrono::nanoseconds::zero();
Maxwell Gumleyd26e6292024-04-24 10:45:07 -060082 std::chrono::nanoseconds total_encode_duration_ =
83 std::chrono::nanoseconds::zero();
Alexei Strots01395492023-03-20 13:59:56 -070084 int total_write_count_ = 0;
85 int total_write_messages_ = 0;
86 int total_write_bytes_ = 0;
87};
88
89// Currently, all write operations only cares about out-of-space error. This is
90// a simple representation of write result.
91enum class WriteCode { kOk, kOutOfSpace };
92
93struct WriteResult {
94 WriteCode code = WriteCode::kOk;
95 size_t messages_written = 0;
96};
97
Alexei Strotsbc082d82023-05-03 08:43:42 -070098// Interface that abstract writing to log from media.
99class LogSink {
100 public:
101 LogSink() = default;
102 virtual ~LogSink() = default;
103
104 LogSink(const LogSink &) = delete;
105 LogSink &operator=(const LogSink &) = delete;
106
107 // Try to open file. App will crash if there are other than out-of-space
108 // problems with backend media.
109 virtual WriteCode OpenForWrite() = 0;
110
111 // Close the file handler.
112 virtual WriteCode Close() = 0;
113
114 // Returns true if sink is open and need to be closed.
115 virtual bool is_open() const = 0;
116
117 // Peeks messages from queue and writes it to file. Returns code when
118 // out-of-space problem occurred along with number of messages from queue that
119 // was written.
120 virtual WriteResult Write(
121 const absl::Span<const absl::Span<const uint8_t>> &queue) = 0;
122
123 // Get access to statistics related to the write operations.
124 WriteStats *WriteStatistics() { return &write_stats_; }
125
126 // Name of the log sink.
127 virtual std::string_view name() const = 0;
128
129 private:
130 WriteStats write_stats_;
131};
132
133// Source for iovec with an additional flag that pointer and size of data is
Alexei Strotsa0b99d72023-04-11 15:12:42 -0700134// aligned and be ready for O_DIRECT operation.
135struct AlignedIovec {
136 const uint8_t *data;
137 size_t size;
138 bool aligned;
139
140 AlignedIovec(const uint8_t *data, size_t size, bool aligned)
141 : data(data), size(size), aligned(aligned) {}
142};
143
144// Converts queue of pieces to write to the disk to the queue where every
145// element is either aligned for O_DIRECT operation or marked as not aligned.
146class QueueAligner {
147 public:
148 QueueAligner();
149
150 // Reads input queue and fills with aligned and unaligned pieces. It is easy
151 // to deal with smaller pieces and batch it during the write operation.
152 void FillAlignedQueue(
153 const absl::Span<const absl::Span<const uint8_t>> &queue);
154
155 const std::vector<AlignedIovec> &aligned_queue() const {
156 return aligned_queue_;
157 }
158
159 private:
160 std::vector<AlignedIovec> aligned_queue_;
161};
162
Alexei Strots01395492023-03-20 13:59:56 -0700163// FileHandler is a replacement for bare filename in log writing and reading
164// operations.
165//
166// There are a couple over-arching constraints on writing to keep track of.
167// 1) The kernel is both faster and more efficient at writing large, aligned
168// chunks with O_DIRECT set on the file. The alignment needed is specified
169// by kSector and is file system dependent.
170// 2) Not all encoders support generating round multiples of kSector of data.
171// Rather than burden the API for detecting when that is the case, we want
172// DetachedBufferWriter to be as efficient as it can at writing what given.
173// 3) Some files are small and not updated frequently. They need to be
174// flushed or we will lose data on power off. It is most efficient to write
175// as much as we can aligned by kSector and then fall back to the non direct
176// method when it has been flushed.
177// 4) Not all filesystems support O_DIRECT, and different sizes may be optimal
178// for different machines. The defaults should work decently anywhere and
179// be tunable for faster systems.
Alexei Strotsbc082d82023-05-03 08:43:42 -0700180class FileHandler : public LogSink {
Alexei Strots01395492023-03-20 13:59:56 -0700181 public:
182 // Size of an aligned sector used to detect when the data is aligned enough to
183 // use O_DIRECT instead.
184 static constexpr size_t kSector = 512u;
185
colleen61276dc2023-06-01 09:23:29 -0700186 explicit FileHandler(std::string filename, bool supports_odirect);
Alexei Strotsbc082d82023-05-03 08:43:42 -0700187 ~FileHandler() override;
Alexei Strots01395492023-03-20 13:59:56 -0700188
189 FileHandler(const FileHandler &) = delete;
190 FileHandler &operator=(const FileHandler &) = delete;
191
192 // Try to open file. App will crash if there are other than out-of-space
193 // problems with backend media.
Alexei Strotsbc082d82023-05-03 08:43:42 -0700194 WriteCode OpenForWrite() override;
Alexei Strots01395492023-03-20 13:59:56 -0700195
196 // Close the file handler.
Alexei Strotsbc082d82023-05-03 08:43:42 -0700197 WriteCode Close() override;
Alexei Strots01395492023-03-20 13:59:56 -0700198
199 // This will be true until Close() is called, unless the file couldn't be
200 // created due to running out of space.
Alexei Strotsbc082d82023-05-03 08:43:42 -0700201 bool is_open() const override { return fd_ != -1; }
Alexei Strots01395492023-03-20 13:59:56 -0700202
203 // Peeks messages from queue and writes it to file. Returns code when
204 // out-of-space problem occurred along with number of messages from queue that
205 // was written.
Austin Schuh3ebaf782023-04-07 16:03:28 -0700206 //
207 // The spans can be aligned or not, and can have any lengths. This code will
208 // write faster if the spans passed in start at aligned addresses, and are
209 // multiples of kSector long (and the data written so far is also a multiple
210 // of kSector length).
Alexei Strotsbc082d82023-05-03 08:43:42 -0700211 WriteResult Write(
212 const absl::Span<const absl::Span<const uint8_t>> &queue) override;
Alexei Strots01395492023-03-20 13:59:56 -0700213
Alexei Strotsbc082d82023-05-03 08:43:42 -0700214 // Name of the log sink mostly for informational purposes.
215 std::string_view name() const override { return filename_; }
Alexei Strots01395492023-03-20 13:59:56 -0700216
Alexei Strotsa0b99d72023-04-11 15:12:42 -0700217 // Number of bytes written in aligned mode. It is mostly for testing.
218 size_t written_aligned() const { return written_aligned_; }
219
Alexei Strotsbc082d82023-05-03 08:43:42 -0700220 protected:
221 // This is used by subclasses who need to access filename.
222 std::string_view filename() const { return filename_; }
223
Alexei Strots01395492023-03-20 13:59:56 -0700224 private:
225 // Enables O_DIRECT on the open file if it is supported. Cheap to call if it
226 // is already enabled.
227 void EnableDirect();
228 // Disables O_DIRECT on the open file if it is supported. Cheap to call if it
229 // is already disabld.
230 void DisableDirect();
231
232 bool ODirectEnabled() const { return !!(flags_ & O_DIRECT); }
233
Alexei Strotsa0b99d72023-04-11 15:12:42 -0700234 // Writes a chunk of iovecs. aligned is true if all the data is kSector byte
235 // aligned and multiples of it in length.
236 WriteCode WriteV(const std::vector<struct iovec> &iovec, bool aligned);
Alexei Strots01395492023-03-20 13:59:56 -0700237
238 const std::string filename_;
239
240 int fd_ = -1;
241
242 // List of iovecs to use with writev. This is a member variable to avoid
243 // churn.
244 std::vector<struct iovec> iovec_;
245
Alexei Strotsa0b99d72023-04-11 15:12:42 -0700246 QueueAligner queue_aligner_;
247
Alexei Strots01395492023-03-20 13:59:56 -0700248 int total_write_bytes_ = 0;
249 int last_synced_bytes_ = 0;
250
Alexei Strotsa0b99d72023-04-11 15:12:42 -0700251 size_t written_aligned_ = 0;
252
Alexei Strots01395492023-03-20 13:59:56 -0700253 bool supports_odirect_ = true;
254 int flags_ = 0;
Alexei Strots01395492023-03-20 13:59:56 -0700255};
256
Austin Schuh95460cc2023-06-26 11:53:10 -0700257// Interface to decouple reading of logs and media (file system, memory or S3).
258class LogSource {
259 public:
260 struct File {
261 std::string name;
262 size_t size;
263 };
264
265 virtual ~LogSource() = default;
266
267 // Provides a list of readable sources for log reading.
268 virtual std::vector<File> ListFiles() const = 0;
269
270 // Entry point for reading the content of log file.
271 virtual std::unique_ptr<DataDecoder> GetDecoder(
272 const std::string_view id) const = 0;
273 std::unique_ptr<DataDecoder> GetDecoder(const LogSource::File &id) const {
274 return GetDecoder(id.name);
275 }
276};
277
Alexei Strotsf08b8fb2023-04-21 19:46:08 -0700278// Interface to decouple log writing and media (file system or memory). It is
Alexei Strots01395492023-03-20 13:59:56 -0700279// handy to use for tests.
280class LogBackend {
281 public:
282 virtual ~LogBackend() = default;
283
284 // Request file-like object from the log backend. It maybe a file on a disk or
285 // in memory. id is usually generated by log namer and looks like name of the
286 // file within a log folder.
Austin Schuh95460cc2023-06-26 11:53:10 -0700287 virtual std::unique_ptr<LogSink> RequestFile(const std::string_view id) = 0;
Alexei Strotsf08b8fb2023-04-21 19:46:08 -0700288};
289
Alexei Strots01395492023-03-20 13:59:56 -0700290// Implements requests log files from file system.
Alexei Strotsf08b8fb2023-04-21 19:46:08 -0700291class FileBackend : public LogBackend, public LogSource {
Alexei Strots01395492023-03-20 13:59:56 -0700292 public:
293 // base_name is the path to the folder where log files are.
colleen61276dc2023-06-01 09:23:29 -0700294 explicit FileBackend(std::string_view base_name, bool supports_direct);
Alexei Strots01395492023-03-20 13:59:56 -0700295 ~FileBackend() override = default;
296
297 // Request file from a file system. It is not open yet.
Austin Schuh95460cc2023-06-26 11:53:10 -0700298 std::unique_ptr<LogSink> RequestFile(const std::string_view id) override;
Alexei Strots01395492023-03-20 13:59:56 -0700299
Alexei Strotsf08b8fb2023-04-21 19:46:08 -0700300 // List all files that looks like log files under base_name.
Austin Schuh95460cc2023-06-26 11:53:10 -0700301 std::vector<File> ListFiles() const override;
Alexei Strotsf08b8fb2023-04-21 19:46:08 -0700302
303 // Open decoder to read the content of the file.
Austin Schuh95460cc2023-06-26 11:53:10 -0700304 std::unique_ptr<DataDecoder> GetDecoder(
305 const std::string_view id) const override;
Alexei Strotsf08b8fb2023-04-21 19:46:08 -0700306
Alexei Strots01395492023-03-20 13:59:56 -0700307 private:
colleen61276dc2023-06-01 09:23:29 -0700308 const bool supports_odirect_;
Alexei Strots01395492023-03-20 13:59:56 -0700309 const std::string base_name_;
310 const std::string_view separator_;
311};
312
313// Provides a file backend that supports renaming of the base log folder and
314// temporary files.
315class RenamableFileBackend : public LogBackend {
316 public:
317 // Adds call to rename, when closed.
318 class RenamableFileHandler final : public FileHandler {
319 public:
colleen61276dc2023-06-01 09:23:29 -0700320 RenamableFileHandler(RenamableFileBackend *owner, std::string filename,
321 bool supports_odirect)
322 : FileHandler(std::move(filename), supports_odirect), owner_(owner) {}
Alexei Strots01395492023-03-20 13:59:56 -0700323 ~RenamableFileHandler() final = default;
324
Alexei Strotscaf17d32023-04-03 22:31:11 -0700325 // Closes and if needed renames file.
Alexei Strots01395492023-03-20 13:59:56 -0700326 WriteCode Close() final;
327
328 private:
329 RenamableFileBackend *owner_;
330 };
331
colleen61276dc2023-06-01 09:23:29 -0700332 explicit RenamableFileBackend(std::string_view base_name,
333 bool supports_odirect);
Alexei Strots01395492023-03-20 13:59:56 -0700334 ~RenamableFileBackend() = default;
335
336 // Request file from a file system. It is not open yet.
Austin Schuh95460cc2023-06-26 11:53:10 -0700337 std::unique_ptr<LogSink> RequestFile(const std::string_view id) override;
Alexei Strots01395492023-03-20 13:59:56 -0700338
339 // TODO (Alexei): it is called by Logger, and left here for compatibility.
340 // Logger should not call it.
Alexei Strotscaf17d32023-04-03 22:31:11 -0700341 std::string_view base_name() const { return base_name_; }
Alexei Strots01395492023-03-20 13:59:56 -0700342
343 // If temp files are enabled, then this will write files with the .tmp
344 // suffix, and then rename them to the desired name after they are fully
345 // written.
346 //
347 // This is useful to enable incremental copying of the log files.
348 //
349 // Defaults to writing directly to the final filename.
350 void EnableTempFiles();
351
352 // Moves the current log location to the new name. Returns true if a change
353 // was made, false otherwise.
354 // Only renaming the folder is supported, not the file base name.
355 bool RenameLogBase(std::string_view new_base_name);
356
357 private:
358 // This function called after file closed, to adjust file names in case of
359 // base name was changed or temp files enabled.
360 WriteCode RenameFileAfterClose(std::string_view filename);
361
colleen61276dc2023-06-01 09:23:29 -0700362 const bool supports_odirect_;
Alexei Strots01395492023-03-20 13:59:56 -0700363 std::string base_name_;
364 std::string_view separator_;
365
366 bool use_temp_files_ = false;
367 std::string_view temp_suffix_;
368
369 std::string old_base_name_;
370};
371
372} // namespace aos::logger
373
374#endif // AOS_EVENTS_LOGGING_LOG_BACKEND_H_