Austin Schuh | e309d2a | 2019-11-29 13:25:21 -0800 | [diff] [blame] | 1 | #ifndef AOS_EVENTS_LOGGER_H_ |
| 2 | #define AOS_EVENTS_LOGGER_H_ |
| 3 | |
| 4 | #include <deque> |
| 5 | #include <vector> |
Austin Schuh | 05b7047 | 2020-01-01 17:11:17 -0800 | [diff] [blame] | 6 | #include <string_view> |
Austin Schuh | e309d2a | 2019-11-29 13:25:21 -0800 | [diff] [blame] | 7 | |
Austin Schuh | e309d2a | 2019-11-29 13:25:21 -0800 | [diff] [blame] | 8 | #include "absl/types/span.h" |
| 9 | #include "aos/events/event_loop.h" |
Austin Schuh | a36c890 | 2019-12-30 18:07:15 -0800 | [diff] [blame] | 10 | #include "aos/events/logging/logfile_utils.h" |
James Kuszmaul | 38735e8 | 2019-12-07 16:42:06 -0800 | [diff] [blame] | 11 | #include "aos/events/logging/logger_generated.h" |
Austin Schuh | 9254752 | 2019-12-28 14:33:43 -0800 | [diff] [blame] | 12 | #include "aos/events/simulated_event_loop.h" |
Austin Schuh | e309d2a | 2019-11-29 13:25:21 -0800 | [diff] [blame] | 13 | #include "aos/time/time.h" |
| 14 | #include "flatbuffers/flatbuffers.h" |
| 15 | |
| 16 | namespace aos { |
| 17 | namespace logger { |
| 18 | |
Austin Schuh | e309d2a | 2019-11-29 13:25:21 -0800 | [diff] [blame] | 19 | // Logs all channels available in the event loop to disk every 100 ms. |
| 20 | // Start by logging one message per channel to capture any state and |
| 21 | // configuration that is sent rately on a channel and would affect execution. |
| 22 | class Logger { |
| 23 | public: |
| 24 | Logger(DetachedBufferWriter *writer, EventLoop *event_loop, |
| 25 | std::chrono::milliseconds polling_period = |
| 26 | std::chrono::milliseconds(100)); |
| 27 | |
| 28 | private: |
| 29 | void DoLogData(); |
| 30 | |
| 31 | EventLoop *event_loop_; |
| 32 | DetachedBufferWriter *writer_; |
| 33 | |
| 34 | // Structure to track both a fetcher, and if the data fetched has been |
| 35 | // written. We may want to delay writing data to disk so that we don't let |
| 36 | // data get too far out of order when written to disk so we can avoid making |
| 37 | // it too hard to sort when reading. |
| 38 | struct FetcherStruct { |
| 39 | std::unique_ptr<RawFetcher> fetcher; |
| 40 | bool written = false; |
Austin Schuh | 15649d6 | 2019-12-28 16:36:38 -0800 | [diff] [blame] | 41 | |
| 42 | LogType log_type; |
Austin Schuh | e309d2a | 2019-11-29 13:25:21 -0800 | [diff] [blame] | 43 | }; |
| 44 | |
| 45 | std::vector<FetcherStruct> fetchers_; |
| 46 | TimerHandler *timer_handler_; |
| 47 | |
| 48 | // Period to poll the channels. |
| 49 | const std::chrono::milliseconds polling_period_; |
| 50 | |
| 51 | // Last time that data was written for all channels to disk. |
| 52 | monotonic_clock::time_point last_synchronized_time_; |
| 53 | |
| 54 | // Max size that the header has consumed. This much extra data will be |
| 55 | // reserved in the builder to avoid reallocating. |
| 56 | size_t max_header_size_ = 0; |
| 57 | }; |
| 58 | |
| 59 | // Replays all the channels in the logfile to the event loop. |
| 60 | class LogReader { |
| 61 | public: |
James Kuszmaul | c7bbb3e | 2020-01-03 20:01:00 -0800 | [diff] [blame] | 62 | // If you want to supply a new configuration that will be used for replay |
| 63 | // (e.g., to change message rates, or to populate an updated schema), then |
| 64 | // pass it in here. It must provide all the channels that the original logged |
| 65 | // config did. |
| 66 | LogReader(std::string_view filename, |
| 67 | const Configuration *replay_configuration = nullptr); |
James Kuszmaul | 7daef36 | 2019-12-31 18:28:17 -0800 | [diff] [blame] | 68 | ~LogReader(); |
Austin Schuh | e309d2a | 2019-11-29 13:25:21 -0800 | [diff] [blame] | 69 | |
Austin Schuh | 6331ef9 | 2020-01-07 18:28:09 -0800 | [diff] [blame^] | 70 | // Registers all the callbacks to send the log file data out on an event loop |
| 71 | // created in event_loop_factory. This also updates time to be at the start |
| 72 | // of the log file by running until the log file starts. |
| 73 | // Note: the configuration used in the factory should be configuration() |
| 74 | // below, but can be anything as long as the locations needed to send |
| 75 | // everything are available. |
James Kuszmaul | 84ff3e5 | 2020-01-03 19:48:53 -0800 | [diff] [blame] | 76 | void Register(SimulatedEventLoopFactory *event_loop_factory); |
Austin Schuh | 6331ef9 | 2020-01-07 18:28:09 -0800 | [diff] [blame^] | 77 | // Creates an SimulatedEventLoopFactory accessible via event_loop_factory(), |
| 78 | // and then calls Register. |
| 79 | void Register(); |
| 80 | // Registers callbacks for all the events after the log file starts. This is |
| 81 | // only useful when replaying live. |
Austin Schuh | e309d2a | 2019-11-29 13:25:21 -0800 | [diff] [blame] | 82 | void Register(EventLoop *event_loop); |
Austin Schuh | 6331ef9 | 2020-01-07 18:28:09 -0800 | [diff] [blame^] | 83 | |
James Kuszmaul | 84ff3e5 | 2020-01-03 19:48:53 -0800 | [diff] [blame] | 84 | // Unregisters the senders. You only need to call this if you separately |
| 85 | // supplied an event loop or event loop factory and the lifetimes are such |
| 86 | // that they need to be explicitly destroyed before the LogReader destructor |
| 87 | // gets called. |
Austin Schuh | e309d2a | 2019-11-29 13:25:21 -0800 | [diff] [blame] | 88 | void Deregister(); |
| 89 | |
Austin Schuh | e309d2a | 2019-11-29 13:25:21 -0800 | [diff] [blame] | 90 | // Returns the configuration from the log file. |
James Kuszmaul | c7bbb3e | 2020-01-03 20:01:00 -0800 | [diff] [blame] | 91 | const Configuration *logged_configuration() const; |
| 92 | // Returns the configuration being used for replay. |
Austin Schuh | 15649d6 | 2019-12-28 16:36:38 -0800 | [diff] [blame] | 93 | const Configuration *configuration() const; |
| 94 | |
Austin Schuh | 6331ef9 | 2020-01-07 18:28:09 -0800 | [diff] [blame^] | 95 | const LogFileHeader *log_file_header() const { |
| 96 | return sorted_message_reader_.log_file_header(); |
| 97 | } |
| 98 | |
| 99 | // Returns the node that this log file was created on. This is a pointer to a |
| 100 | // node in the nodes() list inside configuration(). |
Austin Schuh | 15649d6 | 2019-12-28 16:36:38 -0800 | [diff] [blame] | 101 | const Node *node() const; |
Austin Schuh | e309d2a | 2019-11-29 13:25:21 -0800 | [diff] [blame] | 102 | |
| 103 | // Returns the starting timestamp for the log file. |
| 104 | monotonic_clock::time_point monotonic_start_time(); |
| 105 | realtime_clock::time_point realtime_start_time(); |
| 106 | |
James Kuszmaul | c7bbb3e | 2020-01-03 20:01:00 -0800 | [diff] [blame] | 107 | // Causes the logger to publish the provided channel on a different name so |
| 108 | // that replayed applications can publish on the proper channel name without |
| 109 | // interference. This operates on raw channel names, without any node or |
| 110 | // application specific mappings. |
| 111 | void RemapLoggedChannel(std::string_view name, std::string_view type, |
| 112 | std::string_view add_prefix = "/original"); |
| 113 | template <typename T> |
| 114 | void RemapLoggedChannel(std::string_view name, |
| 115 | std::string_view add_prefix = "/original") { |
| 116 | RemapLoggedChannel(name, T::GetFullyQualifiedName(), add_prefix); |
| 117 | } |
| 118 | |
James Kuszmaul | 84ff3e5 | 2020-01-03 19:48:53 -0800 | [diff] [blame] | 119 | SimulatedEventLoopFactory *event_loop_factory() { |
| 120 | return event_loop_factory_; |
| 121 | } |
| 122 | |
Austin Schuh | e309d2a | 2019-11-29 13:25:21 -0800 | [diff] [blame] | 123 | // TODO(austin): Add the ability to re-publish the fetched messages. Add 2 |
| 124 | // options, one which publishes them *now*, and another which publishes them |
| 125 | // to the simulated event loop factory back in time where they actually |
| 126 | // happened. |
| 127 | |
| 128 | private: |
Austin Schuh | e309d2a | 2019-11-29 13:25:21 -0800 | [diff] [blame] | 129 | // Queues at least max_out_of_order_duration_ messages into channels_. |
| 130 | void QueueMessages(); |
James Kuszmaul | c7bbb3e | 2020-01-03 20:01:00 -0800 | [diff] [blame] | 131 | // Handle constructing a configuration with all the additional remapped |
| 132 | // channels from calls to RemapLoggedChannel. |
| 133 | void MakeRemappedConfig(); |
Austin Schuh | e309d2a | 2019-11-29 13:25:21 -0800 | [diff] [blame] | 134 | |
Austin Schuh | 05b7047 | 2020-01-01 17:11:17 -0800 | [diff] [blame] | 135 | // Log chunk reader. |
| 136 | SortedMessageReader sorted_message_reader_; |
Austin Schuh | e309d2a | 2019-11-29 13:25:21 -0800 | [diff] [blame] | 137 | |
James Kuszmaul | 84ff3e5 | 2020-01-03 19:48:53 -0800 | [diff] [blame] | 138 | std::unique_ptr<FlatbufferDetachedBuffer<Configuration>> |
| 139 | remapped_configuration_buffer_; |
| 140 | |
Austin Schuh | 05b7047 | 2020-01-01 17:11:17 -0800 | [diff] [blame] | 141 | std::vector<std::unique_ptr<RawSender>> channels_; |
Austin Schuh | e309d2a | 2019-11-29 13:25:21 -0800 | [diff] [blame] | 142 | |
Austin Schuh | 9254752 | 2019-12-28 14:33:43 -0800 | [diff] [blame] | 143 | std::unique_ptr<EventLoop> event_loop_unique_ptr_; |
| 144 | EventLoop *event_loop_ = nullptr; |
Austin Schuh | e309d2a | 2019-11-29 13:25:21 -0800 | [diff] [blame] | 145 | TimerHandler *timer_handler_; |
James Kuszmaul | 84ff3e5 | 2020-01-03 19:48:53 -0800 | [diff] [blame] | 146 | |
| 147 | std::unique_ptr<SimulatedEventLoopFactory> event_loop_factory_unique_ptr_; |
| 148 | SimulatedEventLoopFactory *event_loop_factory_ = nullptr; |
James Kuszmaul | c7bbb3e | 2020-01-03 20:01:00 -0800 | [diff] [blame] | 149 | |
| 150 | // Map of channel indices to new name. The channel index will be an index into |
| 151 | // logged_configuration(), and the string key will be the name of the channel |
| 152 | // to send on instead of the logged channel name. |
| 153 | std::map<size_t, std::string> remapped_channels_; |
| 154 | |
| 155 | const Configuration *remapped_configuration_ = nullptr; |
| 156 | const Configuration *replay_configuration_ = nullptr; |
Austin Schuh | e309d2a | 2019-11-29 13:25:21 -0800 | [diff] [blame] | 157 | }; |
| 158 | |
| 159 | } // namespace logger |
| 160 | } // namespace aos |
| 161 | |
| 162 | #endif // AOS_EVENTS_LOGGER_H_ |