blob: ca7fc61d9fc867b8f5321a521a33f2ef9de10b73 [file] [log] [blame]
Austin Schuhcb5601b2020-09-10 15:29:59 -07001#include "aos/events/logging/log_namer.h"
2
3#include <functional>
4#include <map>
5#include <memory>
6#include <string_view>
7#include <vector>
8
9#include "absl/strings/str_cat.h"
10#include "aos/events/logging/logfile_utils.h"
11#include "aos/events/logging/logger_generated.h"
Austin Schuh73340842021-07-30 22:32:06 -070012#include "aos/flatbuffer_merge.h"
Austin Schuh4385b142021-03-14 21:31:13 -070013#include "aos/uuid.h"
Austin Schuhcb5601b2020-09-10 15:29:59 -070014#include "flatbuffers/flatbuffers.h"
15#include "glog/logging.h"
16
17namespace aos {
18namespace logger {
19
Austin Schuh572924a2021-07-30 22:32:12 -070020NewDataWriter::NewDataWriter(LogNamer *log_namer, const Node *node,
21 std::function<void(NewDataWriter *)> reopen,
22 std::function<void(NewDataWriter *)> close)
23 : node_(node),
24 node_index_(configuration::GetNodeIndex(log_namer->configuration_, node)),
25 log_namer_(log_namer),
26 reopen_(std::move(reopen)),
27 close_(std::move(close)) {
28 reopen_(this);
29}
30
31NewDataWriter::~NewDataWriter() {
32 if (writer) {
33 Close();
34 }
35}
36
37void NewDataWriter::Rotate() {
38 ++parts_index_;
39 reopen_(this);
40 header_written_ = false;
41 QueueHeader(log_namer_->MakeHeader(node_index_, source_node_boot_uuid_,
42 parts_uuid(), parts_index_));
43}
44
45void NewDataWriter::Reboot() {
46 parts_uuid_ = UUID::Random();
47 ++parts_index_;
48 reopen_(this);
49 header_written_ = false;
50}
51
52void NewDataWriter::QueueSizedFlatbuffer(flatbuffers::FlatBufferBuilder *fbb,
53 const UUID &source_node_boot_uuid,
54 aos::monotonic_clock::time_point now) {
55 // TODO(austin): Handle remote nodes changing too, not just the source node.
56 if (source_node_boot_uuid_ != source_node_boot_uuid) {
57 if (header_written_) {
58 Reboot();
59 }
60
61 QueueHeader(log_namer_->MakeHeader(node_index_, source_node_boot_uuid,
62 parts_uuid(), parts_index_));
63 }
64 CHECK_EQ(source_node_boot_uuid_, source_node_boot_uuid);
65 CHECK(header_written_) << ": Attempting to write message before header to "
66 << writer->filename();
67 writer->QueueSizedFlatbuffer(fbb, now);
68}
69
70void NewDataWriter::QueueHeader(
71 aos::SizePrefixedFlatbufferDetachedBuffer<LogFileHeader> &&header) {
72 CHECK(!header_written_) << ": Attempting to write duplicate header to "
73 << writer->filename();
74 CHECK(header.message().has_source_node_boot_uuid());
75 source_node_boot_uuid_ =
76 UUID::FromString(header.message().source_node_boot_uuid());
77 // TODO(austin): This triggers a dummy allocation that we don't need as part
78 // of releasing. Can we skip it?
79 writer->QueueSizedFlatbuffer(header.Release());
80 header_written_ = true;
81}
82
83void NewDataWriter::Close() {
84 CHECK(writer);
85 close_(this);
86 writer.reset();
87 header_written_ = false;
88}
89
Austin Schuh73340842021-07-30 22:32:06 -070090aos::SizePrefixedFlatbufferDetachedBuffer<LogFileHeader> LogNamer::MakeHeader(
91 size_t node_index, const UUID &source_node_boot_uuid,
92 const UUID &parts_uuid, int parts_index) const {
93 const Node *const source_node =
94 configuration::GetNode(configuration_, node_index);
95 CHECK_EQ(LogFileHeader::MiniReflectTypeTable()->num_elems, 18u);
96 flatbuffers::FlatBufferBuilder fbb;
97 fbb.ForceDefaults(true);
98
99 flatbuffers::Offset<flatbuffers::String> config_sha256_offset;
100 flatbuffers::Offset<aos::Configuration> configuration_offset;
101 if (header_.message().has_configuration()) {
102 CHECK(!header_.message().has_configuration_sha256());
103 configuration_offset =
104 CopyFlatBuffer(header_.message().configuration(), &fbb);
105 } else {
106 CHECK(!header_.message().has_configuration());
107 CHECK(header_.message().has_configuration_sha256());
108 config_sha256_offset = fbb.CreateString(
109 header_.message().configuration_sha256()->string_view());
110 }
111
112 CHECK(header_.message().has_name());
113 const flatbuffers::Offset<flatbuffers::String> name_offset =
114 fbb.CreateString(header_.message().name()->string_view());
115
116 CHECK(header_.message().has_log_event_uuid());
117 const flatbuffers::Offset<flatbuffers::String> log_event_uuid_offset =
118 fbb.CreateString(header_.message().log_event_uuid()->string_view());
119
120 CHECK(header_.message().has_logger_instance_uuid());
121 const flatbuffers::Offset<flatbuffers::String> logger_instance_uuid_offset =
122 fbb.CreateString(header_.message().logger_instance_uuid()->string_view());
123
124 flatbuffers::Offset<flatbuffers::String> log_start_uuid_offset;
125 if (header_.message().has_log_start_uuid()) {
126 log_start_uuid_offset =
127 fbb.CreateString(header_.message().log_start_uuid()->string_view());
128 }
129
130 CHECK(header_.message().has_logger_node_boot_uuid());
131 const flatbuffers::Offset<flatbuffers::String> logger_node_boot_uuid_offset =
132 fbb.CreateString(
133 header_.message().logger_node_boot_uuid()->string_view());
134
135 CHECK_NE(source_node_boot_uuid, UUID::Zero());
136 const flatbuffers::Offset<flatbuffers::String> source_node_boot_uuid_offset =
137 source_node_boot_uuid.PackString(&fbb);
138
139 const flatbuffers::Offset<flatbuffers::String> parts_uuid_offset =
140 parts_uuid.PackString(&fbb);
141
142 flatbuffers::Offset<Node> node_offset;
143 flatbuffers::Offset<Node> logger_node_offset;
144
145 if (configuration::MultiNode(configuration_)) {
146 node_offset = RecursiveCopyFlatBuffer(source_node, &fbb);
147 logger_node_offset = RecursiveCopyFlatBuffer(node_, &fbb);
148 }
149
150 aos::logger::LogFileHeader::Builder log_file_header_builder(fbb);
151
152 log_file_header_builder.add_name(name_offset);
153
154 // Only add the node if we are running in a multinode configuration.
155 if (!logger_node_offset.IsNull()) {
156 log_file_header_builder.add_node(node_offset);
157 log_file_header_builder.add_logger_node(logger_node_offset);
158 }
159
160 if (!configuration_offset.IsNull()) {
161 log_file_header_builder.add_configuration(configuration_offset);
162 }
163 log_file_header_builder.add_max_out_of_order_duration(
164 header_.message().max_out_of_order_duration());
165
166 log_file_header_builder.add_monotonic_start_time(
167 std::chrono::duration_cast<std::chrono::nanoseconds>(
168 node_states_[node_index].monotonic_start_time.time_since_epoch())
169 .count());
170 if (source_node == node_) {
171 log_file_header_builder.add_realtime_start_time(
172 std::chrono::duration_cast<std::chrono::nanoseconds>(
173 node_states_[node_index].realtime_start_time.time_since_epoch())
174 .count());
175 } else {
176 // Fill out the legacy start times. Since these were implemented to never
177 // change on reboot, they aren't very helpful in tracking what happened.
178 log_file_header_builder.add_logger_monotonic_start_time(
179 std::chrono::duration_cast<std::chrono::nanoseconds>(
180 node_states_[node_index]
181 .logger_monotonic_start_time.time_since_epoch())
182 .count());
183 log_file_header_builder.add_logger_realtime_start_time(
184 std::chrono::duration_cast<std::chrono::nanoseconds>(
185 node_states_[node_index]
186 .logger_realtime_start_time.time_since_epoch())
187 .count());
188 }
189
190 // TODO(austin): Add more useful times. When was this part started? What do
191 // we know about both the logger and remote then?
192
193 log_file_header_builder.add_log_event_uuid(log_event_uuid_offset);
194 log_file_header_builder.add_logger_instance_uuid(logger_instance_uuid_offset);
195 if (!log_start_uuid_offset.IsNull()) {
196 log_file_header_builder.add_log_start_uuid(log_start_uuid_offset);
197 }
198 log_file_header_builder.add_logger_node_boot_uuid(
199 logger_node_boot_uuid_offset);
200 log_file_header_builder.add_source_node_boot_uuid(
201 source_node_boot_uuid_offset);
202
203 log_file_header_builder.add_parts_uuid(parts_uuid_offset);
204 log_file_header_builder.add_parts_index(parts_index);
205
206 if (!config_sha256_offset.IsNull()) {
207 log_file_header_builder.add_configuration_sha256(config_sha256_offset);
208 }
209
210 fbb.FinishSizePrefixed(log_file_header_builder.Finish());
211 aos::SizePrefixedFlatbufferDetachedBuffer<LogFileHeader> result(
212 fbb.Release());
213
214 CHECK(result.Verify()) << ": Built a corrupted header.";
215
216 return result;
Austin Schuhcb5601b2020-09-10 15:29:59 -0700217}
218
Austin Schuhb8bca732021-07-30 22:32:00 -0700219NewDataWriter *LocalLogNamer::MakeWriter(const Channel *channel) {
Austin Schuhdf576472020-10-19 09:39:37 -0700220 CHECK(configuration::ChannelIsSendableOnNode(channel, node()))
221 << ": " << configuration::CleanedChannelToString(channel);
Austin Schuhb8bca732021-07-30 22:32:00 -0700222 return &data_writer_;
Austin Schuhcb5601b2020-09-10 15:29:59 -0700223}
224
Austin Schuh73340842021-07-30 22:32:06 -0700225void LocalLogNamer::Rotate(const Node *node) {
Austin Schuhcb5601b2020-09-10 15:29:59 -0700226 CHECK(node == this->node());
Austin Schuhb8bca732021-07-30 22:32:00 -0700227 data_writer_.Rotate();
Austin Schuhcb5601b2020-09-10 15:29:59 -0700228}
Austin Schuh8c399962020-12-25 21:51:45 -0800229
230void LocalLogNamer::WriteConfiguration(
231 aos::SizePrefixedFlatbufferDetachedBuffer<LogFileHeader> *header,
232 std::string_view config_sha256) {
233 const std::string filename = absl::StrCat(base_name_, config_sha256, ".bfbs");
234
235 std::unique_ptr<DetachedBufferWriter> writer =
236 std::make_unique<DetachedBufferWriter>(
237 filename, std::make_unique<aos::logger::DummyEncoder>());
238 writer->QueueSizedFlatbuffer(header->Release());
239}
240
Austin Schuhb8bca732021-07-30 22:32:00 -0700241NewDataWriter *LocalLogNamer::MakeTimestampWriter(const Channel *channel) {
Austin Schuhcb5601b2020-09-10 15:29:59 -0700242 CHECK(configuration::ChannelIsReadableOnNode(channel, node_))
243 << ": Message is not delivered to this node.";
244 CHECK(node_ != nullptr) << ": Can't log timestamps in a single node world";
245 CHECK(configuration::ConnectionDeliveryTimeIsLoggedOnNode(channel, node_,
246 node_))
247 << ": Delivery times aren't logged for this channel on this node.";
Austin Schuhb8bca732021-07-30 22:32:00 -0700248 return &data_writer_;
Austin Schuhcb5601b2020-09-10 15:29:59 -0700249}
250
Austin Schuhb8bca732021-07-30 22:32:00 -0700251NewDataWriter *LocalLogNamer::MakeForwardedTimestampWriter(
Austin Schuhcb5601b2020-09-10 15:29:59 -0700252 const Channel * /*channel*/, const Node * /*node*/) {
253 LOG(FATAL) << "Can't log forwarded timestamps in a singe log file.";
254 return nullptr;
255}
256
257MultiNodeLogNamer::MultiNodeLogNamer(std::string_view base_name,
258 const Configuration *configuration,
Brian Silvermancb805822020-10-06 17:43:35 -0700259 const Node *node)
Austin Schuh73340842021-07-30 22:32:06 -0700260 : LogNamer(configuration, node), base_name_(base_name), old_base_name_() {}
Austin Schuhcb5601b2020-09-10 15:29:59 -0700261
Brian Silverman48deab12020-09-30 18:39:28 -0700262MultiNodeLogNamer::~MultiNodeLogNamer() {
263 if (!ran_out_of_space_) {
264 // This handles renaming temporary files etc.
265 Close();
266 }
267}
268
Austin Schuh572924a2021-07-30 22:32:12 -0700269void MultiNodeLogNamer::Rotate(const Node *node) {
Austin Schuhcb5601b2020-09-10 15:29:59 -0700270 if (node == this->node()) {
Austin Schuhb8bca732021-07-30 22:32:00 -0700271 if (data_writer_) {
Austin Schuh572924a2021-07-30 22:32:12 -0700272 data_writer_->Rotate();
Brian Silvermancb805822020-10-06 17:43:35 -0700273 }
Austin Schuhcb5601b2020-09-10 15:29:59 -0700274 } else {
Austin Schuhb8bca732021-07-30 22:32:00 -0700275 for (std::pair<const Channel *const, NewDataWriter> &data_writer :
Austin Schuhcb5601b2020-09-10 15:29:59 -0700276 data_writers_) {
Austin Schuh572924a2021-07-30 22:32:12 -0700277 if (node == data_writer.second.node()) {
278 data_writer.second.Rotate();
Austin Schuhcb5601b2020-09-10 15:29:59 -0700279 }
280 }
281 }
282}
283
Austin Schuh8c399962020-12-25 21:51:45 -0800284void MultiNodeLogNamer::WriteConfiguration(
285 aos::SizePrefixedFlatbufferDetachedBuffer<LogFileHeader> *header,
286 std::string_view config_sha256) {
287 if (ran_out_of_space_) {
288 return;
289 }
290
291 const std::string_view separator = base_name_.back() == '/' ? "" : "_";
292 const std::string filename = absl::StrCat(
293 base_name_, separator, config_sha256, ".bfbs", extension_, temp_suffix_);
294
295 std::unique_ptr<DetachedBufferWriter> writer =
296 std::make_unique<DetachedBufferWriter>(filename, encoder_factory_());
297
298 writer->QueueSizedFlatbuffer(header->Release());
299
300 if (!writer->ran_out_of_space()) {
301 all_filenames_.emplace_back(filename);
302 }
303 CloseWriter(&writer);
304}
305
Austin Schuhb8bca732021-07-30 22:32:00 -0700306NewDataWriter *MultiNodeLogNamer::MakeWriter(const Channel *channel) {
Austin Schuhcb5601b2020-09-10 15:29:59 -0700307 // See if we can read the data on this node at all.
308 const bool is_readable =
309 configuration::ChannelIsReadableOnNode(channel, this->node());
310 if (!is_readable) {
311 return nullptr;
312 }
313
314 // Then, see if we are supposed to log the data here.
315 const bool log_message =
316 configuration::ChannelMessageIsLoggedOnNode(channel, this->node());
317
318 if (!log_message) {
319 return nullptr;
320 }
321
322 // Now, sort out if this is data generated on this node, or not. It is
323 // generated if it is sendable on this node.
324 if (configuration::ChannelIsSendableOnNode(channel, this->node())) {
Austin Schuhb8bca732021-07-30 22:32:00 -0700325 if (!data_writer_) {
Brian Silvermancb805822020-10-06 17:43:35 -0700326 OpenDataWriter();
327 }
Austin Schuhb8bca732021-07-30 22:32:00 -0700328 return data_writer_.get();
Austin Schuhcb5601b2020-09-10 15:29:59 -0700329 }
330
331 // Ok, we have data that is being forwarded to us that we are supposed to
332 // log. It needs to be logged with send timestamps, but be sorted enough
333 // to be able to be processed.
334 CHECK(data_writers_.find(channel) == data_writers_.end());
335
336 // Track that this node is being logged.
337 const Node *source_node = configuration::GetNode(
338 configuration_, channel->source_node()->string_view());
339
340 if (std::find(nodes_.begin(), nodes_.end(), source_node) == nodes_.end()) {
341 nodes_.emplace_back(source_node);
342 }
343
Austin Schuh572924a2021-07-30 22:32:12 -0700344 NewDataWriter data_writer(this, source_node,
345 [this, channel](NewDataWriter *data_writer) {
346 OpenWriter(channel, data_writer);
347 },
348 [this](NewDataWriter *data_writer) {
349 CloseWriter(&data_writer->writer);
350 });
Austin Schuhb8bca732021-07-30 22:32:00 -0700351 return &(
352 data_writers_.emplace(channel, std::move(data_writer)).first->second);
Austin Schuhcb5601b2020-09-10 15:29:59 -0700353}
354
Austin Schuhb8bca732021-07-30 22:32:00 -0700355NewDataWriter *MultiNodeLogNamer::MakeForwardedTimestampWriter(
Austin Schuhcb5601b2020-09-10 15:29:59 -0700356 const Channel *channel, const Node *node) {
357 // See if we can read the data on this node at all.
358 const bool is_readable =
359 configuration::ChannelIsReadableOnNode(channel, this->node());
360 CHECK(is_readable) << ": " << configuration::CleanedChannelToString(channel);
361
362 CHECK(data_writers_.find(channel) == data_writers_.end());
363
364 if (std::find(nodes_.begin(), nodes_.end(), node) == nodes_.end()) {
365 nodes_.emplace_back(node);
366 }
367
Austin Schuh572924a2021-07-30 22:32:12 -0700368 NewDataWriter data_writer(this, node,
369 [this, channel](NewDataWriter *data_writer) {
370 OpenForwardedTimestampWriter(channel,
371 data_writer);
372 },
373 [this](NewDataWriter *data_writer) {
374 CloseWriter(&data_writer->writer);
375 });
Austin Schuhb8bca732021-07-30 22:32:00 -0700376 return &(
377 data_writers_.emplace(channel, std::move(data_writer)).first->second);
Austin Schuhcb5601b2020-09-10 15:29:59 -0700378}
379
Austin Schuhb8bca732021-07-30 22:32:00 -0700380NewDataWriter *MultiNodeLogNamer::MakeTimestampWriter(const Channel *channel) {
Brian Silverman0465fcf2020-09-24 00:29:18 -0700381 bool log_delivery_times = false;
382 if (this->node() != nullptr) {
383 log_delivery_times = configuration::ConnectionDeliveryTimeIsLoggedOnNode(
384 channel, this->node(), this->node());
385 }
Austin Schuhcb5601b2020-09-10 15:29:59 -0700386 if (!log_delivery_times) {
387 return nullptr;
388 }
389
Austin Schuhb8bca732021-07-30 22:32:00 -0700390 if (!data_writer_) {
Brian Silvermancb805822020-10-06 17:43:35 -0700391 OpenDataWriter();
392 }
Austin Schuhb8bca732021-07-30 22:32:00 -0700393 return data_writer_.get();
Austin Schuhcb5601b2020-09-10 15:29:59 -0700394}
395
Brian Silverman0465fcf2020-09-24 00:29:18 -0700396void MultiNodeLogNamer::Close() {
Austin Schuhb8bca732021-07-30 22:32:00 -0700397 data_writers_.clear();
398 data_writer_.reset();
Brian Silvermancb805822020-10-06 17:43:35 -0700399}
400
401void MultiNodeLogNamer::ResetStatistics() {
Austin Schuhb8bca732021-07-30 22:32:00 -0700402 for (std::pair<const Channel *const, NewDataWriter> &data_writer :
Brian Silvermancb805822020-10-06 17:43:35 -0700403 data_writers_) {
Austin Schuhad0cfc32020-12-21 12:34:26 -0800404 if (!data_writer.second.writer) continue;
Brian Silvermancb805822020-10-06 17:43:35 -0700405 data_writer.second.writer->ResetStatistics();
Brian Silverman0465fcf2020-09-24 00:29:18 -0700406 }
Austin Schuhb8bca732021-07-30 22:32:00 -0700407 if (data_writer_) {
408 data_writer_->writer->ResetStatistics();
Brian Silvermancb805822020-10-06 17:43:35 -0700409 }
410 max_write_time_ = std::chrono::nanoseconds::zero();
411 max_write_time_bytes_ = -1;
412 max_write_time_messages_ = -1;
413 total_write_time_ = std::chrono::nanoseconds::zero();
414 total_write_count_ = 0;
415 total_write_messages_ = 0;
416 total_write_bytes_ = 0;
Brian Silverman0465fcf2020-09-24 00:29:18 -0700417}
418
Austin Schuhb8bca732021-07-30 22:32:00 -0700419void MultiNodeLogNamer::OpenForwardedTimestampWriter(
420 const Channel *channel, NewDataWriter *data_writer) {
Austin Schuhcb5601b2020-09-10 15:29:59 -0700421 std::string filename =
Austin Schuhe715eae2020-10-10 15:39:30 -0700422 absl::StrCat("timestamps", channel->name()->string_view(), "/",
Brian Silvermana621f522020-09-30 16:52:43 -0700423 channel->type()->string_view(), ".part",
Austin Schuh572924a2021-07-30 22:32:12 -0700424 data_writer->parts_index(), ".bfbs", extension_);
Brian Silverman0465fcf2020-09-24 00:29:18 -0700425 CreateBufferWriter(filename, &data_writer->writer);
Austin Schuhcb5601b2020-09-10 15:29:59 -0700426}
427
428void MultiNodeLogNamer::OpenWriter(const Channel *channel,
Austin Schuhb8bca732021-07-30 22:32:00 -0700429 NewDataWriter *data_writer) {
Austin Schuhcb5601b2020-09-10 15:29:59 -0700430 const std::string filename = absl::StrCat(
Austin Schuhe715eae2020-10-10 15:39:30 -0700431 CHECK_NOTNULL(channel->source_node())->string_view(), "_data",
Brian Silvermana621f522020-09-30 16:52:43 -0700432 channel->name()->string_view(), "/", channel->type()->string_view(),
Austin Schuh572924a2021-07-30 22:32:12 -0700433 ".part", data_writer->parts_index(), ".bfbs", extension_);
Brian Silverman0465fcf2020-09-24 00:29:18 -0700434 CreateBufferWriter(filename, &data_writer->writer);
Austin Schuhcb5601b2020-09-10 15:29:59 -0700435}
436
Brian Silvermana621f522020-09-30 16:52:43 -0700437void MultiNodeLogNamer::OpenDataWriter() {
Austin Schuhb8bca732021-07-30 22:32:00 -0700438 if (!data_writer_) {
439 data_writer_ = std::make_unique<NewDataWriter>(
Austin Schuh572924a2021-07-30 22:32:12 -0700440 this, node_,
Austin Schuhb8bca732021-07-30 22:32:00 -0700441 [this](NewDataWriter *writer) {
442 std::string name;
443 if (node() != nullptr) {
444 name = absl::StrCat(name, node()->name()->string_view(), "_");
445 }
Austin Schuh572924a2021-07-30 22:32:12 -0700446 absl::StrAppend(&name, "data.part", writer->parts_index(), ".bfbs",
Austin Schuhb8bca732021-07-30 22:32:00 -0700447 extension_);
448 CreateBufferWriter(name, &writer->writer);
449 },
450 [this](NewDataWriter *data_writer) {
451 CloseWriter(&data_writer->writer);
452 });
Brian Silverman7af8c902020-09-29 16:14:04 -0700453 }
Austin Schuhcb5601b2020-09-10 15:29:59 -0700454}
455
Brian Silverman0465fcf2020-09-24 00:29:18 -0700456void MultiNodeLogNamer::CreateBufferWriter(
Brian Silvermana621f522020-09-30 16:52:43 -0700457 std::string_view path, std::unique_ptr<DetachedBufferWriter> *destination) {
Brian Silverman0465fcf2020-09-24 00:29:18 -0700458 if (ran_out_of_space_) {
459 // Refuse to open any new files, which might skip data. Any existing files
460 // are in the same folder, which means they're on the same filesystem, which
461 // means they're probably going to run out of space and get stuck too.
Austin Schuha426f1f2021-03-31 22:27:41 -0700462 if (!destination->get()) {
463 // But avoid leaving a nullptr writer if we're out of space when
464 // attempting to open the first file.
465 *destination = std::make_unique<DetachedBufferWriter>(
466 DetachedBufferWriter::already_out_of_space_t());
467 }
Brian Silverman0465fcf2020-09-24 00:29:18 -0700468 return;
469 }
Austin Schuhe715eae2020-10-10 15:39:30 -0700470 const std::string_view separator = base_name_.back() == '/' ? "" : "_";
471 const std::string filename =
472 absl::StrCat(base_name_, separator, path, temp_suffix_);
Brian Silverman0465fcf2020-09-24 00:29:18 -0700473 if (!destination->get()) {
Brian Silvermana9f2ec92020-10-06 18:00:53 -0700474 if (ran_out_of_space_) {
475 *destination = std::make_unique<DetachedBufferWriter>(
476 DetachedBufferWriter::already_out_of_space_t());
477 return;
478 }
Brian Silvermancb805822020-10-06 17:43:35 -0700479 *destination =
480 std::make_unique<DetachedBufferWriter>(filename, encoder_factory_());
Brian Silvermana9f2ec92020-10-06 18:00:53 -0700481 if (!destination->get()->ran_out_of_space()) {
482 all_filenames_.emplace_back(path);
483 }
Brian Silverman0465fcf2020-09-24 00:29:18 -0700484 return;
485 }
Brian Silvermancb805822020-10-06 17:43:35 -0700486
487 CloseWriter(destination);
488 if (ran_out_of_space_) {
Brian Silvermana9f2ec92020-10-06 18:00:53 -0700489 *destination->get() =
490 DetachedBufferWriter(DetachedBufferWriter::already_out_of_space_t());
Brian Silverman0465fcf2020-09-24 00:29:18 -0700491 return;
492 }
Brian Silvermana9f2ec92020-10-06 18:00:53 -0700493
Brian Silvermancb805822020-10-06 17:43:35 -0700494 *destination->get() = DetachedBufferWriter(filename, encoder_factory_());
Brian Silvermana9f2ec92020-10-06 18:00:53 -0700495 if (!destination->get()->ran_out_of_space()) {
496 all_filenames_.emplace_back(path);
497 }
Brian Silverman0465fcf2020-09-24 00:29:18 -0700498}
499
Brian Silverman48deab12020-09-30 18:39:28 -0700500void MultiNodeLogNamer::RenameTempFile(DetachedBufferWriter *destination) {
501 if (temp_suffix_.empty()) {
502 return;
503 }
Austin Schuh6bb8a822021-03-31 23:04:39 -0700504 std::string current_filename = std::string(destination->filename());
Brian Silverman48deab12020-09-30 18:39:28 -0700505 CHECK(current_filename.size() > temp_suffix_.size());
Austin Schuh6bb8a822021-03-31 23:04:39 -0700506 std::string final_filename =
Brian Silverman48deab12020-09-30 18:39:28 -0700507 current_filename.substr(0, current_filename.size() - temp_suffix_.size());
Austin Schuh6bb8a822021-03-31 23:04:39 -0700508 int result = rename(current_filename.c_str(), final_filename.c_str());
509
510 // When changing the base name, we rename the log folder while there active
511 // buffer writers. Therefore, the name of that active buffer may still refer
512 // to the old file location rather than the new one. This minimized changes to
513 // existing code.
514 if (result != 0 && errno != ENOSPC && !old_base_name_.empty()) {
515 auto offset = current_filename.find(old_base_name_);
516 if (offset != std::string::npos) {
517 current_filename.replace(offset, old_base_name_.length(), base_name_);
518 }
519 offset = final_filename.find(old_base_name_);
520 if (offset != std::string::npos) {
521 final_filename.replace(offset, old_base_name_.length(), base_name_);
522 }
523 result = rename(current_filename.c_str(), final_filename.c_str());
524 }
525
Brian Silverman48deab12020-09-30 18:39:28 -0700526 if (result != 0) {
527 if (errno == ENOSPC) {
528 ran_out_of_space_ = true;
529 return;
530 } else {
531 PLOG(FATAL) << "Renaming " << current_filename << " to " << final_filename
532 << " failed";
533 }
Austin Schuh6bb8a822021-03-31 23:04:39 -0700534 } else {
535 VLOG(1) << "Renamed " << current_filename << " -> " << final_filename;
Brian Silverman48deab12020-09-30 18:39:28 -0700536 }
537}
538
Brian Silvermancb805822020-10-06 17:43:35 -0700539void MultiNodeLogNamer::CloseWriter(
540 std::unique_ptr<DetachedBufferWriter> *writer_pointer) {
541 DetachedBufferWriter *const writer = writer_pointer->get();
542 if (!writer) {
543 return;
544 }
Brian Silvermana9f2ec92020-10-06 18:00:53 -0700545 const bool was_open = writer->is_open();
Brian Silvermancb805822020-10-06 17:43:35 -0700546 writer->Close();
547
548 if (writer->max_write_time() > max_write_time_) {
549 max_write_time_ = writer->max_write_time();
550 max_write_time_bytes_ = writer->max_write_time_bytes();
551 max_write_time_messages_ = writer->max_write_time_messages();
552 }
553 total_write_time_ += writer->total_write_time();
554 total_write_count_ += writer->total_write_count();
555 total_write_messages_ += writer->total_write_messages();
556 total_write_bytes_ += writer->total_write_bytes();
557
558 if (writer->ran_out_of_space()) {
559 ran_out_of_space_ = true;
560 writer->acknowledge_out_of_space();
561 }
Brian Silvermana9f2ec92020-10-06 18:00:53 -0700562 if (was_open) {
563 RenameTempFile(writer);
564 } else {
565 CHECK(access(std::string(writer->filename()).c_str(), F_OK) == -1)
566 << ": File should not exist: " << writer->filename();
567 }
Brian Silvermancb805822020-10-06 17:43:35 -0700568}
569
Austin Schuhcb5601b2020-09-10 15:29:59 -0700570} // namespace logger
571} // namespace aos