blob: 91f048c1a31581d603610aed2302c2c3f5ff9384 [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 }
33 // The total number of writes which have been performed.
34 int total_write_count() const { return total_write_count_; }
35 // The total number of messages which have been written.
36 int total_write_messages() const { return total_write_messages_; }
37 // The total number of bytes which have been written.
38 int total_write_bytes() const { return total_write_bytes_; }
39
40 void ResetStats() {
41 max_write_time_ = std::chrono::nanoseconds::zero();
42 max_write_time_bytes_ = -1;
43 max_write_time_messages_ = -1;
44 total_write_time_ = std::chrono::nanoseconds::zero();
45 total_write_count_ = 0;
46 total_write_messages_ = 0;
47 total_write_bytes_ = 0;
48 }
49
50 void UpdateStats(aos::monotonic_clock::duration duration, ssize_t written,
51 int iovec_size) {
52 if (duration > max_write_time_) {
53 max_write_time_ = duration;
54 max_write_time_bytes_ = written;
55 max_write_time_messages_ = iovec_size;
56 }
57 total_write_time_ += duration;
58 ++total_write_count_;
59 total_write_messages_ += iovec_size;
60 total_write_bytes_ += written;
61 }
62
63 private:
64 std::chrono::nanoseconds max_write_time_ = std::chrono::nanoseconds::zero();
65 int max_write_time_bytes_ = -1;
66 int max_write_time_messages_ = -1;
67 std::chrono::nanoseconds total_write_time_ = std::chrono::nanoseconds::zero();
68 int total_write_count_ = 0;
69 int total_write_messages_ = 0;
70 int total_write_bytes_ = 0;
71};
72
73// Currently, all write operations only cares about out-of-space error. This is
74// a simple representation of write result.
75enum class WriteCode { kOk, kOutOfSpace };
76
77struct WriteResult {
78 WriteCode code = WriteCode::kOk;
79 size_t messages_written = 0;
80};
81
Alexei Strotsbc082d82023-05-03 08:43:42 -070082// Interface that abstract writing to log from media.
83class LogSink {
84 public:
85 LogSink() = default;
86 virtual ~LogSink() = default;
87
88 LogSink(const LogSink &) = delete;
89 LogSink &operator=(const LogSink &) = delete;
90
91 // Try to open file. App will crash if there are other than out-of-space
92 // problems with backend media.
93 virtual WriteCode OpenForWrite() = 0;
94
95 // Close the file handler.
96 virtual WriteCode Close() = 0;
97
98 // Returns true if sink is open and need to be closed.
99 virtual bool is_open() const = 0;
100
101 // Peeks messages from queue and writes it to file. Returns code when
102 // out-of-space problem occurred along with number of messages from queue that
103 // was written.
104 virtual WriteResult Write(
105 const absl::Span<const absl::Span<const uint8_t>> &queue) = 0;
106
107 // Get access to statistics related to the write operations.
108 WriteStats *WriteStatistics() { return &write_stats_; }
109
110 // Name of the log sink.
111 virtual std::string_view name() const = 0;
112
113 private:
114 WriteStats write_stats_;
115};
116
117// Source for iovec with an additional flag that pointer and size of data is
Alexei Strotsa0b99d72023-04-11 15:12:42 -0700118// aligned and be ready for O_DIRECT operation.
119struct AlignedIovec {
120 const uint8_t *data;
121 size_t size;
122 bool aligned;
123
124 AlignedIovec(const uint8_t *data, size_t size, bool aligned)
125 : data(data), size(size), aligned(aligned) {}
126};
127
128// Converts queue of pieces to write to the disk to the queue where every
129// element is either aligned for O_DIRECT operation or marked as not aligned.
130class QueueAligner {
131 public:
132 QueueAligner();
133
134 // Reads input queue and fills with aligned and unaligned pieces. It is easy
135 // to deal with smaller pieces and batch it during the write operation.
136 void FillAlignedQueue(
137 const absl::Span<const absl::Span<const uint8_t>> &queue);
138
139 const std::vector<AlignedIovec> &aligned_queue() const {
140 return aligned_queue_;
141 }
142
143 private:
144 std::vector<AlignedIovec> aligned_queue_;
145};
146
Alexei Strots01395492023-03-20 13:59:56 -0700147// FileHandler is a replacement for bare filename in log writing and reading
148// operations.
149//
150// There are a couple over-arching constraints on writing to keep track of.
151// 1) The kernel is both faster and more efficient at writing large, aligned
152// chunks with O_DIRECT set on the file. The alignment needed is specified
153// by kSector and is file system dependent.
154// 2) Not all encoders support generating round multiples of kSector of data.
155// Rather than burden the API for detecting when that is the case, we want
156// DetachedBufferWriter to be as efficient as it can at writing what given.
157// 3) Some files are small and not updated frequently. They need to be
158// flushed or we will lose data on power off. It is most efficient to write
159// as much as we can aligned by kSector and then fall back to the non direct
160// method when it has been flushed.
161// 4) Not all filesystems support O_DIRECT, and different sizes may be optimal
162// for different machines. The defaults should work decently anywhere and
163// be tunable for faster systems.
Alexei Strotsbc082d82023-05-03 08:43:42 -0700164class FileHandler : public LogSink {
Alexei Strots01395492023-03-20 13:59:56 -0700165 public:
166 // Size of an aligned sector used to detect when the data is aligned enough to
167 // use O_DIRECT instead.
168 static constexpr size_t kSector = 512u;
169
colleen61276dc2023-06-01 09:23:29 -0700170 explicit FileHandler(std::string filename, bool supports_odirect);
Alexei Strotsbc082d82023-05-03 08:43:42 -0700171 ~FileHandler() override;
Alexei Strots01395492023-03-20 13:59:56 -0700172
173 FileHandler(const FileHandler &) = delete;
174 FileHandler &operator=(const FileHandler &) = delete;
175
176 // Try to open file. App will crash if there are other than out-of-space
177 // problems with backend media.
Alexei Strotsbc082d82023-05-03 08:43:42 -0700178 WriteCode OpenForWrite() override;
Alexei Strots01395492023-03-20 13:59:56 -0700179
180 // Close the file handler.
Alexei Strotsbc082d82023-05-03 08:43:42 -0700181 WriteCode Close() override;
Alexei Strots01395492023-03-20 13:59:56 -0700182
183 // This will be true until Close() is called, unless the file couldn't be
184 // created due to running out of space.
Alexei Strotsbc082d82023-05-03 08:43:42 -0700185 bool is_open() const override { return fd_ != -1; }
Alexei Strots01395492023-03-20 13:59:56 -0700186
187 // Peeks messages from queue and writes it to file. Returns code when
188 // out-of-space problem occurred along with number of messages from queue that
189 // was written.
Austin Schuh3ebaf782023-04-07 16:03:28 -0700190 //
191 // The spans can be aligned or not, and can have any lengths. This code will
192 // write faster if the spans passed in start at aligned addresses, and are
193 // multiples of kSector long (and the data written so far is also a multiple
194 // of kSector length).
Alexei Strotsbc082d82023-05-03 08:43:42 -0700195 WriteResult Write(
196 const absl::Span<const absl::Span<const uint8_t>> &queue) override;
Alexei Strots01395492023-03-20 13:59:56 -0700197
Alexei Strotsbc082d82023-05-03 08:43:42 -0700198 // Name of the log sink mostly for informational purposes.
199 std::string_view name() const override { return filename_; }
Alexei Strots01395492023-03-20 13:59:56 -0700200
Alexei Strotsa0b99d72023-04-11 15:12:42 -0700201 // Number of bytes written in aligned mode. It is mostly for testing.
202 size_t written_aligned() const { return written_aligned_; }
203
Alexei Strotsbc082d82023-05-03 08:43:42 -0700204 protected:
205 // This is used by subclasses who need to access filename.
206 std::string_view filename() const { return filename_; }
207
Alexei Strots01395492023-03-20 13:59:56 -0700208 private:
209 // Enables O_DIRECT on the open file if it is supported. Cheap to call if it
210 // is already enabled.
211 void EnableDirect();
212 // Disables O_DIRECT on the open file if it is supported. Cheap to call if it
213 // is already disabld.
214 void DisableDirect();
215
216 bool ODirectEnabled() const { return !!(flags_ & O_DIRECT); }
217
Alexei Strotsa0b99d72023-04-11 15:12:42 -0700218 // Writes a chunk of iovecs. aligned is true if all the data is kSector byte
219 // aligned and multiples of it in length.
220 WriteCode WriteV(const std::vector<struct iovec> &iovec, bool aligned);
Alexei Strots01395492023-03-20 13:59:56 -0700221
222 const std::string filename_;
223
224 int fd_ = -1;
225
226 // List of iovecs to use with writev. This is a member variable to avoid
227 // churn.
228 std::vector<struct iovec> iovec_;
229
Alexei Strotsa0b99d72023-04-11 15:12:42 -0700230 QueueAligner queue_aligner_;
231
Alexei Strots01395492023-03-20 13:59:56 -0700232 int total_write_bytes_ = 0;
233 int last_synced_bytes_ = 0;
234
Alexei Strotsa0b99d72023-04-11 15:12:42 -0700235 size_t written_aligned_ = 0;
236
Alexei Strots01395492023-03-20 13:59:56 -0700237 bool supports_odirect_ = true;
238 int flags_ = 0;
Alexei Strots01395492023-03-20 13:59:56 -0700239};
240
Austin Schuh95460cc2023-06-26 11:53:10 -0700241// Interface to decouple reading of logs and media (file system, memory or S3).
242class LogSource {
243 public:
244 struct File {
245 std::string name;
246 size_t size;
247 };
248
249 virtual ~LogSource() = default;
250
251 // Provides a list of readable sources for log reading.
252 virtual std::vector<File> ListFiles() const = 0;
253
254 // Entry point for reading the content of log file.
255 virtual std::unique_ptr<DataDecoder> GetDecoder(
256 const std::string_view id) const = 0;
257 std::unique_ptr<DataDecoder> GetDecoder(const LogSource::File &id) const {
258 return GetDecoder(id.name);
259 }
260};
261
Alexei Strotsf08b8fb2023-04-21 19:46:08 -0700262// Interface to decouple log writing and media (file system or memory). It is
Alexei Strots01395492023-03-20 13:59:56 -0700263// handy to use for tests.
264class LogBackend {
265 public:
266 virtual ~LogBackend() = default;
267
268 // Request file-like object from the log backend. It maybe a file on a disk or
269 // in memory. id is usually generated by log namer and looks like name of the
270 // file within a log folder.
Austin Schuh95460cc2023-06-26 11:53:10 -0700271 virtual std::unique_ptr<LogSink> RequestFile(const std::string_view id) = 0;
Alexei Strotsf08b8fb2023-04-21 19:46:08 -0700272};
273
Alexei Strots01395492023-03-20 13:59:56 -0700274// Implements requests log files from file system.
Alexei Strotsf08b8fb2023-04-21 19:46:08 -0700275class FileBackend : public LogBackend, public LogSource {
Alexei Strots01395492023-03-20 13:59:56 -0700276 public:
277 // base_name is the path to the folder where log files are.
colleen61276dc2023-06-01 09:23:29 -0700278 explicit FileBackend(std::string_view base_name, bool supports_direct);
Alexei Strots01395492023-03-20 13:59:56 -0700279 ~FileBackend() override = default;
280
281 // Request file from a file system. It is not open yet.
Austin Schuh95460cc2023-06-26 11:53:10 -0700282 std::unique_ptr<LogSink> RequestFile(const std::string_view id) override;
Alexei Strots01395492023-03-20 13:59:56 -0700283
Alexei Strotsf08b8fb2023-04-21 19:46:08 -0700284 // List all files that looks like log files under base_name.
Austin Schuh95460cc2023-06-26 11:53:10 -0700285 std::vector<File> ListFiles() const override;
Alexei Strotsf08b8fb2023-04-21 19:46:08 -0700286
287 // Open decoder to read the content of the file.
Austin Schuh95460cc2023-06-26 11:53:10 -0700288 std::unique_ptr<DataDecoder> GetDecoder(
289 const std::string_view id) const override;
Alexei Strotsf08b8fb2023-04-21 19:46:08 -0700290
Alexei Strots01395492023-03-20 13:59:56 -0700291 private:
colleen61276dc2023-06-01 09:23:29 -0700292 const bool supports_odirect_;
Alexei Strots01395492023-03-20 13:59:56 -0700293 const std::string base_name_;
294 const std::string_view separator_;
295};
296
297// Provides a file backend that supports renaming of the base log folder and
298// temporary files.
299class RenamableFileBackend : public LogBackend {
300 public:
301 // Adds call to rename, when closed.
302 class RenamableFileHandler final : public FileHandler {
303 public:
colleen61276dc2023-06-01 09:23:29 -0700304 RenamableFileHandler(RenamableFileBackend *owner, std::string filename,
305 bool supports_odirect)
306 : FileHandler(std::move(filename), supports_odirect), owner_(owner) {}
Alexei Strots01395492023-03-20 13:59:56 -0700307 ~RenamableFileHandler() final = default;
308
Alexei Strotscaf17d32023-04-03 22:31:11 -0700309 // Closes and if needed renames file.
Alexei Strots01395492023-03-20 13:59:56 -0700310 WriteCode Close() final;
311
312 private:
313 RenamableFileBackend *owner_;
314 };
315
colleen61276dc2023-06-01 09:23:29 -0700316 explicit RenamableFileBackend(std::string_view base_name,
317 bool supports_odirect);
Alexei Strots01395492023-03-20 13:59:56 -0700318 ~RenamableFileBackend() = default;
319
320 // Request file from a file system. It is not open yet.
Austin Schuh95460cc2023-06-26 11:53:10 -0700321 std::unique_ptr<LogSink> RequestFile(const std::string_view id) override;
Alexei Strots01395492023-03-20 13:59:56 -0700322
323 // TODO (Alexei): it is called by Logger, and left here for compatibility.
324 // Logger should not call it.
Alexei Strotscaf17d32023-04-03 22:31:11 -0700325 std::string_view base_name() const { return base_name_; }
Alexei Strots01395492023-03-20 13:59:56 -0700326
327 // If temp files are enabled, then this will write files with the .tmp
328 // suffix, and then rename them to the desired name after they are fully
329 // written.
330 //
331 // This is useful to enable incremental copying of the log files.
332 //
333 // Defaults to writing directly to the final filename.
334 void EnableTempFiles();
335
336 // Moves the current log location to the new name. Returns true if a change
337 // was made, false otherwise.
338 // Only renaming the folder is supported, not the file base name.
339 bool RenameLogBase(std::string_view new_base_name);
340
341 private:
342 // This function called after file closed, to adjust file names in case of
343 // base name was changed or temp files enabled.
344 WriteCode RenameFileAfterClose(std::string_view filename);
345
colleen61276dc2023-06-01 09:23:29 -0700346 const bool supports_odirect_;
Alexei Strots01395492023-03-20 13:59:56 -0700347 std::string base_name_;
348 std::string_view separator_;
349
350 bool use_temp_files_ = false;
351 std::string_view temp_suffix_;
352
353 std::string old_base_name_;
354};
355
356} // namespace aos::logger
357
358#endif // AOS_EVENTS_LOGGING_LOG_BACKEND_H_