blob: d1481d50874bf174f73a64effb792ca20bb3ab6f [file] [log] [blame]
James Kuszmaul38735e82019-12-07 16:42:06 -08001#include <iostream>
2
3#include "aos/configuration.h"
4#include "aos/events/logging/logger.h"
5#include "aos/events/simulated_event_loop.h"
6#include "aos/init.h"
7#include "aos/json_to_flatbuffer.h"
8#include "gflags/gflags.h"
9
James Kuszmaul38735e82019-12-07 16:42:06 -080010DEFINE_string(
11 name, "",
12 "Name to match for printing out channels. Empty means no name filter.");
13DEFINE_string(type, "",
14 "Channel type to match for printing out channels. Empty means no "
15 "type filter.");
Austin Schuha81454b2020-05-12 19:58:36 -070016DEFINE_bool(fetch, false,
17 "If true, also print out the messages from before the start of the "
18 "log file");
Austin Schuh6f3babe2020-01-26 20:34:50 -080019DEFINE_bool(raw, false,
20 "If true, just print the data out unsorted and unparsed");
Austin Schuha81454b2020-05-12 19:58:36 -070021DEFINE_bool(format_raw, true,
22 "If true and --raw is specified, print out raw data, but use the "
23 "schema to format the data.");
Austin Schuhae46f362020-04-11 19:52:56 -070024DEFINE_int32(max_vector_size, 100,
25 "If positive, vectors longer than this will not be printed");
Austin Schuh6f3babe2020-01-26 20:34:50 -080026
Austin Schuha81454b2020-05-12 19:58:36 -070027void LogContext(const aos::Channel *channel, std::string node_name,
28 const aos::Context &context) {
29 // Print the flatbuffer out to stdout, both to remove the
30 // unnecessary cruft from glog and to allow the user to readily
31 // redirect just the logged output independent of any debugging
32 // information on stderr.
33 if (context.monotonic_remote_time != context.monotonic_event_time) {
34 std::cout << node_name << context.realtime_event_time << " ("
35 << context.monotonic_event_time << ") sent "
36 << context.realtime_remote_time << " ("
37 << context.monotonic_remote_time << ") "
38 << channel->name()->c_str() << ' ' << channel->type()->c_str()
39 << ": "
40 << aos::FlatbufferToJson(
41 channel->schema(),
42 static_cast<const uint8_t *>(context.data),
43 FLAGS_max_vector_size)
44 << std::endl;
45 } else {
46 std::cout << node_name << context.realtime_event_time << " ("
47 << context.monotonic_event_time << ") "
48 << channel->name()->c_str() << ' ' << channel->type()->c_str()
49 << ": "
50 << aos::FlatbufferToJson(
51 channel->schema(),
52 static_cast<const uint8_t *>(context.data),
53 FLAGS_max_vector_size)
54 << std::endl;
55 }
56}
57
James Kuszmaul38735e82019-12-07 16:42:06 -080058int main(int argc, char **argv) {
59 gflags::SetUsageMessage(
Austin Schuh6f3babe2020-01-26 20:34:50 -080060 "Usage:\n"
61 " log_cat [args] logfile1 logfile2 ...\n"
62 "\n"
James Kuszmaul38735e82019-12-07 16:42:06 -080063 "This program provides a basic interface to dump data from a logfile to "
64 "stdout. Given a logfile, channel name filter, and type filter, it will "
65 "print all the messages in the logfile matching the filters. The message "
66 "filters work by taking the values of --name and --type and printing any "
67 "channel whose name contains --name as a substr and whose type contains "
68 "--type as a substr. Not specifying --name or --type leaves them free. "
69 "Calling this program without --name or --type specified prints out all "
70 "the logged data.");
71 aos::InitGoogle(&argc, &argv);
72
Austin Schuh6f3babe2020-01-26 20:34:50 -080073 if (FLAGS_raw) {
74 if (argc != 2) {
75 LOG(FATAL) << "Expected 1 logfile as an argument.";
James Kuszmaul38735e82019-12-07 16:42:06 -080076 }
Austin Schuh6f3babe2020-01-26 20:34:50 -080077 aos::logger::MessageReader reader(argv[1]);
78
79 while (true) {
80 std::optional<aos::FlatbufferVector<aos::logger::MessageHeader>> message =
81 reader.ReadMessage();
82 if (!message) {
83 break;
84 }
Austin Schuha81454b2020-05-12 19:58:36 -070085 const aos::Channel *channel =
86 reader.log_file_header()->configuration()->channels()->Get(
87 message.value().message().channel_index());
Austin Schuh6f3babe2020-01-26 20:34:50 -080088
Austin Schuha81454b2020-05-12 19:58:36 -070089 if (FLAGS_format_raw && message.value().message().data() != nullptr) {
90 std::cout << aos::configuration::StrippedChannelToString(channel) << " "
91 << aos::FlatbufferToJson(message.value(), false, 4) << ": "
92 << aos::FlatbufferToJson(
93 channel->schema(),
94 message.value().message().data()->data(),
95 FLAGS_max_vector_size)
96 << std::endl;
97 } else {
98 std::cout << aos::configuration::StrippedChannelToString(channel) << " "
99 << aos::FlatbufferToJson(message.value(), false,
100 FLAGS_max_vector_size)
101 << std::endl;
102 }
Austin Schuh6f3babe2020-01-26 20:34:50 -0800103 }
104 return 0;
James Kuszmaul38735e82019-12-07 16:42:06 -0800105 }
106
Austin Schuh6f3babe2020-01-26 20:34:50 -0800107 if (argc < 2) {
108 LOG(FATAL) << "Expected at least 1 logfile as an argument.";
109 }
110
111 std::vector<std::vector<std::string>> logfiles;
112
113 for (int i = 1; i < argc; ++i) {
114 logfiles.emplace_back(std::vector<std::string>{std::string(argv[i])});
115 }
116
117 aos::logger::LogReader reader(logfiles);
Austin Schuha81454b2020-05-12 19:58:36 -0700118
119 aos::SimulatedEventLoopFactory event_loop_factory(reader.configuration());
120 reader.Register(&event_loop_factory);
Austin Schuh6f3babe2020-01-26 20:34:50 -0800121
122 std::vector<std::unique_ptr<aos::EventLoop>> printer_event_loops;
123
124 for (const aos::Node *node : reader.Nodes()) {
125 std::unique_ptr<aos::EventLoop> printer_event_loop =
Austin Schuha81454b2020-05-12 19:58:36 -0700126 event_loop_factory.MakeEventLoop("printer", node);
Austin Schuh6f3babe2020-01-26 20:34:50 -0800127 printer_event_loop->SkipTimingReport();
128 printer_event_loop->SkipAosLog();
129
Austin Schuha81454b2020-05-12 19:58:36 -0700130 std::vector<std::tuple<aos::monotonic_clock::time_point, std::string,
131 std::unique_ptr<aos::RawFetcher>>>
132 messages;
133
Austin Schuh6f3babe2020-01-26 20:34:50 -0800134 bool found_channel = false;
135 const flatbuffers::Vector<flatbuffers::Offset<aos::Channel>> *channels =
136 reader.configuration()->channels();
137 for (flatbuffers::uoffset_t i = 0; i < channels->size(); i++) {
138 const aos::Channel *channel = channels->Get(i);
139 const flatbuffers::string_view name = channel->name()->string_view();
140 const flatbuffers::string_view type = channel->type()->string_view();
141 if (name.find(FLAGS_name) != std::string::npos &&
142 type.find(FLAGS_type) != std::string::npos) {
143 if (!aos::configuration::ChannelIsReadableOnNode(
144 channel, printer_event_loop->node())) {
145 continue;
146 }
147 VLOG(1) << "Listening on " << name << " " << type;
148
149 std::string node_name =
150 node == nullptr ? ""
151 : std::string(node->name()->string_view()) + " ";
152
153 CHECK_NOTNULL(channel->schema());
Austin Schuha81454b2020-05-12 19:58:36 -0700154
155 if (FLAGS_fetch) {
156 // Grab the last message on each channel.
157 std::unique_ptr<aos::RawFetcher> fetcher =
158 printer_event_loop->MakeRawFetcher(channel);
159 if (fetcher->Fetch()) {
160 auto message =
161 std::make_tuple(fetcher->context().monotonic_event_time,
162 node_name, std::move(fetcher));
163
164 // Insert it sorted into the vector so we can print in time order
165 // instead of channel order at the start.
166 auto it = std::lower_bound(
167 messages.begin(), messages.end(), message,
168 [](const std::tuple<aos::monotonic_clock::time_point,
169 std::string,
170 std::unique_ptr<aos::RawFetcher>> &a,
171 const std::tuple<aos::monotonic_clock::time_point,
172 std::string,
173 std::unique_ptr<aos::RawFetcher>> &b) {
174 if (std::get<0>(a) < std::get<0>(b)) {
175 return true;
176 }
177 if (std::get<0>(a) > std::get<0>(b)) {
178 return false;
179 }
180
181 return std::get<2>(a)->channel() < std::get<2>(b)->channel();
182 });
183 messages.insert(it, std::move(message));
184 }
185 }
186
Austin Schuh6f3babe2020-01-26 20:34:50 -0800187 printer_event_loop->MakeRawWatcher(
188 channel, [channel, node_name](const aos::Context &context,
Austin Schuha81454b2020-05-12 19:58:36 -0700189 const void * /*message*/) {
190 LogContext(channel, node_name, context);
Austin Schuh6f3babe2020-01-26 20:34:50 -0800191 });
192 found_channel = true;
193 }
194 }
195
196 if (!found_channel) {
197 LOG(FATAL) << "Could not find any channels";
198 }
Austin Schuha81454b2020-05-12 19:58:36 -0700199 // TODO(austin): Sort between nodes too when it becomes annoying enough.
200 for (const std::tuple<aos::monotonic_clock::time_point, std::string,
201 std::unique_ptr<aos::RawFetcher>> &message :
202 messages) {
203 LogContext(std::get<2>(message)->channel(), std::get<1>(message),
204 std::get<2>(message)->context());
205 }
Austin Schuh6f3babe2020-01-26 20:34:50 -0800206 printer_event_loops.emplace_back(std::move(printer_event_loop));
James Kuszmaul38735e82019-12-07 16:42:06 -0800207 }
208
Austin Schuha81454b2020-05-12 19:58:36 -0700209 if (FLAGS_fetch) {
210 // New line to separate fetched messages from non-fetched messages.
211 std::cout << std::endl;
212 }
213
214 event_loop_factory.Run();
James Kuszmaul38735e82019-12-07 16:42:06 -0800215
216 aos::Cleanup();
217 return 0;
218}