blob: f192dd3d05a4971cce2c86b05d45a454cafd4836 [file] [log] [blame]
Austin Schuhcb5601b2020-09-10 15:29:59 -07001#ifndef AOS_EVENTS_LOGGING_LOG_NAMER_H_
2#define AOS_EVENTS_LOGGING_LOG_NAMER_H_
3
4#include <functional>
5#include <map>
6#include <memory>
7#include <string_view>
8#include <vector>
9
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
15namespace aos {
16namespace logger {
17
Austin Schuhb8bca732021-07-30 22:32:00 -070018// TODO(austin): Track if headers are written here much more carefully.
19//
20// TODO(austin): Rename this back to DataWriter once all other callers are of
21// the old DataWriter.
22class NewDataWriter {
23 public:
24 // Constructs a NewDataWriter.
25 // reopen is called whenever a file needs to be reopened.
26 // close is called to close that file and extract any statistics.
27 NewDataWriter(std::function<void(NewDataWriter *)> reopen,
28 std::function<void(NewDataWriter *)> close)
29 : reopen_(std::move(reopen)), close_(std::move(close)) {
30 reopen_(this);
31 }
32
33 NewDataWriter(NewDataWriter &&other) = default;
34 aos::logger::NewDataWriter &operator=(NewDataWriter &&other) = default;
35 NewDataWriter(const NewDataWriter &) = delete;
36 void operator=(const NewDataWriter &) = delete;
37
38 ~NewDataWriter() {
39 if (writer) {
40 Close();
41 }
42 }
43
44 void Rotate() {
45 ++part_number;
46 reopen_(this);
47 }
48
49 // TODO(austin): Store common header in LogNamer.
50 //
51 // TODO(austin): Copy header and add all UUIDs and such when available
52 // whenever data is written.
53 //
54 // TODO(austin): Automatically write the header and update on boot UUID
55 // change.
56 //
57 // TODO(austin): Add known timestamps for each node every time we cycle a log
58 // for sorting.
59
60 void QueueSizedFlatbuffer(flatbuffers::FlatBufferBuilder *fbb,
61 aos::monotonic_clock::time_point now) {
62 writer->QueueSizedFlatbuffer(fbb, now);
63 }
64
65 std::string_view filename() const { return writer->filename(); }
66
67 void Reboot() {
68 uuid_ = UUID::Random();
69 Rotate();
70 }
71
72 void Close() {
73 CHECK(writer);
74 close_(this);
75 writer.reset();
76 }
77
78 std::unique_ptr<DetachedBufferWriter> writer = nullptr;
79 const Node *node = nullptr;
80 size_t part_number = 0;
81 const UUID &uuid() const { return uuid_; }
82
83 private:
84 UUID uuid_ = UUID::Random();
85 std::function<void(NewDataWriter *)> reopen_;
86 std::function<void(NewDataWriter *)> close_;
87};
88
Austin Schuhcb5601b2020-09-10 15:29:59 -070089// Interface describing how to name, track, and add headers to log file parts.
90class LogNamer {
91 public:
92 // Constructs a LogNamer with the primary node (ie the one the logger runs on)
93 // being node.
94 LogNamer(const Node *node) : node_(node) { nodes_.emplace_back(node_); }
95 virtual ~LogNamer() {}
96
Austin Schuh6bb8a822021-03-31 23:04:39 -070097 virtual std::string_view base_name() const = 0;
98
99 // Rotate should be called at least once in between calls to set_base_name.
100 // Otherwise temporary files will not be recoverable.
101 // Rotate is called by Logger::RenameLogBase, which is currently the only user
102 // of this method.
103 // Only renaming the folder is supported, not the file base name.
104 virtual void set_base_name(std::string_view base_name) = 0;
105
Austin Schuhcb5601b2020-09-10 15:29:59 -0700106 // Writes the header to all log files for a specific node. This function
107 // needs to be called after all the writers are created.
108 //
109 // Modifies header to contain the uuid and part number for each writer as it
110 // writes it. Since this is done unconditionally, it does not restore the
111 // previous value at the end.
112 virtual void WriteHeader(
113 aos::SizePrefixedFlatbufferDetachedBuffer<LogFileHeader> *header,
114 const Node *node) = 0;
115
Brian Silverman87ac0402020-09-17 14:47:01 -0700116 // Returns a writer for writing data from messages on this channel (on the
117 // primary node).
118 //
119 // The returned pointer will stay valid across rotations, but the object it
120 // points to will be assigned to.
Austin Schuhb8bca732021-07-30 22:32:00 -0700121 virtual NewDataWriter *MakeWriter(const Channel *channel) = 0;
Austin Schuhcb5601b2020-09-10 15:29:59 -0700122
Brian Silverman87ac0402020-09-17 14:47:01 -0700123 // Returns a writer for writing timestamps from messages on this channel (on
124 // the primary node).
125 //
126 // The returned pointer will stay valid across rotations, but the object it
127 // points to will be assigned to.
Austin Schuhb8bca732021-07-30 22:32:00 -0700128 virtual NewDataWriter *MakeTimestampWriter(const Channel *channel) = 0;
Austin Schuhcb5601b2020-09-10 15:29:59 -0700129
130 // Returns a writer for writing timestamps delivered over the special
131 // /aos/remote_timestamps/* channels. node is the node that the timestamps
Brian Silverman87ac0402020-09-17 14:47:01 -0700132 // are forwarded back from (to the primary node).
133 //
134 // The returned pointer will stay valid across rotations, but the object it
135 // points to will be assigned to.
Austin Schuhb8bca732021-07-30 22:32:00 -0700136 virtual NewDataWriter *MakeForwardedTimestampWriter(
Austin Schuhcb5601b2020-09-10 15:29:59 -0700137 const Channel *channel, const Node *node) = 0;
138
139 // Rotates all log files for the provided node. The provided header will be
140 // modified and written per WriteHeader above.
141 virtual void Rotate(
142 const Node *node,
143 aos::SizePrefixedFlatbufferDetachedBuffer<LogFileHeader> *header) = 0;
144
Austin Schuh315b96b2020-12-11 21:21:12 -0800145 // Reboots all log files for the provided node. The provided header will be
146 // modified and written per WriteHeader above. Resets any parts UUIDs.
147 virtual void Reboot(
148 const Node *node,
149 aos::SizePrefixedFlatbufferDetachedBuffer<LogFileHeader> *header) = 0;
150
Austin Schuhcb5601b2020-09-10 15:29:59 -0700151 // Returns all the nodes that data is being written for.
152 const std::vector<const Node *> &nodes() const { return nodes_; }
153
154 // Returns the node the logger is running on.
155 const Node *node() const { return node_; }
156
Austin Schuh8c399962020-12-25 21:51:45 -0800157 // Writes out the nested Configuration object to the config file location.
158 virtual void WriteConfiguration(
159 aos::SizePrefixedFlatbufferDetachedBuffer<LogFileHeader> *header,
160 std::string_view config_sha256) = 0;
161
Austin Schuhcb5601b2020-09-10 15:29:59 -0700162 protected:
163 // Modifies the header to have the provided UUID and part id.
164 void UpdateHeader(
165 aos::SizePrefixedFlatbufferDetachedBuffer<LogFileHeader> *header,
166 const UUID &uuid, int part_id) const;
167
168 const Node *const node_;
169 std::vector<const Node *> nodes_;
170};
171
172// Local log namer is a simple version which only names things
173// "base_name.part#.bfbs" and increments the part number. It doesn't support
174// any other log type.
175class LocalLogNamer : public LogNamer {
176 public:
177 LocalLogNamer(std::string_view base_name, const Node *node)
178 : LogNamer(node),
179 base_name_(base_name),
Austin Schuhb8bca732021-07-30 22:32:00 -0700180 data_writer_(
181 [this](NewDataWriter *writer) {
182 writer->writer = std::make_unique<DetachedBufferWriter>(
183 absl::StrCat(base_name_, ".part", writer->part_number,
184 ".bfbs"),
185 std::make_unique<aos::logger::DummyEncoder>());
186 },
187 [](NewDataWriter * /*writer*/) {}) {}
188
189 LocalLogNamer(const LocalLogNamer &) = delete;
190 LocalLogNamer(LocalLogNamer &&) = delete;
191 LocalLogNamer &operator=(const LocalLogNamer &) = delete;
192 LocalLogNamer &operator=(LocalLogNamer &&) = delete;
193
Brian Silverman0465fcf2020-09-24 00:29:18 -0700194 ~LocalLogNamer() override = default;
Austin Schuhcb5601b2020-09-10 15:29:59 -0700195
Austin Schuh6bb8a822021-03-31 23:04:39 -0700196 std::string_view base_name() const final { return base_name_; }
197
198 void set_base_name(std::string_view base_name) final {
199 base_name_ = base_name;
200 }
201
Austin Schuhcb5601b2020-09-10 15:29:59 -0700202 void WriteHeader(
203 aos::SizePrefixedFlatbufferDetachedBuffer<LogFileHeader> *header,
204 const Node *node) override;
205
Austin Schuhb8bca732021-07-30 22:32:00 -0700206 NewDataWriter *MakeWriter(const Channel *channel) override;
Austin Schuhcb5601b2020-09-10 15:29:59 -0700207
208 void Rotate(const Node *node,
209 aos::SizePrefixedFlatbufferDetachedBuffer<LogFileHeader> *header)
210 override;
211
Austin Schuh315b96b2020-12-11 21:21:12 -0800212 void Reboot(const Node *node,
213 aos::SizePrefixedFlatbufferDetachedBuffer<LogFileHeader> *header)
214 override;
215
Austin Schuhb8bca732021-07-30 22:32:00 -0700216 NewDataWriter *MakeTimestampWriter(const Channel *channel) override;
Austin Schuhcb5601b2020-09-10 15:29:59 -0700217
Austin Schuhb8bca732021-07-30 22:32:00 -0700218 NewDataWriter *MakeForwardedTimestampWriter(
Austin Schuhcb5601b2020-09-10 15:29:59 -0700219 const Channel * /*channel*/, const Node * /*node*/) override;
220
Austin Schuh8c399962020-12-25 21:51:45 -0800221 void WriteConfiguration(
222 aos::SizePrefixedFlatbufferDetachedBuffer<LogFileHeader> *header,
223 std::string_view config_sha256) override;
224
Austin Schuhcb5601b2020-09-10 15:29:59 -0700225 private:
Austin Schuh6bb8a822021-03-31 23:04:39 -0700226 std::string base_name_;
Austin Schuhb8bca732021-07-30 22:32:00 -0700227
228 NewDataWriter data_writer_;
Austin Schuhcb5601b2020-09-10 15:29:59 -0700229};
230
231// Log namer which uses a config and a base name to name a bunch of files.
232class MultiNodeLogNamer : public LogNamer {
233 public:
Brian Silvermancb805822020-10-06 17:43:35 -0700234 MultiNodeLogNamer(std::string_view base_name,
235 const Configuration *configuration, const Node *node);
236 ~MultiNodeLogNamer() override;
237
Austin Schuh6bb8a822021-03-31 23:04:39 -0700238 std::string_view base_name() const final { return base_name_; }
239
240 void set_base_name(std::string_view base_name) final {
241 old_base_name_ = base_name_;
242 base_name_ = base_name;
243 }
Brian Silvermancb805822020-10-06 17:43:35 -0700244
Brian Silverman48deab12020-09-30 18:39:28 -0700245 // If temp_suffix is set, then this will write files under names beginning
246 // with the specified suffix, and then rename them to the desired name after
247 // they are fully written.
248 //
249 // This is useful to enable incremental copying of the log files.
250 //
251 // Defaults to writing directly to the final filename.
Brian Silvermancb805822020-10-06 17:43:35 -0700252 void set_temp_suffix(std::string_view temp_suffix) {
253 temp_suffix_ = temp_suffix;
254 }
Austin Schuhcb5601b2020-09-10 15:29:59 -0700255
Brian Silvermancb805822020-10-06 17:43:35 -0700256 // Sets the function for creating encoders.
257 //
258 // Defaults to just creating DummyEncoders.
259 void set_encoder_factory(
260 std::function<std::unique_ptr<DetachedBufferEncoder>()> encoder_factory) {
261 encoder_factory_ = std::move(encoder_factory);
262 }
263
264 // Sets an additional file extension.
265 //
266 // Defaults to nothing.
267 void set_extension(std::string_view extension) { extension_ = extension; }
Brian Silverman1f345222020-09-24 21:14:48 -0700268
Brian Silvermana621f522020-09-30 16:52:43 -0700269 // A list of all the filenames we've written.
270 //
271 // This only includes the part after base_name().
272 const std::vector<std::string> &all_filenames() const {
273 return all_filenames_;
274 }
275
Austin Schuhcb5601b2020-09-10 15:29:59 -0700276 void WriteHeader(
277 aos::SizePrefixedFlatbufferDetachedBuffer<LogFileHeader> *header,
278 const Node *node) override;
279
280 void Rotate(const Node *node,
281 aos::SizePrefixedFlatbufferDetachedBuffer<LogFileHeader> *header)
282 override;
283
Austin Schuh315b96b2020-12-11 21:21:12 -0800284 void Reboot(const Node *node,
285 aos::SizePrefixedFlatbufferDetachedBuffer<LogFileHeader> *header)
286 override;
287
Austin Schuh8c399962020-12-25 21:51:45 -0800288 void WriteConfiguration(
289 aos::SizePrefixedFlatbufferDetachedBuffer<LogFileHeader> *header,
290 std::string_view config_sha256) override;
291
Austin Schuhb8bca732021-07-30 22:32:00 -0700292 NewDataWriter *MakeWriter(const Channel *channel) override;
Austin Schuhcb5601b2020-09-10 15:29:59 -0700293
Austin Schuhb8bca732021-07-30 22:32:00 -0700294 NewDataWriter *MakeForwardedTimestampWriter(const Channel *channel,
Brian Silvermand90905f2020-09-23 14:42:56 -0700295 const Node *node) override;
Austin Schuhcb5601b2020-09-10 15:29:59 -0700296
Austin Schuhb8bca732021-07-30 22:32:00 -0700297 NewDataWriter *MakeTimestampWriter(const Channel *channel) override;
Austin Schuhcb5601b2020-09-10 15:29:59 -0700298
Brian Silverman0465fcf2020-09-24 00:29:18 -0700299 // Indicates that at least one file ran out of space. Once this happens, we
300 // stop trying to open new files, to avoid writing any files with holes from
301 // previous parts.
302 //
303 // Besides this function, this object will silently stop logging data when
304 // this occurs. If you want to ensure log files are complete, you must call
305 // this method.
Brian Silvermana9f2ec92020-10-06 18:00:53 -0700306 bool ran_out_of_space() const {
307 return accumulate_data_writers<bool>(
Austin Schuhb8bca732021-07-30 22:32:00 -0700308 ran_out_of_space_, [](bool x, const NewDataWriter &data_writer) {
Brian Silvermana9f2ec92020-10-06 18:00:53 -0700309 return x ||
310 (data_writer.writer && data_writer.writer->ran_out_of_space());
311 });
312 }
Brian Silverman0465fcf2020-09-24 00:29:18 -0700313
Brian Silverman1f345222020-09-24 21:14:48 -0700314 // Returns the maximum total_bytes() value for all existing
315 // DetachedBufferWriters.
316 //
317 // Returns 0 if no files are open.
318 size_t maximum_total_bytes() const {
Brian Silvermancb805822020-10-06 17:43:35 -0700319 return accumulate_data_writers<size_t>(
Austin Schuhb8bca732021-07-30 22:32:00 -0700320 0, [](size_t x, const NewDataWriter &data_writer) {
Brian Silvermancb805822020-10-06 17:43:35 -0700321 return std::max(x, data_writer.writer->total_bytes());
322 });
Brian Silverman1f345222020-09-24 21:14:48 -0700323 }
324
Brian Silverman0465fcf2020-09-24 00:29:18 -0700325 // Closes all existing log files. No more data may be written after this.
326 //
327 // This may set ran_out_of_space().
328 void Close();
329
Brian Silvermancb805822020-10-06 17:43:35 -0700330 // Accessors for various statistics. See the identically-named methods in
331 // DetachedBufferWriter for documentation. These are aggregated across all
332 // past and present DetachedBufferWriters.
333 std::chrono::nanoseconds max_write_time() const {
334 return accumulate_data_writers(
335 max_write_time_,
Austin Schuhb8bca732021-07-30 22:32:00 -0700336 [](std::chrono::nanoseconds x, const NewDataWriter &data_writer) {
Brian Silvermancb805822020-10-06 17:43:35 -0700337 return std::max(x, data_writer.writer->max_write_time());
338 });
339 }
340 int max_write_time_bytes() const {
341 return std::get<0>(accumulate_data_writers(
342 std::make_tuple(max_write_time_bytes_, max_write_time_),
343 [](std::tuple<int, std::chrono::nanoseconds> x,
Austin Schuhb8bca732021-07-30 22:32:00 -0700344 const NewDataWriter &data_writer) {
Brian Silvermancb805822020-10-06 17:43:35 -0700345 if (data_writer.writer->max_write_time() > std::get<1>(x)) {
346 return std::make_tuple(data_writer.writer->max_write_time_bytes(),
347 data_writer.writer->max_write_time());
348 }
349 return x;
350 }));
351 }
352 int max_write_time_messages() const {
353 return std::get<0>(accumulate_data_writers(
354 std::make_tuple(max_write_time_messages_, max_write_time_),
355 [](std::tuple<int, std::chrono::nanoseconds> x,
Austin Schuhb8bca732021-07-30 22:32:00 -0700356 const NewDataWriter &data_writer) {
Brian Silvermancb805822020-10-06 17:43:35 -0700357 if (data_writer.writer->max_write_time() > std::get<1>(x)) {
358 return std::make_tuple(
359 data_writer.writer->max_write_time_messages(),
360 data_writer.writer->max_write_time());
361 }
362 return x;
363 }));
364 }
365 std::chrono::nanoseconds total_write_time() const {
366 return accumulate_data_writers(
367 total_write_time_,
Austin Schuhb8bca732021-07-30 22:32:00 -0700368 [](std::chrono::nanoseconds x, const NewDataWriter &data_writer) {
Brian Silvermancb805822020-10-06 17:43:35 -0700369 return x + data_writer.writer->total_write_time();
370 });
371 }
372 int total_write_count() const {
373 return accumulate_data_writers(
Austin Schuhb8bca732021-07-30 22:32:00 -0700374 total_write_count_, [](int x, const NewDataWriter &data_writer) {
Brian Silvermancb805822020-10-06 17:43:35 -0700375 return x + data_writer.writer->total_write_count();
376 });
377 }
378 int total_write_messages() const {
379 return accumulate_data_writers(
Austin Schuhb8bca732021-07-30 22:32:00 -0700380 total_write_messages_, [](int x, const NewDataWriter &data_writer) {
Brian Silvermancb805822020-10-06 17:43:35 -0700381 return x + data_writer.writer->total_write_messages();
382 });
383 }
384 int total_write_bytes() const {
385 return accumulate_data_writers(
Austin Schuhb8bca732021-07-30 22:32:00 -0700386 total_write_bytes_, [](int x, const NewDataWriter &data_writer) {
Brian Silvermancb805822020-10-06 17:43:35 -0700387 return x + data_writer.writer->total_write_bytes();
388 });
389 }
390
391 void ResetStatistics();
392
Austin Schuhcb5601b2020-09-10 15:29:59 -0700393 private:
Austin Schuh315b96b2020-12-11 21:21:12 -0800394 // Implements Rotate and Reboot, controlled by the 'reboot' flag. The only
395 // difference between the two is if DataWriter::uuid is reset or not.
396 void DoRotate(
397 const Node *node,
398 aos::SizePrefixedFlatbufferDetachedBuffer<LogFileHeader> *header,
399 bool reboot);
400
Austin Schuhcb5601b2020-09-10 15:29:59 -0700401 // Opens up a writer for timestamps forwarded back.
402 void OpenForwardedTimestampWriter(const Channel *channel,
Austin Schuhb8bca732021-07-30 22:32:00 -0700403 NewDataWriter *data_writer);
Austin Schuhcb5601b2020-09-10 15:29:59 -0700404
405 // Opens up a writer for remote data.
Austin Schuhb8bca732021-07-30 22:32:00 -0700406 void OpenWriter(const Channel *channel, NewDataWriter *data_writer);
Austin Schuhcb5601b2020-09-10 15:29:59 -0700407
408 // Opens the main data writer file for this node responsible for data_writer_.
Brian Silvermana621f522020-09-30 16:52:43 -0700409 void OpenDataWriter();
Austin Schuhcb5601b2020-09-10 15:29:59 -0700410
Brian Silvermana621f522020-09-30 16:52:43 -0700411 void CreateBufferWriter(std::string_view path,
Brian Silverman0465fcf2020-09-24 00:29:18 -0700412 std::unique_ptr<DetachedBufferWriter> *destination);
413
Brian Silverman48deab12020-09-30 18:39:28 -0700414 void RenameTempFile(DetachedBufferWriter *destination);
415
Brian Silvermancb805822020-10-06 17:43:35 -0700416 void CloseWriter(std::unique_ptr<DetachedBufferWriter> *writer_pointer);
Austin Schuhcb5601b2020-09-10 15:29:59 -0700417
Brian Silvermancb805822020-10-06 17:43:35 -0700418 // A version of std::accumulate which operates over all of our DataWriters.
419 template <typename T, typename BinaryOperation>
420 T accumulate_data_writers(T t, BinaryOperation op) const {
Austin Schuhb8bca732021-07-30 22:32:00 -0700421 for (const std::pair<const Channel *const, NewDataWriter> &data_writer :
Brian Silvermancb805822020-10-06 17:43:35 -0700422 data_writers_) {
Austin Schuhad0cfc32020-12-21 12:34:26 -0800423 if (!data_writer.second.writer) continue;
Brian Silvermancb805822020-10-06 17:43:35 -0700424 t = op(std::move(t), data_writer.second);
425 }
Austin Schuhb8bca732021-07-30 22:32:00 -0700426 if (data_writer_) {
427 t = op(std::move(t), *data_writer_);
Brian Silvermancb805822020-10-06 17:43:35 -0700428 }
429 return t;
430 }
431
Austin Schuh6bb8a822021-03-31 23:04:39 -0700432 std::string base_name_;
433 std::string old_base_name_;
Brian Silvermancb805822020-10-06 17:43:35 -0700434 const Configuration *const configuration_;
Austin Schuhcb5601b2020-09-10 15:29:59 -0700435
Brian Silverman0465fcf2020-09-24 00:29:18 -0700436 bool ran_out_of_space_ = false;
Brian Silvermana621f522020-09-30 16:52:43 -0700437 std::vector<std::string> all_filenames_;
Brian Silverman0465fcf2020-09-24 00:29:18 -0700438
Brian Silvermancb805822020-10-06 17:43:35 -0700439 std::string temp_suffix_;
440 std::function<std::unique_ptr<DetachedBufferEncoder>()> encoder_factory_ =
441 []() { return std::make_unique<DummyEncoder>(); };
442 std::string extension_;
443
444 // Storage for statistics from previously-rotated DetachedBufferWriters.
445 std::chrono::nanoseconds max_write_time_ = std::chrono::nanoseconds::zero();
446 int max_write_time_bytes_ = -1;
447 int max_write_time_messages_ = -1;
448 std::chrono::nanoseconds total_write_time_ = std::chrono::nanoseconds::zero();
449 int total_write_count_ = 0;
450 int total_write_messages_ = 0;
451 int total_write_bytes_ = 0;
452
Austin Schuhcb5601b2020-09-10 15:29:59 -0700453 // File to write both delivery timestamps and local data to.
Austin Schuhb8bca732021-07-30 22:32:00 -0700454 std::unique_ptr<NewDataWriter> data_writer_;
Austin Schuhcb5601b2020-09-10 15:29:59 -0700455
Austin Schuhb8bca732021-07-30 22:32:00 -0700456 std::map<const Channel *, NewDataWriter> data_writers_;
Austin Schuhcb5601b2020-09-10 15:29:59 -0700457};
458
459} // namespace logger
460} // namespace aos
461
462#endif // AOS_EVENTS_LOGGING_LOG_NAMER_H_