blob: cfc14c0a66797e4c9158da5fa2cb5ffd25a8e737 [file] [log] [blame]
John Park33858a32018-09-28 23:05:48 -07001#include "aos/logging/implementations.h"
Brian Silvermanf665d692013-02-17 22:11:39 -08002
Brian4a424a22014-04-02 11:52:45 -07003#include <inttypes.h>
Tyler Chatow5febd8f2020-01-05 18:25:31 -08004#include <stdarg.h>
Austin Schuh044e18b2015-10-21 20:17:09 -07005
Brian Silvermancb5da1f2015-12-05 22:19:58 -05006#include <algorithm>
Austin Schuhf2a50ba2016-12-24 16:16:26 -08007#include <chrono>
Brian Silvermancb5da1f2015-12-05 22:19:58 -05008
Tyler Chatow5febd8f2020-01-05 18:25:31 -08009#include "absl/base/call_once.h"
John Park33858a32018-09-28 23:05:48 -070010#include "aos/die.h"
Tyler Chatow5febd8f2020-01-05 18:25:31 -080011#include "aos/ipc_lib/queue.h"
John Park33858a32018-09-28 23:05:48 -070012#include "aos/logging/printf_formats.h"
John Park33858a32018-09-28 23:05:48 -070013#include "aos/time/time.h"
Brian Silvermanf665d692013-02-17 22:11:39 -080014
15namespace aos {
16namespace logging {
17namespace {
18
Austin Schuhf2a50ba2016-12-24 16:16:26 -080019namespace chrono = ::std::chrono;
20
Brian Silvermanf665d692013-02-17 22:11:39 -080021// The root LogImplementation. It only logs to stderr/stdout.
22// Some of the things specified in the LogImplementation documentation doesn't
Austin Schuhf257f3c2019-10-27 21:00:43 -070023// apply here (mostly the parts about being able to use AOS_LOG) because this is
24// the root one.
Alex Perrycb7da4b2019-08-28 19:35:56 -070025class RootLogImplementation : public LogImplementation {
Alex Perrycb7da4b2019-08-28 19:35:56 -070026 protected:
27 virtual ::aos::monotonic_clock::time_point monotonic_now() const {
28 return ::aos::monotonic_clock::now();
29 }
30
Brian Silverman2e799732014-06-05 21:50:19 -070031 private:
Tyler Chatow5febd8f2020-01-05 18:25:31 -080032 __attribute__((format(GOOD_PRINTF_FORMAT_TYPE, 3, 0))) void DoLog(
33 log_level level, const char *format, va_list ap) override {
Brian Silvermanf665d692013-02-17 22:11:39 -080034 LogMessage message;
Austin Schuh82c0c822019-05-27 19:55:20 -070035 internal::FillInMessage(level, monotonic_now(), format, ap, &message);
Brian Silvermanf665d692013-02-17 22:11:39 -080036 internal::PrintMessage(stderr, message);
Brian Silvermanf665d692013-02-17 22:11:39 -080037 }
38};
39
Brian Silverman2e799732014-06-05 21:50:19 -070040RootLogImplementation *root_implementation = nullptr;
41
Brian Silvermanf665d692013-02-17 22:11:39 -080042void SetGlobalImplementation(LogImplementation *implementation) {
Brian Silverman5c201e22014-06-12 22:40:28 -070043 if (root_implementation == nullptr) {
44 fputs("Somebody didn't call logging::Init()!\n", stderr);
45 abort();
46 }
47
Brian Silvermancb5da1f2015-12-05 22:19:58 -050048 internal::Context *context = internal::Context::Get();
Brian Silvermanf665d692013-02-17 22:11:39 -080049
50 context->implementation = implementation;
Brian Silvermancb5da1f2015-12-05 22:19:58 -050051 internal::global_top_implementation.store(implementation);
Brian Silvermanf665d692013-02-17 22:11:39 -080052}
53
Tyler Chatow5febd8f2020-01-05 18:25:31 -080054void NewContext() { internal::Context::Delete(); }
Brian Silvermane5d65692013-02-28 15:15:03 -080055
John Park06da0452019-12-02 22:19:03 -080056void DoInit() {
Brian Silverman2e799732014-06-05 21:50:19 -070057 SetGlobalImplementation(root_implementation = new RootLogImplementation());
Brian Silvermane5d65692013-02-28 15:15:03 -080058
Tyler Chatow5febd8f2020-01-05 18:25:31 -080059 if (pthread_atfork(NULL /*prepare*/, NULL /*parent*/, NewContext /*child*/) !=
60 0) {
Austin Schuhf257f3c2019-10-27 21:00:43 -070061 AOS_LOG(FATAL, "pthread_atfork(NULL, NULL, %p) failed\n", NewContext);
Brian Silvermane5d65692013-02-28 15:15:03 -080062 }
Brian Silvermanf665d692013-02-17 22:11:39 -080063}
64
65} // namespace
66namespace internal {
Brian Silverman88471dc2014-02-15 22:35:42 -080067namespace {
Brian Silvermanf665d692013-02-17 22:11:39 -080068
Austin Schuh82c0c822019-05-27 19:55:20 -070069void FillInMessageBase(log_level level,
70 monotonic_clock::time_point monotonic_now,
71 LogMessage *message) {
Brian Silvermanf665d692013-02-17 22:11:39 -080072 Context *context = Context::Get();
73
Brian Silvermanf665d692013-02-17 22:11:39 -080074 message->level = level;
75 message->source = context->source;
Austin Schuhaebbc342015-01-25 02:25:13 -080076 memcpy(message->name, context->name, context->name_size);
77 message->name_length = context->name_size;
Brian Silvermanf665d692013-02-17 22:11:39 -080078
Austin Schuhf2a50ba2016-12-24 16:16:26 -080079 message->seconds =
80 chrono::duration_cast<chrono::seconds>(monotonic_now.time_since_epoch())
81 .count();
82 message->nseconds =
83 chrono::duration_cast<chrono::nanoseconds>(
84 monotonic_now.time_since_epoch() - chrono::seconds(message->seconds))
85 .count();
Brian Silvermanf665d692013-02-17 22:11:39 -080086
87 message->sequence = context->sequence++;
88}
89
Brian Silverman88471dc2014-02-15 22:35:42 -080090} // namespace
91
Austin Schuh82c0c822019-05-27 19:55:20 -070092void FillInMessage(log_level level, monotonic_clock::time_point monotonic_now,
93 const char *format, va_list ap, LogMessage *message) {
94 FillInMessageBase(level, monotonic_now, message);
Brian Silverman88471dc2014-02-15 22:35:42 -080095
96 message->message_length =
97 ExecuteFormat(message->message, sizeof(message->message), format, ap);
98 message->type = LogMessage::Type::kString;
99}
100
Brian Silvermanf665d692013-02-17 22:11:39 -0800101void PrintMessage(FILE *output, const LogMessage &message) {
Brian Silvermana7234c62014-03-24 20:23:25 -0700102#define BASE_ARGS \
103 AOS_LOGGING_BASE_ARGS( \
104 message.name_length, message.name, static_cast<int32_t>(message.source), \
105 message.sequence, message.level, message.seconds, message.nseconds)
106 switch (message.type) {
Brian Silverman88471dc2014-02-15 22:35:42 -0800107 case LogMessage::Type::kString:
Brian Silvermana7234c62014-03-24 20:23:25 -0700108 fprintf(output, AOS_LOGGING_BASE_FORMAT "%.*s", BASE_ARGS,
Brian Silverman88471dc2014-02-15 22:35:42 -0800109 static_cast<int>(message.message_length), message.message);
110 break;
Brian Silverman88471dc2014-02-15 22:35:42 -0800111 }
Brian Silvermanc1a244e2014-02-20 14:12:39 -0800112#undef BASE_ARGS
Brian Silvermanf665d692013-02-17 22:11:39 -0800113}
114
115} // namespace internal
116
Brian Silvermanbe858a12014-04-30 17:37:28 -0700117void HandleMessageLogImplementation::DoLog(log_level level, const char *format,
118 va_list ap) {
119 LogMessage message;
Austin Schuh82c0c822019-05-27 19:55:20 -0700120 internal::FillInMessage(level, monotonic_now(), format, ap, &message);
Brian Silvermanbe858a12014-04-30 17:37:28 -0700121 HandleMessage(message);
122}
123
Brian Silverman1e8ddfe2013-12-19 16:20:53 -0800124StreamLogImplementation::StreamLogImplementation(FILE *stream)
125 : stream_(stream) {}
126
Brian Silvermanbe858a12014-04-30 17:37:28 -0700127void StreamLogImplementation::HandleMessage(const LogMessage &message) {
Brian Silverman1e8ddfe2013-12-19 16:20:53 -0800128 internal::PrintMessage(stream_, message);
129}
130
Tyler Chatow4b471e12020-01-05 20:19:36 -0800131void SetImplementation(LogImplementation *implementation, bool update_global) {
Brian Silvermancb5da1f2015-12-05 22:19:58 -0500132 internal::Context *context = internal::Context::Get();
Brian Silvermanf665d692013-02-17 22:11:39 -0800133
Tyler Chatow4b471e12020-01-05 20:19:36 -0800134 if (implementation == nullptr) {
135 AOS_LOG(FATAL, "SetImplementation got invalid implementation");
Brian Silvermanf665d692013-02-17 22:11:39 -0800136 }
137
Tyler Chatow4b471e12020-01-05 20:19:36 -0800138 context->implementation = implementation;
139 if (update_global) {
140 SetGlobalImplementation(implementation);
Brian Silvermanf665d692013-02-17 22:11:39 -0800141 }
Tyler Chatow4b471e12020-01-05 20:19:36 -0800142}
143
144LogImplementation *SwapImplementation(LogImplementation *implementation) {
145 internal::Context *context = internal::Context::Get();
146
147 LogImplementation *old = context->implementation;
148 context->implementation = implementation;
149
150 return old;
Brian Silvermanf665d692013-02-17 22:11:39 -0800151}
152
153void Init() {
John Park06da0452019-12-02 22:19:03 -0800154 static absl::once_flag once;
155 absl::call_once(once, DoInit);
Brian Silvermanf665d692013-02-17 22:11:39 -0800156}
157
Tyler Chatow5febd8f2020-01-05 18:25:31 -0800158void Load() { internal::Context::Get(); }
Brian Silvermanf665d692013-02-17 22:11:39 -0800159
Tyler Chatow5febd8f2020-01-05 18:25:31 -0800160void Cleanup() { internal::Context::Delete(); }
Brian Silvermancb5da1f2015-12-05 22:19:58 -0500161
162namespace {
163
164RawQueue *queue = NULL;
165
166int dropped_messages = 0;
Austin Schuhf2a50ba2016-12-24 16:16:26 -0800167monotonic_clock::time_point dropped_start, backoff_start;
Brian Silvermancb5da1f2015-12-05 22:19:58 -0500168// Wait this long after dropping a message before even trying to write any more.
Austin Schuhf2a50ba2016-12-24 16:16:26 -0800169constexpr chrono::milliseconds kDropBackoff = chrono::milliseconds(100);
Brian Silvermancb5da1f2015-12-05 22:19:58 -0500170
171LogMessage *GetMessageOrDie() {
172 LogMessage *message = static_cast<LogMessage *>(queue->GetMessage());
173 if (message == NULL) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700174 AOS_LOG(FATAL, "%p->GetMessage() failed\n", queue);
Brian Silvermancb5da1f2015-12-05 22:19:58 -0500175 } else {
176 return message;
177 }
178}
179
180void Write(LogMessage *msg) {
181 if (__builtin_expect(dropped_messages > 0, false)) {
Austin Schuhf2a50ba2016-12-24 16:16:26 -0800182 monotonic_clock::time_point message_time(
183 chrono::seconds(msg->seconds) + chrono::nanoseconds(msg->nseconds));
Brian Silvermancb5da1f2015-12-05 22:19:58 -0500184 if (message_time - backoff_start < kDropBackoff) {
185 ++dropped_messages;
186 queue->FreeMessage(msg);
187 return;
188 }
189
190 LogMessage *dropped_message = GetMessageOrDie();
Austin Schuhf2a50ba2016-12-24 16:16:26 -0800191 chrono::seconds dropped_start_sec = chrono::duration_cast<chrono::seconds>(
192 dropped_start.time_since_epoch());
193 chrono::nanoseconds dropped_start_nsec =
194 chrono::duration_cast<chrono::nanoseconds>(
195 dropped_start.time_since_epoch() - dropped_start_sec);
Brian Silvermancb5da1f2015-12-05 22:19:58 -0500196 internal::FillInMessageVarargs(
Austin Schuh82c0c822019-05-27 19:55:20 -0700197 ERROR, message_time, dropped_message,
Brian Silvermancb5da1f2015-12-05 22:19:58 -0500198 "%d logs starting at %" PRId32 ".%" PRId32 " dropped\n",
Austin Schuhf2a50ba2016-12-24 16:16:26 -0800199 dropped_messages, static_cast<int32_t>(dropped_start_sec.count()),
200 static_cast<int32_t>(dropped_start_nsec.count()));
Brian Silvermancb5da1f2015-12-05 22:19:58 -0500201 if (queue->WriteMessage(dropped_message, RawQueue::kNonBlock)) {
202 dropped_messages = 0;
203 } else {
204 // Don't even bother trying to write this message because it's not likely
205 // to work and it would be confusing to have one log in the middle of a
206 // string of failures get through.
207 ++dropped_messages;
208 backoff_start = message_time;
209 queue->FreeMessage(msg);
210 return;
211 }
212 }
213 if (!queue->WriteMessage(msg, RawQueue::kNonBlock)) {
214 if (dropped_messages == 0) {
Austin Schuhf2a50ba2016-12-24 16:16:26 -0800215 monotonic_clock::time_point message_time(
216 chrono::seconds(msg->seconds) + chrono::nanoseconds(msg->nseconds));
217 dropped_start = backoff_start = message_time;
Brian Silvermancb5da1f2015-12-05 22:19:58 -0500218 }
219 ++dropped_messages;
220 }
221}
222
223class LinuxQueueLogImplementation : public LogImplementation {
Austin Schuh82c0c822019-05-27 19:55:20 -0700224 virtual ::aos::monotonic_clock::time_point monotonic_now() const {
225 return ::aos::monotonic_clock::now();
226 }
227
Tyler Chatow5febd8f2020-01-05 18:25:31 -0800228 __attribute__((format(GOOD_PRINTF_FORMAT_TYPE, 3, 0))) void DoLog(
229 log_level level, const char *format, va_list ap) override {
Brian Silvermancb5da1f2015-12-05 22:19:58 -0500230 LogMessage *message = GetMessageOrDie();
Austin Schuh82c0c822019-05-27 19:55:20 -0700231 internal::FillInMessage(level, monotonic_now(), format, ap, message);
Brian Silvermancb5da1f2015-12-05 22:19:58 -0500232 Write(message);
233 }
Brian Silvermancb5da1f2015-12-05 22:19:58 -0500234};
235
Tyler Chatow5febd8f2020-01-05 18:25:31 -0800236class CallbackLogImplementation : public HandleMessageLogImplementation {
237 public:
238 CallbackLogImplementation(
239 const ::std::function<void(const LogMessage &)> &callback)
240 : callback_(callback) {}
241
242 private:
243 void HandleMessage(const LogMessage &message) override { callback_(message); }
244
245 ::std::function<void(const LogMessage &)> callback_;
246};
247
Brian Silvermancb5da1f2015-12-05 22:19:58 -0500248} // namespace
249
250RawQueue *GetLoggingQueue() {
Austin Schuh3e252e72019-12-29 16:25:58 -0800251 return RawQueue::Fetch("LoggingQueue", sizeof(LogMessage), 1323, 10000);
Brian Silvermancb5da1f2015-12-05 22:19:58 -0500252}
253
254void RegisterQueueImplementation() {
255 Init();
256
257 queue = GetLoggingQueue();
258 if (queue == NULL) {
259 Die("logging: couldn't fetch queue\n");
260 }
261
Tyler Chatow4b471e12020-01-05 20:19:36 -0800262 SetImplementation(new LinuxQueueLogImplementation());
Brian Silvermanf665d692013-02-17 22:11:39 -0800263}
264
Tyler Chatow5febd8f2020-01-05 18:25:31 -0800265void RegisterCallbackImplementation(
Tyler Chatow4b471e12020-01-05 20:19:36 -0800266 const ::std::function<void(const LogMessage &)> &callback,
267 bool update_global = true) {
Tyler Chatow5febd8f2020-01-05 18:25:31 -0800268 Init();
Tyler Chatow4b471e12020-01-05 20:19:36 -0800269 SetImplementation(new CallbackLogImplementation(callback), update_global);
Tyler Chatow5febd8f2020-01-05 18:25:31 -0800270}
271
Brian Silvermanf665d692013-02-17 22:11:39 -0800272} // namespace logging
273} // namespace aos