blob: 6cd9a7de3afb26bce4f0d1410a2e94a6c1609adb [file] [log] [blame]
Alexei Strots01395492023-03-20 13:59:56 -07001#include "aos/events/logging/log_backend.h"
2
3#include <dirent.h>
4
5#include <filesystem>
6
Alexei Strotsf08b8fb2023-04-21 19:46:08 -07007#include "absl/strings/match.h"
Alexei Strots01395492023-03-20 13:59:56 -07008#include "absl/strings/str_cat.h"
Alexei Strots01395492023-03-20 13:59:56 -07009#include "glog/logging.h"
10
Alexei Strotsf08b8fb2023-04-21 19:46:08 -070011#include "aos/events/logging/file_operations.h"
Philipp Schrader790cb542023-07-05 21:06:52 -070012#include "aos/util/file.h"
13
Austin Schuh3ebaf782023-04-07 16:03:28 -070014DEFINE_bool(
15 sync, false,
16 "If true, sync data to disk as we go so we don't get too far ahead. Also "
17 "fadvise that we are done with the memory once it hits disk.");
Alexei Strots01395492023-03-20 13:59:56 -070018
Alexei Strotsa0b99d72023-04-11 15:12:42 -070019DEFINE_uint32(queue_reserve, 32, "Pre-reserved size of write queue.");
20
Alexei Strots01395492023-03-20 13:59:56 -070021namespace aos::logger {
22namespace {
23constexpr const char *kTempExtension = ".tmp";
Alexei Strotsa0b99d72023-04-11 15:12:42 -070024
25// Assuming that kSector is power of 2, it aligns address to the left size.
26inline size_t AlignToLeft(size_t value) {
27 return value & (~(FileHandler::kSector - 1));
28}
29
30inline bool IsAligned(size_t value) {
31 return value % FileHandler::kSector == 0;
32}
33
34inline bool IsAlignedStart(const absl::Span<const uint8_t> span) {
35 return (reinterpret_cast<size_t>(span.data()) % FileHandler::kSector) == 0;
36}
37
38inline bool IsAlignedLength(const absl::Span<const uint8_t> span) {
39 return (span.size() % FileHandler::kSector) == 0;
40}
41
42} // namespace
43
44logger::QueueAligner::QueueAligner() {
45 aligned_queue_.reserve(FLAGS_queue_reserve);
46}
47
48void logger::QueueAligner::FillAlignedQueue(
49 const absl::Span<const absl::Span<const uint8_t>> &queue) {
50 aligned_queue_.clear();
51
Austin Schuh790ec9c2023-05-03 13:47:15 -070052 size_t queue_index = 0;
Alexei Strotsa0b99d72023-04-11 15:12:42 -070053 for (const auto &span : queue) {
Austin Schuh790ec9c2023-05-03 13:47:15 -070054 ++queue_index;
Alexei Strotsa0b99d72023-04-11 15:12:42 -070055 // Generally, every span might have 3 optional parts (i.e. 2^3 cases):
56 // 1. unaligned prefix - from start till first aligned block.
57 // 2. aligned main - block with aligned start and size
58 // 3. unaligned suffix - block with aligned start, and size less than one
59 // sector. If size of the span is less than 1 sector, let's call it prefix.
60
61 auto *data = span.data();
62 size_t size = span.size();
63 const auto start = reinterpret_cast<size_t>(data);
64 VLOG(2) << "Consider span starting at " << std::hex << start
65 << " with size " << size;
66
Austin Schuh790ec9c2023-05-03 13:47:15 -070067 CHECK_GT(size, 0u)
68 << ": Nobody should be sending empty messages. Queue index "
69 << (queue_index - 1) << " out of " << queue.size();
Alexei Strotsa0b99d72023-04-11 15:12:42 -070070
71 const auto next_aligned =
72 IsAligned(start) ? start : AlignToLeft(start) + FileHandler::kSector;
73 const auto prefix_size = next_aligned - start;
74 VLOG(2) << "Calculated prefix size " << std::hex << prefix_size;
75
76 if (prefix_size >= size) {
77 // size of prefix >= size of span - alignment is not possible, accept the
78 // whole span
79 VLOG(2) << "Only prefix found";
80 CHECK_GT(size, 0u);
81 aligned_queue_.emplace_back(data, size, false);
82 continue;
83 }
84 CHECK_LT(prefix_size, FileHandler::kSector)
85 << ": Wrong calculation of 'next' aligned position";
86 if (prefix_size > 0) {
87 // Cut the prefix and move to the main part.
88 VLOG(2) << "Cutting prefix at " << std::hex << start << " of size "
89 << prefix_size;
90 aligned_queue_.emplace_back(data, prefix_size, false);
91 data += prefix_size;
92 size -= prefix_size;
93 CHECK(data <= span.data() + span.size()) << " :Boundaries after prefix";
94 }
95
96 if (IsAligned(size)) {
97 // the rest is aligned.
98 VLOG(2) << "Returning aligned main part";
99 CHECK_GT(size, 0u);
100 aligned_queue_.emplace_back(data, size, true);
101 continue;
102 }
103
104 const auto aligned_size = AlignToLeft(size);
105 CHECK(aligned_size < size) << ": Wrong calculation of 'main' size";
106 if (aligned_size > 0) {
107 VLOG(2) << "Cutting main part starting " << std::hex
108 << reinterpret_cast<size_t>(data) << " of size " << aligned_size;
109 aligned_queue_.emplace_back(data, aligned_size, true);
110
111 data += aligned_size;
112 size -= aligned_size;
113 CHECK(data <= span.data() + span.size()) << " :Boundaries after main";
114 }
115
116 VLOG(2) << "Cutting suffix part starting " << std::hex
117 << reinterpret_cast<size_t>(data) << " of size " << size;
118 CHECK_GT(size, 0u);
119 aligned_queue_.emplace_back(data, size, false);
120 }
Alexei Strots01395492023-03-20 13:59:56 -0700121}
122
colleen61276dc2023-06-01 09:23:29 -0700123FileHandler::FileHandler(std::string filename, bool supports_odirect)
124 : filename_(std::move(filename)), supports_odirect_(supports_odirect) {}
Alexei Strots01395492023-03-20 13:59:56 -0700125
126FileHandler::~FileHandler() { Close(); }
127
128WriteCode FileHandler::OpenForWrite() {
129 iovec_.reserve(10);
130 if (!aos::util::MkdirPIfSpace(filename_, 0777)) {
131 return WriteCode::kOutOfSpace;
132 } else {
133 fd_ = open(filename_.c_str(), O_RDWR | O_CLOEXEC | O_CREAT | O_EXCL, 0774);
134 if (fd_ == -1 && errno == ENOSPC) {
135 return WriteCode::kOutOfSpace;
136 } else {
137 PCHECK(fd_ != -1) << ": Failed to open " << filename_ << " for writing";
138 VLOG(1) << "Opened " << filename_ << " for writing";
139 }
140
141 flags_ = fcntl(fd_, F_GETFL, 0);
Alexei Strotsbc082d82023-05-03 08:43:42 -0700142 PCHECK(flags_ >= 0) << ": Failed to get flags for " << filename_;
Alexei Strots01395492023-03-20 13:59:56 -0700143
144 EnableDirect();
145
146 CHECK(std::filesystem::exists(filename_));
147
148 return WriteCode::kOk;
149 }
150}
151
152void FileHandler::EnableDirect() {
153 if (supports_odirect_ && !ODirectEnabled()) {
154 const int new_flags = flags_ | O_DIRECT;
155 // Track if we failed to set O_DIRECT. Note: Austin hasn't seen this call
156 // fail. The write call tends to fail instead.
157 if (fcntl(fd_, F_SETFL, new_flags) == -1) {
Alexei Strotsbc082d82023-05-03 08:43:42 -0700158 PLOG(WARNING) << "Failed to set O_DIRECT on " << filename_;
Alexei Strots01395492023-03-20 13:59:56 -0700159 supports_odirect_ = false;
160 } else {
Alexei Strotsbc082d82023-05-03 08:43:42 -0700161 VLOG(1) << "Enabled O_DIRECT on " << filename_;
Alexei Strots01395492023-03-20 13:59:56 -0700162 flags_ = new_flags;
163 }
164 }
165}
166
167void FileHandler::DisableDirect() {
168 if (supports_odirect_ && ODirectEnabled()) {
169 flags_ = flags_ & (~O_DIRECT);
170 PCHECK(fcntl(fd_, F_SETFL, flags_) != -1) << ": Failed to disable O_DIRECT";
Alexei Strotsbc082d82023-05-03 08:43:42 -0700171 VLOG(1) << "Disabled O_DIRECT on " << filename_;
Alexei Strots01395492023-03-20 13:59:56 -0700172 }
173}
174
175WriteResult FileHandler::Write(
176 const absl::Span<const absl::Span<const uint8_t>> &queue) {
177 iovec_.clear();
Austin Schuh3ebaf782023-04-07 16:03:28 -0700178 CHECK_LE(queue.size(), static_cast<size_t>(IOV_MAX));
Alexei Strotsa0b99d72023-04-11 15:12:42 -0700179
180 queue_aligner_.FillAlignedQueue(queue);
181 CHECK_LE(queue_aligner_.aligned_queue().size(), static_cast<size_t>(IOV_MAX));
Alexei Strots01395492023-03-20 13:59:56 -0700182
183 // Ok, we now need to figure out if we were aligned, and if we were, how much
184 // of the data we are being asked to write is aligned.
185 //
Austin Schuh3ebaf782023-04-07 16:03:28 -0700186 // When writing with O_DIRECT, the kernel only will accept writes where the
187 // offset into the file is a multiple of kSector, the data is aligned to
188 // kSector in memory, and the length being written is a multiple of kSector.
189 // Some of the callers use an aligned ResizeableBuffer to generate 512 byte
190 // aligned buffers for this code to find and use.
Alexei Strotsa0b99d72023-04-11 15:12:42 -0700191 bool was_aligned = IsAligned(total_write_bytes_);
192 VLOG(1) << "Started " << (was_aligned ? "aligned" : "unaligned")
193 << " at offset " << total_write_bytes_ << " on " << filename();
Austin Schuh9e8df9e2023-05-03 08:28:29 -0700194
Alexei Strotscaf17d32023-04-03 22:31:11 -0700195 // Walk through aligned queue and batch writes based on aligned flag
Alexei Strotsa0b99d72023-04-11 15:12:42 -0700196 for (const auto &item : queue_aligner_.aligned_queue()) {
197 if (was_aligned != item.aligned) {
198 // Switching aligned context. Let's flush current batch.
199 if (!iovec_.empty()) {
200 // Flush current queue if we need.
201 const auto code = WriteV(iovec_, was_aligned);
Austin Schuh3ebaf782023-04-07 16:03:28 -0700202 if (code == WriteCode::kOutOfSpace) {
Alexei Strotsa0b99d72023-04-11 15:12:42 -0700203 // We cannot say anything about what number of messages was written
204 // for sure.
Austin Schuh3ebaf782023-04-07 16:03:28 -0700205 return {
206 .code = code,
Alexei Strotsa0b99d72023-04-11 15:12:42 -0700207 .messages_written = queue.size(),
Austin Schuh3ebaf782023-04-07 16:03:28 -0700208 };
209 }
Alexei Strotsa0b99d72023-04-11 15:12:42 -0700210 iovec_.clear();
Austin Schuh3ebaf782023-04-07 16:03:28 -0700211 }
Alexei Strotsa0b99d72023-04-11 15:12:42 -0700212 // Write queue is flushed. WriteV updates the total_write_bytes_.
213 was_aligned = IsAligned(total_write_bytes_) && item.aligned;
Alexei Strots01395492023-03-20 13:59:56 -0700214 }
Alexei Strotsa0b99d72023-04-11 15:12:42 -0700215 iovec_.push_back(
216 {.iov_base = const_cast<uint8_t *>(item.data), .iov_len = item.size});
Alexei Strots01395492023-03-20 13:59:56 -0700217 }
218
Alexei Strotsa0b99d72023-04-11 15:12:42 -0700219 WriteCode result_code = WriteCode::kOk;
220 if (!iovec_.empty()) {
221 // Flush current queue if we need.
222 result_code = WriteV(iovec_, was_aligned);
223 }
Alexei Strots01395492023-03-20 13:59:56 -0700224 return {
Alexei Strotsa0b99d72023-04-11 15:12:42 -0700225 .code = result_code,
Austin Schuh3ebaf782023-04-07 16:03:28 -0700226 .messages_written = queue.size(),
Alexei Strots01395492023-03-20 13:59:56 -0700227 };
228}
229
Alexei Strotsa0b99d72023-04-11 15:12:42 -0700230WriteCode FileHandler::WriteV(const std::vector<struct iovec> &iovec,
231 bool aligned) {
Alexei Strots01395492023-03-20 13:59:56 -0700232 // Configure the file descriptor to match the mode we should be in. This is
233 // safe to over-call since it only does the syscall if needed.
234 if (aligned) {
235 EnableDirect();
236 } else {
237 DisableDirect();
238 }
239
Alexei Strotsa0b99d72023-04-11 15:12:42 -0700240 VLOG(2) << "Flushing queue of " << iovec.size() << " elements, "
241 << (aligned ? "aligned" : "unaligned");
242
243 CHECK_GT(iovec.size(), 0u);
Alexei Strots01395492023-03-20 13:59:56 -0700244 const auto start = aos::monotonic_clock::now();
Alexei Strots01395492023-03-20 13:59:56 -0700245
Alexei Strotsa0b99d72023-04-11 15:12:42 -0700246 // Validation of alignment assumptions.
Austin Schuh9e8df9e2023-05-03 08:28:29 -0700247 if (aligned) {
Alexei Strotsa0b99d72023-04-11 15:12:42 -0700248 CHECK(IsAligned(total_write_bytes_))
Austin Schuh9e8df9e2023-05-03 08:28:29 -0700249 << ": Failed after writing " << total_write_bytes_
250 << " to the file, attempting aligned write with unaligned start.";
Alexei Strotsa0b99d72023-04-11 15:12:42 -0700251
252 for (const auto &iovec_item : iovec) {
Austin Schuh9e8df9e2023-05-03 08:28:29 -0700253 absl::Span<const uint8_t> data(
Alexei Strotsa0b99d72023-04-11 15:12:42 -0700254 reinterpret_cast<const uint8_t *>(iovec_item.iov_base),
255 iovec_item.iov_len);
256 VLOG(2) << " iov_base " << static_cast<void *>(iovec_item.iov_base)
257 << ", iov_len " << iovec_item.iov_len;
Austin Schuh9e8df9e2023-05-03 08:28:29 -0700258 CHECK(IsAlignedStart(data) && IsAlignedLength(data));
Austin Schuh9e8df9e2023-05-03 08:28:29 -0700259 }
260 }
261
Alexei Strotsa0b99d72023-04-11 15:12:42 -0700262 // Calculation of expected written size.
263 size_t counted_size = 0;
264 for (const auto &iovec_item : iovec) {
265 CHECK_GT(iovec_item.iov_len, 0u);
266 counted_size += iovec_item.iov_len;
Austin Schuh3ebaf782023-04-07 16:03:28 -0700267 }
268
Alexei Strotsa0b99d72023-04-11 15:12:42 -0700269 VLOG(2) << "Going to write " << counted_size;
270 CHECK_GT(counted_size, 0u);
271
272 const ssize_t written = writev(fd_, iovec.data(), iovec.size());
273 VLOG(2) << "Wrote " << written << ", for iovec size " << iovec.size();
274
275 const auto end = aos::monotonic_clock::now();
276 if (written == -1 && errno == ENOSPC) {
277 return WriteCode::kOutOfSpace;
278 }
279 PCHECK(written >= 0) << ": write failed, got " << written;
280 if (written < static_cast<ssize_t>(counted_size)) {
281 // Sometimes this happens instead of ENOSPC. On a real filesystem, this
282 // never seems to happen in any other case. If we ever want to log to a
283 // socket, this will happen more often. However, until we get there, we'll
284 // just assume it means we ran out of space.
285 return WriteCode::kOutOfSpace;
286 }
Austin Schuh3ebaf782023-04-07 16:03:28 -0700287
Austin Schuh9e8df9e2023-05-03 08:28:29 -0700288 if (FLAGS_sync) {
Alexei Strots01395492023-03-20 13:59:56 -0700289 // Flush asynchronously and force the data out of the cache.
290 sync_file_range(fd_, total_write_bytes_, written, SYNC_FILE_RANGE_WRITE);
291 if (last_synced_bytes_ != 0) {
292 // Per Linus' recommendation online on how to do fast file IO, do a
293 // blocking flush of the previous write chunk, and then tell the kernel to
294 // drop the pages from the cache. This makes sure we can't get too far
295 // ahead.
296 sync_file_range(fd_, last_synced_bytes_,
297 total_write_bytes_ - last_synced_bytes_,
298 SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE |
299 SYNC_FILE_RANGE_WAIT_AFTER);
300 posix_fadvise(fd_, last_synced_bytes_,
301 total_write_bytes_ - last_synced_bytes_,
302 POSIX_FADV_DONTNEED);
Alexei Strots01395492023-03-20 13:59:56 -0700303 }
Austin Schuh3c4029f2023-04-13 12:09:55 -0700304 last_synced_bytes_ = total_write_bytes_;
Alexei Strots01395492023-03-20 13:59:56 -0700305 }
306
Alexei Strots01395492023-03-20 13:59:56 -0700307 total_write_bytes_ += written;
Alexei Strotsa0b99d72023-04-11 15:12:42 -0700308 if (aligned) {
309 written_aligned_ += written;
310 }
Alexei Strotsbc082d82023-05-03 08:43:42 -0700311 WriteStatistics()->UpdateStats(end - start, written, iovec.size());
Alexei Strots01395492023-03-20 13:59:56 -0700312 return WriteCode::kOk;
313}
314
315WriteCode FileHandler::Close() {
316 if (!is_open()) {
317 return WriteCode::kOk;
318 }
319 bool ran_out_of_space = false;
320 if (close(fd_) == -1) {
321 if (errno == ENOSPC) {
322 ran_out_of_space = true;
323 } else {
324 PLOG(ERROR) << "Closing log file failed";
325 }
326 }
327 fd_ = -1;
328 VLOG(1) << "Closed " << filename_;
329 return ran_out_of_space ? WriteCode::kOutOfSpace : WriteCode::kOk;
330}
331
colleen61276dc2023-06-01 09:23:29 -0700332FileBackend::FileBackend(std::string_view base_name, bool supports_odirect)
333 : supports_odirect_(supports_odirect),
334 base_name_(base_name),
335 separator_(base_name_.back() == '/' ? "" : "_") {}
Alexei Strots01395492023-03-20 13:59:56 -0700336
Alexei Strotsbc082d82023-05-03 08:43:42 -0700337std::unique_ptr<LogSink> FileBackend::RequestFile(std::string_view id) {
Alexei Strots01395492023-03-20 13:59:56 -0700338 const std::string filename = absl::StrCat(base_name_, separator_, id);
colleen61276dc2023-06-01 09:23:29 -0700339 return std::make_unique<FileHandler>(filename, supports_odirect_);
Alexei Strots01395492023-03-20 13:59:56 -0700340}
341
Alexei Strotsf08b8fb2023-04-21 19:46:08 -0700342std::vector<std::string> FileBackend::ListFiles() const {
343 std::filesystem::path directory(base_name_);
344 if (!is_directory(directory)) {
345 directory = directory.parent_path();
346 }
347 internal::LocalFileOperations operations(directory.string());
348 std::vector<std::string> files;
349 operations.FindLogs(&files);
350
351 std::vector<std::string> names;
352 const std::string prefix = absl::StrCat(base_name_, separator_);
353 for (const auto &file : files) {
Alexei Strotsc1c5d592023-06-13 10:03:28 -0700354 CHECK(absl::StartsWith(file, prefix))
355 << ": File " << file << ", prefix " << prefix;
Alexei Strotsf08b8fb2023-04-21 19:46:08 -0700356 names.push_back(file.substr(prefix.size()));
357 }
358 return names;
359}
360
361std::unique_ptr<DataDecoder> FileBackend::GetDecoder(
362 std::string_view id) const {
363 const std::string filename = absl::StrCat(base_name_, separator_, id);
364 CHECK(std::filesystem::exists(filename));
365 return std::make_unique<DummyDecoder>(filename);
366}
367
colleen61276dc2023-06-01 09:23:29 -0700368RenamableFileBackend::RenamableFileBackend(std::string_view base_name,
369 bool supports_odirect)
370 : supports_odirect_(supports_odirect),
371 base_name_(base_name),
372 separator_(base_name_.back() == '/' ? "" : "_") {}
Alexei Strots01395492023-03-20 13:59:56 -0700373
Alexei Strotsbc082d82023-05-03 08:43:42 -0700374std::unique_ptr<LogSink> RenamableFileBackend::RequestFile(
Alexei Strots01395492023-03-20 13:59:56 -0700375 std::string_view id) {
376 const std::string filename =
377 absl::StrCat(base_name_, separator_, id, temp_suffix_);
colleen61276dc2023-06-01 09:23:29 -0700378 return std::make_unique<RenamableFileHandler>(this, filename,
379 supports_odirect_);
Alexei Strots01395492023-03-20 13:59:56 -0700380}
381
382void RenamableFileBackend::EnableTempFiles() {
383 use_temp_files_ = true;
384 temp_suffix_ = kTempExtension;
385}
386
387bool RenamableFileBackend::RenameLogBase(std::string_view new_base_name) {
388 if (new_base_name == base_name_) {
389 return true;
390 }
391 CHECK(old_base_name_.empty())
392 << "Only one change of base_name is supported. Was: " << old_base_name_;
393
394 std::string current_directory = base_name_;
395 std::string new_directory(new_base_name);
396
397 auto current_path_split = current_directory.rfind("/");
398 CHECK(current_path_split != std::string::npos)
399 << "Could not find / in the current directory path";
400 auto new_path_split = new_directory.rfind("/");
401 CHECK(new_path_split != std::string::npos)
402 << "Could not find / in the new directory path";
403
404 CHECK(new_base_name.substr(new_path_split) ==
405 current_directory.substr(current_path_split))
406 << "Rename of file base from " << current_directory << " to "
407 << new_directory << " is not supported.";
408
409 current_directory.resize(current_path_split);
410 new_directory.resize(new_path_split);
411 DIR *dir = opendir(current_directory.c_str());
412 if (dir) {
413 closedir(dir);
414 const int result = rename(current_directory.c_str(), new_directory.c_str());
415 if (result != 0) {
416 PLOG(ERROR) << "Unable to rename " << current_directory << " to "
417 << new_directory;
418 return false;
419 }
420 } else {
421 // Handle if directory was already renamed.
422 dir = opendir(new_directory.c_str());
423 if (!dir) {
424 LOG(ERROR) << "Old directory " << current_directory
425 << " missing and new directory " << new_directory
426 << " not present.";
427 return false;
428 }
429 closedir(dir);
430 }
431 old_base_name_ = base_name_;
432 base_name_ = std::string(new_base_name);
433 separator_ = base_name_.back() == '/' ? "" : "_";
434 return true;
435}
436
437WriteCode RenamableFileBackend::RenameFileAfterClose(
438 std::string_view filename) {
439 // Fast check that we can skip rename.
440 if (!use_temp_files_ && old_base_name_.empty()) {
441 return WriteCode::kOk;
442 }
443
444 std::string current_filename(filename);
445
446 // When changing the base name, we rename the log folder while there active
447 // buffer writers. Therefore, the name of that active buffer may still refer
448 // to the old file location rather than the new one.
449 if (!old_base_name_.empty()) {
450 auto offset = current_filename.find(old_base_name_);
451 if (offset != std::string::npos) {
452 current_filename.replace(offset, old_base_name_.length(), base_name_);
453 }
454 }
455
456 std::string final_filename = current_filename;
457 if (use_temp_files_) {
458 CHECK(current_filename.size() > temp_suffix_.size());
459 final_filename = current_filename.substr(
460 0, current_filename.size() - temp_suffix_.size());
461 }
462
463 int result = rename(current_filename.c_str(), final_filename.c_str());
464
465 bool ran_out_of_space = false;
466 if (result != 0) {
467 if (errno == ENOSPC) {
468 ran_out_of_space = true;
469 } else {
470 PLOG(FATAL) << "Renaming " << current_filename << " to " << final_filename
471 << " failed";
472 }
473 } else {
474 VLOG(1) << "Renamed " << current_filename << " -> " << final_filename;
475 }
476 return ran_out_of_space ? WriteCode::kOutOfSpace : WriteCode::kOk;
477}
478
479WriteCode RenamableFileBackend::RenamableFileHandler::Close() {
480 if (!is_open()) {
481 return WriteCode::kOk;
482 }
483 if (FileHandler::Close() == WriteCode::kOutOfSpace) {
484 return WriteCode::kOutOfSpace;
485 }
486 if (owner_->RenameFileAfterClose(filename()) == WriteCode::kOutOfSpace) {
487 return WriteCode::kOutOfSpace;
488 }
489 return WriteCode::kOk;
490}
Alexei Strotsa0b99d72023-04-11 15:12:42 -0700491
Austin Schuh3ebaf782023-04-07 16:03:28 -0700492} // namespace aos::logger