blob: 6cf91db91201e312027590a94d7eee7a556f1e1e [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 Schuh4385b142021-03-14 21:31:13 -070012#include "aos/uuid.h"
Austin Schuhcb5601b2020-09-10 15:29:59 -070013#include "flatbuffers/flatbuffers.h"
14#include "glog/logging.h"
15
16namespace aos {
17namespace logger {
18
19void LogNamer::UpdateHeader(
20 aos::SizePrefixedFlatbufferDetachedBuffer<LogFileHeader> *header,
21 const UUID &uuid, int parts_index) const {
22 header->mutable_message()->mutate_parts_index(parts_index);
Austin Schuh5e2bfb82021-03-13 22:46:55 -080023 CHECK_EQ(UUID::kStringSize,
Austin Schuhcb5601b2020-09-10 15:29:59 -070024 header->mutable_message()->mutable_parts_uuid()->size());
Austin Schuh5e2bfb82021-03-13 22:46:55 -080025 uuid.CopyTo(reinterpret_cast<char *>(
26 header->mutable_message()->mutable_parts_uuid()->Data()));
Austin Schuhcb5601b2020-09-10 15:29:59 -070027}
28
29void LocalLogNamer::WriteHeader(
30 aos::SizePrefixedFlatbufferDetachedBuffer<LogFileHeader> *header,
31 const Node *node) {
32 CHECK_EQ(node, this->node());
33 UpdateHeader(header, uuid_, part_number_);
Austin Schuhadd6eb32020-11-09 21:24:26 -080034 data_writer_->QueueSpan(header->span());
Austin Schuhcb5601b2020-09-10 15:29:59 -070035}
36
37DetachedBufferWriter *LocalLogNamer::MakeWriter(const Channel *channel) {
Austin Schuhdf576472020-10-19 09:39:37 -070038 CHECK(configuration::ChannelIsSendableOnNode(channel, node()))
39 << ": " << configuration::CleanedChannelToString(channel);
Austin Schuhcb5601b2020-09-10 15:29:59 -070040 return data_writer_.get();
41}
42
43void LocalLogNamer::Rotate(
44 const Node *node,
45 aos::SizePrefixedFlatbufferDetachedBuffer<LogFileHeader> *header) {
46 CHECK(node == this->node());
47 ++part_number_;
48 *data_writer_ = std::move(*OpenDataWriter());
49 UpdateHeader(header, uuid_, part_number_);
Austin Schuhadd6eb32020-11-09 21:24:26 -080050 data_writer_->QueueSpan(header->span());
Austin Schuhcb5601b2020-09-10 15:29:59 -070051}
Austin Schuh8c399962020-12-25 21:51:45 -080052
53void LocalLogNamer::WriteConfiguration(
54 aos::SizePrefixedFlatbufferDetachedBuffer<LogFileHeader> *header,
55 std::string_view config_sha256) {
56 const std::string filename = absl::StrCat(base_name_, config_sha256, ".bfbs");
57
58 std::unique_ptr<DetachedBufferWriter> writer =
59 std::make_unique<DetachedBufferWriter>(
60 filename, std::make_unique<aos::logger::DummyEncoder>());
61 writer->QueueSizedFlatbuffer(header->Release());
62}
63
Austin Schuh315b96b2020-12-11 21:21:12 -080064void LocalLogNamer::Reboot(
65 const Node * /*node*/,
66 aos::SizePrefixedFlatbufferDetachedBuffer<LogFileHeader> * /*header*/) {
67 LOG(FATAL) << "Can't reboot a single node.";
68}
Austin Schuhcb5601b2020-09-10 15:29:59 -070069
70DetachedBufferWriter *LocalLogNamer::MakeTimestampWriter(
71 const Channel *channel) {
72 CHECK(configuration::ChannelIsReadableOnNode(channel, node_))
73 << ": Message is not delivered to this node.";
74 CHECK(node_ != nullptr) << ": Can't log timestamps in a single node world";
75 CHECK(configuration::ConnectionDeliveryTimeIsLoggedOnNode(channel, node_,
76 node_))
77 << ": Delivery times aren't logged for this channel on this node.";
78 return data_writer_.get();
79}
80
81DetachedBufferWriter *LocalLogNamer::MakeForwardedTimestampWriter(
82 const Channel * /*channel*/, const Node * /*node*/) {
83 LOG(FATAL) << "Can't log forwarded timestamps in a singe log file.";
84 return nullptr;
85}
86
87MultiNodeLogNamer::MultiNodeLogNamer(std::string_view base_name,
88 const Configuration *configuration,
Brian Silvermancb805822020-10-06 17:43:35 -070089 const Node *node)
Austin Schuh6bb8a822021-03-31 23:04:39 -070090 : LogNamer(node),
91 base_name_(base_name),
92 old_base_name_(),
93 configuration_(configuration) {}
Austin Schuhcb5601b2020-09-10 15:29:59 -070094
Brian Silverman48deab12020-09-30 18:39:28 -070095MultiNodeLogNamer::~MultiNodeLogNamer() {
96 if (!ran_out_of_space_) {
97 // This handles renaming temporary files etc.
98 Close();
99 }
100}
101
Austin Schuhcb5601b2020-09-10 15:29:59 -0700102void MultiNodeLogNamer::WriteHeader(
103 aos::SizePrefixedFlatbufferDetachedBuffer<LogFileHeader> *header,
104 const Node *node) {
105 if (node == this->node()) {
Brian Silvermancb805822020-10-06 17:43:35 -0700106 if (!data_writer_.writer) {
107 OpenDataWriter();
108 }
109 UpdateHeader(header, data_writer_.uuid, data_writer_.part_number);
Austin Schuhadd6eb32020-11-09 21:24:26 -0800110 data_writer_.writer->QueueSpan(header->span());
Austin Schuhcb5601b2020-09-10 15:29:59 -0700111 } else {
112 for (std::pair<const Channel *const, DataWriter> &data_writer :
113 data_writers_) {
114 if (node == data_writer.second.node) {
115 UpdateHeader(header, data_writer.second.uuid,
116 data_writer.second.part_number);
Austin Schuhadd6eb32020-11-09 21:24:26 -0800117 data_writer.second.writer->QueueSpan(header->span());
Austin Schuhcb5601b2020-09-10 15:29:59 -0700118 }
119 }
120 }
121}
122
123void MultiNodeLogNamer::Rotate(
124 const Node *node,
125 aos::SizePrefixedFlatbufferDetachedBuffer<LogFileHeader> *header) {
Austin Schuh315b96b2020-12-11 21:21:12 -0800126 DoRotate(node, header, false);
127}
128
129void MultiNodeLogNamer::Reboot(
130 const Node *node,
131 aos::SizePrefixedFlatbufferDetachedBuffer<LogFileHeader> *header) {
132 DoRotate(node, header, true);
133}
134
135void MultiNodeLogNamer::DoRotate(
136 const Node *node,
Austin Schuh8c399962020-12-25 21:51:45 -0800137 aos::SizePrefixedFlatbufferDetachedBuffer<LogFileHeader> *header,
138 bool reboot) {
Austin Schuhcb5601b2020-09-10 15:29:59 -0700139 if (node == this->node()) {
Brian Silvermancb805822020-10-06 17:43:35 -0700140 if (data_writer_.writer) {
Austin Schuh315b96b2020-12-11 21:21:12 -0800141 if (reboot) {
142 data_writer_.uuid = UUID::Random();
143 }
Brian Silvermancb805822020-10-06 17:43:35 -0700144 ++data_writer_.part_number;
145 }
Brian Silvermana621f522020-09-30 16:52:43 -0700146 OpenDataWriter();
Brian Silvermancb805822020-10-06 17:43:35 -0700147 UpdateHeader(header, data_writer_.uuid, data_writer_.part_number);
Austin Schuhadd6eb32020-11-09 21:24:26 -0800148 data_writer_.writer->QueueSpan(header->span());
Austin Schuhcb5601b2020-09-10 15:29:59 -0700149 } else {
150 for (std::pair<const Channel *const, DataWriter> &data_writer :
151 data_writers_) {
152 if (node == data_writer.second.node) {
Austin Schuh315b96b2020-12-11 21:21:12 -0800153 if (reboot) {
154 data_writer.second.uuid = UUID::Random();
155 }
Austin Schuhcb5601b2020-09-10 15:29:59 -0700156 ++data_writer.second.part_number;
157 data_writer.second.rotate(data_writer.first, &data_writer.second);
158 UpdateHeader(header, data_writer.second.uuid,
159 data_writer.second.part_number);
Austin Schuhadd6eb32020-11-09 21:24:26 -0800160 data_writer.second.writer->QueueSpan(header->span());
Austin Schuhcb5601b2020-09-10 15:29:59 -0700161 }
162 }
163 }
164}
165
Austin Schuh8c399962020-12-25 21:51:45 -0800166void MultiNodeLogNamer::WriteConfiguration(
167 aos::SizePrefixedFlatbufferDetachedBuffer<LogFileHeader> *header,
168 std::string_view config_sha256) {
169 if (ran_out_of_space_) {
170 return;
171 }
172
173 const std::string_view separator = base_name_.back() == '/' ? "" : "_";
174 const std::string filename = absl::StrCat(
175 base_name_, separator, config_sha256, ".bfbs", extension_, temp_suffix_);
176
177 std::unique_ptr<DetachedBufferWriter> writer =
178 std::make_unique<DetachedBufferWriter>(filename, encoder_factory_());
179
180 writer->QueueSizedFlatbuffer(header->Release());
181
182 if (!writer->ran_out_of_space()) {
183 all_filenames_.emplace_back(filename);
184 }
185 CloseWriter(&writer);
186}
187
Austin Schuhcb5601b2020-09-10 15:29:59 -0700188DetachedBufferWriter *MultiNodeLogNamer::MakeWriter(const Channel *channel) {
189 // See if we can read the data on this node at all.
190 const bool is_readable =
191 configuration::ChannelIsReadableOnNode(channel, this->node());
192 if (!is_readable) {
193 return nullptr;
194 }
195
196 // Then, see if we are supposed to log the data here.
197 const bool log_message =
198 configuration::ChannelMessageIsLoggedOnNode(channel, this->node());
199
200 if (!log_message) {
201 return nullptr;
202 }
203
204 // Now, sort out if this is data generated on this node, or not. It is
205 // generated if it is sendable on this node.
206 if (configuration::ChannelIsSendableOnNode(channel, this->node())) {
Brian Silvermancb805822020-10-06 17:43:35 -0700207 if (!data_writer_.writer) {
208 OpenDataWriter();
209 }
210 return data_writer_.writer.get();
Austin Schuhcb5601b2020-09-10 15:29:59 -0700211 }
212
213 // Ok, we have data that is being forwarded to us that we are supposed to
214 // log. It needs to be logged with send timestamps, but be sorted enough
215 // to be able to be processed.
216 CHECK(data_writers_.find(channel) == data_writers_.end());
217
218 // Track that this node is being logged.
219 const Node *source_node = configuration::GetNode(
220 configuration_, channel->source_node()->string_view());
221
222 if (std::find(nodes_.begin(), nodes_.end(), source_node) == nodes_.end()) {
223 nodes_.emplace_back(source_node);
224 }
225
226 DataWriter data_writer;
227 data_writer.node = source_node;
228 data_writer.rotate = [this](const Channel *channel, DataWriter *data_writer) {
229 OpenWriter(channel, data_writer);
230 };
231 data_writer.rotate(channel, &data_writer);
232
233 return data_writers_.insert(std::make_pair(channel, std::move(data_writer)))
234 .first->second.writer.get();
235}
236
237DetachedBufferWriter *MultiNodeLogNamer::MakeForwardedTimestampWriter(
238 const Channel *channel, const Node *node) {
239 // See if we can read the data on this node at all.
240 const bool is_readable =
241 configuration::ChannelIsReadableOnNode(channel, this->node());
242 CHECK(is_readable) << ": " << configuration::CleanedChannelToString(channel);
243
244 CHECK(data_writers_.find(channel) == data_writers_.end());
245
246 if (std::find(nodes_.begin(), nodes_.end(), node) == nodes_.end()) {
247 nodes_.emplace_back(node);
248 }
249
250 DataWriter data_writer;
251 data_writer.node = node;
252 data_writer.rotate = [this](const Channel *channel, DataWriter *data_writer) {
253 OpenForwardedTimestampWriter(channel, data_writer);
254 };
255 data_writer.rotate(channel, &data_writer);
256
257 return data_writers_.insert(std::make_pair(channel, std::move(data_writer)))
258 .first->second.writer.get();
259}
260
261DetachedBufferWriter *MultiNodeLogNamer::MakeTimestampWriter(
262 const Channel *channel) {
Brian Silverman0465fcf2020-09-24 00:29:18 -0700263 bool log_delivery_times = false;
264 if (this->node() != nullptr) {
265 log_delivery_times = configuration::ConnectionDeliveryTimeIsLoggedOnNode(
266 channel, this->node(), this->node());
267 }
Austin Schuhcb5601b2020-09-10 15:29:59 -0700268 if (!log_delivery_times) {
269 return nullptr;
270 }
271
Brian Silvermancb805822020-10-06 17:43:35 -0700272 if (!data_writer_.writer) {
273 OpenDataWriter();
274 }
275 return data_writer_.writer.get();
Austin Schuhcb5601b2020-09-10 15:29:59 -0700276}
277
Brian Silverman0465fcf2020-09-24 00:29:18 -0700278void MultiNodeLogNamer::Close() {
279 for (std::pair<const Channel *const, DataWriter> &data_writer :
280 data_writers_) {
Brian Silvermancb805822020-10-06 17:43:35 -0700281 CloseWriter(&data_writer.second.writer);
282 data_writer.second.writer.reset();
Brian Silverman0465fcf2020-09-24 00:29:18 -0700283 }
Brian Silvermancb805822020-10-06 17:43:35 -0700284 CloseWriter(&data_writer_.writer);
285 data_writer_.writer.reset();
286}
287
288void MultiNodeLogNamer::ResetStatistics() {
289 for (std::pair<const Channel *const, DataWriter> &data_writer :
290 data_writers_) {
Austin Schuhad0cfc32020-12-21 12:34:26 -0800291 if (!data_writer.second.writer) continue;
Brian Silvermancb805822020-10-06 17:43:35 -0700292 data_writer.second.writer->ResetStatistics();
Brian Silverman0465fcf2020-09-24 00:29:18 -0700293 }
Brian Silvermancb805822020-10-06 17:43:35 -0700294 if (data_writer_.writer) {
295 data_writer_.writer->ResetStatistics();
296 }
297 max_write_time_ = std::chrono::nanoseconds::zero();
298 max_write_time_bytes_ = -1;
299 max_write_time_messages_ = -1;
300 total_write_time_ = std::chrono::nanoseconds::zero();
301 total_write_count_ = 0;
302 total_write_messages_ = 0;
303 total_write_bytes_ = 0;
Brian Silverman0465fcf2020-09-24 00:29:18 -0700304}
305
Austin Schuhcb5601b2020-09-10 15:29:59 -0700306void MultiNodeLogNamer::OpenForwardedTimestampWriter(const Channel *channel,
307 DataWriter *data_writer) {
308 std::string filename =
Austin Schuhe715eae2020-10-10 15:39:30 -0700309 absl::StrCat("timestamps", channel->name()->string_view(), "/",
Brian Silvermana621f522020-09-30 16:52:43 -0700310 channel->type()->string_view(), ".part",
Brian Silverman1b071eb2020-10-09 12:24:10 -0700311 data_writer->part_number, ".bfbs", extension_);
Brian Silverman0465fcf2020-09-24 00:29:18 -0700312 CreateBufferWriter(filename, &data_writer->writer);
Austin Schuhcb5601b2020-09-10 15:29:59 -0700313}
314
315void MultiNodeLogNamer::OpenWriter(const Channel *channel,
316 DataWriter *data_writer) {
317 const std::string filename = absl::StrCat(
Austin Schuhe715eae2020-10-10 15:39:30 -0700318 CHECK_NOTNULL(channel->source_node())->string_view(), "_data",
Brian Silvermana621f522020-09-30 16:52:43 -0700319 channel->name()->string_view(), "/", channel->type()->string_view(),
Brian Silvermancb805822020-10-06 17:43:35 -0700320 ".part", data_writer->part_number, ".bfbs", extension_);
Brian Silverman0465fcf2020-09-24 00:29:18 -0700321 CreateBufferWriter(filename, &data_writer->writer);
Austin Schuhcb5601b2020-09-10 15:29:59 -0700322}
323
Brian Silvermana621f522020-09-30 16:52:43 -0700324void MultiNodeLogNamer::OpenDataWriter() {
325 std::string name;
Brian Silverman7af8c902020-09-29 16:14:04 -0700326 if (node() != nullptr) {
Austin Schuhe715eae2020-10-10 15:39:30 -0700327 name = absl::StrCat(name, node()->name()->string_view(), "_");
Brian Silverman7af8c902020-09-29 16:14:04 -0700328 }
Austin Schuhe715eae2020-10-10 15:39:30 -0700329 absl::StrAppend(&name, "data.part", data_writer_.part_number, ".bfbs",
Brian Silvermancb805822020-10-06 17:43:35 -0700330 extension_);
331 CreateBufferWriter(name, &data_writer_.writer);
Austin Schuhcb5601b2020-09-10 15:29:59 -0700332}
333
Brian Silverman0465fcf2020-09-24 00:29:18 -0700334void MultiNodeLogNamer::CreateBufferWriter(
Brian Silvermana621f522020-09-30 16:52:43 -0700335 std::string_view path, std::unique_ptr<DetachedBufferWriter> *destination) {
Brian Silverman0465fcf2020-09-24 00:29:18 -0700336 if (ran_out_of_space_) {
337 // Refuse to open any new files, which might skip data. Any existing files
338 // are in the same folder, which means they're on the same filesystem, which
339 // means they're probably going to run out of space and get stuck too.
Austin Schuha426f1f2021-03-31 22:27:41 -0700340 if (!destination->get()) {
341 // But avoid leaving a nullptr writer if we're out of space when
342 // attempting to open the first file.
343 *destination = std::make_unique<DetachedBufferWriter>(
344 DetachedBufferWriter::already_out_of_space_t());
345 }
Brian Silverman0465fcf2020-09-24 00:29:18 -0700346 return;
347 }
Austin Schuhe715eae2020-10-10 15:39:30 -0700348 const std::string_view separator = base_name_.back() == '/' ? "" : "_";
349 const std::string filename =
350 absl::StrCat(base_name_, separator, path, temp_suffix_);
Brian Silverman0465fcf2020-09-24 00:29:18 -0700351 if (!destination->get()) {
Brian Silvermana9f2ec92020-10-06 18:00:53 -0700352 if (ran_out_of_space_) {
353 *destination = std::make_unique<DetachedBufferWriter>(
354 DetachedBufferWriter::already_out_of_space_t());
355 return;
356 }
Brian Silvermancb805822020-10-06 17:43:35 -0700357 *destination =
358 std::make_unique<DetachedBufferWriter>(filename, encoder_factory_());
Brian Silvermana9f2ec92020-10-06 18:00:53 -0700359 if (!destination->get()->ran_out_of_space()) {
360 all_filenames_.emplace_back(path);
361 }
Brian Silverman0465fcf2020-09-24 00:29:18 -0700362 return;
363 }
Brian Silvermancb805822020-10-06 17:43:35 -0700364
365 CloseWriter(destination);
366 if (ran_out_of_space_) {
Brian Silvermana9f2ec92020-10-06 18:00:53 -0700367 *destination->get() =
368 DetachedBufferWriter(DetachedBufferWriter::already_out_of_space_t());
Brian Silverman0465fcf2020-09-24 00:29:18 -0700369 return;
370 }
Brian Silvermana9f2ec92020-10-06 18:00:53 -0700371
Brian Silvermancb805822020-10-06 17:43:35 -0700372 *destination->get() = DetachedBufferWriter(filename, encoder_factory_());
Brian Silvermana9f2ec92020-10-06 18:00:53 -0700373 if (!destination->get()->ran_out_of_space()) {
374 all_filenames_.emplace_back(path);
375 }
Brian Silverman0465fcf2020-09-24 00:29:18 -0700376}
377
Brian Silverman48deab12020-09-30 18:39:28 -0700378void MultiNodeLogNamer::RenameTempFile(DetachedBufferWriter *destination) {
379 if (temp_suffix_.empty()) {
380 return;
381 }
Austin Schuh6bb8a822021-03-31 23:04:39 -0700382 std::string current_filename = std::string(destination->filename());
Brian Silverman48deab12020-09-30 18:39:28 -0700383 CHECK(current_filename.size() > temp_suffix_.size());
Austin Schuh6bb8a822021-03-31 23:04:39 -0700384 std::string final_filename =
Brian Silverman48deab12020-09-30 18:39:28 -0700385 current_filename.substr(0, current_filename.size() - temp_suffix_.size());
Austin Schuh6bb8a822021-03-31 23:04:39 -0700386 int result = rename(current_filename.c_str(), final_filename.c_str());
387
388 // When changing the base name, we rename the log folder while there active
389 // buffer writers. Therefore, the name of that active buffer may still refer
390 // to the old file location rather than the new one. This minimized changes to
391 // existing code.
392 if (result != 0 && errno != ENOSPC && !old_base_name_.empty()) {
393 auto offset = current_filename.find(old_base_name_);
394 if (offset != std::string::npos) {
395 current_filename.replace(offset, old_base_name_.length(), base_name_);
396 }
397 offset = final_filename.find(old_base_name_);
398 if (offset != std::string::npos) {
399 final_filename.replace(offset, old_base_name_.length(), base_name_);
400 }
401 result = rename(current_filename.c_str(), final_filename.c_str());
402 }
403
Brian Silverman48deab12020-09-30 18:39:28 -0700404 if (result != 0) {
405 if (errno == ENOSPC) {
406 ran_out_of_space_ = true;
407 return;
408 } else {
409 PLOG(FATAL) << "Renaming " << current_filename << " to " << final_filename
410 << " failed";
411 }
Austin Schuh6bb8a822021-03-31 23:04:39 -0700412 } else {
413 VLOG(1) << "Renamed " << current_filename << " -> " << final_filename;
Brian Silverman48deab12020-09-30 18:39:28 -0700414 }
415}
416
Brian Silvermancb805822020-10-06 17:43:35 -0700417void MultiNodeLogNamer::CloseWriter(
418 std::unique_ptr<DetachedBufferWriter> *writer_pointer) {
419 DetachedBufferWriter *const writer = writer_pointer->get();
420 if (!writer) {
421 return;
422 }
Brian Silvermana9f2ec92020-10-06 18:00:53 -0700423 const bool was_open = writer->is_open();
Brian Silvermancb805822020-10-06 17:43:35 -0700424 writer->Close();
425
426 if (writer->max_write_time() > max_write_time_) {
427 max_write_time_ = writer->max_write_time();
428 max_write_time_bytes_ = writer->max_write_time_bytes();
429 max_write_time_messages_ = writer->max_write_time_messages();
430 }
431 total_write_time_ += writer->total_write_time();
432 total_write_count_ += writer->total_write_count();
433 total_write_messages_ += writer->total_write_messages();
434 total_write_bytes_ += writer->total_write_bytes();
435
436 if (writer->ran_out_of_space()) {
437 ran_out_of_space_ = true;
438 writer->acknowledge_out_of_space();
439 }
Brian Silvermana9f2ec92020-10-06 18:00:53 -0700440 if (was_open) {
441 RenameTempFile(writer);
442 } else {
443 CHECK(access(std::string(writer->filename()).c_str(), F_OK) == -1)
444 << ": File should not exist: " << writer->filename();
445 }
Brian Silvermancb805822020-10-06 17:43:35 -0700446}
447
Austin Schuhcb5601b2020-09-10 15:29:59 -0700448} // namespace logger
449} // namespace aos