blob: 66e64d860c65a47d07dc0ba8523e1efa2d9e08c2 [file] [log] [blame]
Brian Silvermanf665d692013-02-17 22:11:39 -08001#include "aos/common/logging/logging_impl.h"
2
3#include <assert.h>
4
5#include "aos/common/die.h"
6#include "aos/common/time.h"
7#include "aos/common/inttypes.h"
8#include "aos/common/once.h"
9
10namespace aos {
11namespace logging {
12namespace {
13
14using internal::Context;
15
16LogImplementation *global_top_implementation(NULL);
17// Just going to use a mutex instead of getting fancy because speed doesn't
18// really matter when accessing global_top_implementation.
19Mutex global_top_implementation_mutex;
20LogImplementation *get_global_top_implementation() {
21 MutexLocker locker(&global_top_implementation_mutex);
22 return global_top_implementation;
23}
24
25// The root LogImplementation. It only logs to stderr/stdout.
26// Some of the things specified in the LogImplementation documentation doesn't
27// apply here (mostly the parts about being able to use LOG) because this is the
28// root one.
29class RootLogImplementation : public LogImplementation {
30 virtual void set_next(LogImplementation *) {
31 LOG(FATAL, "can't have a next logger from here\n");
32 }
33
34 virtual void DoLog(log_level level, const char *format, va_list ap) {
35 LogMessage message;
36 internal::FillInMessage(level, format, ap, &message);
37 internal::PrintMessage(stderr, message);
38 fputs("root logger got used, see stderr for message\n", stdout);
39 }
40};
41
42void SetGlobalImplementation(LogImplementation *implementation) {
43 Context *context = Context::Get();
44
45 context->implementation = implementation;
46 {
47 MutexLocker locker(&global_top_implementation_mutex);
48 global_top_implementation = implementation;
49 }
50}
51
52// Prints format (with ap) into output and correctly deals with the result
53// being too long etc.
54void ExecuteFormat(char *output, size_t output_size,
55 const char *format, va_list ap) {
56 static const char *continued = "...\n";
57 const size_t size = output_size - strlen(continued);
58 const int ret = vsnprintf(output, size, format, ap);
59 if (ret < 0) {
60 LOG(FATAL, "vsnprintf(%p, %zd, %s, %p) failed with %d (%s)\n",
61 output, size, format, ap, errno, strerror(errno));
62 } else if (static_cast<uintmax_t>(ret) >= static_cast<uintmax_t>(size)) {
63 // Overwrite the '\0' at the end of the existing data and
64 // copy in the one on the end of continued.
65 memcpy(&output[size - 1], continued, strlen(continued) + 1);
66 }
67}
68
69void *DoInit() {
70 SetGlobalImplementation(new RootLogImplementation());
71 return NULL;
72}
73
74} // namespace
75namespace internal {
76
77Context::Context()
78 : implementation(get_global_top_implementation()),
79 sequence(0) {
80 cork_data.Reset();
81}
82
83void FillInMessage(log_level level, const char *format, va_list ap,
84 LogMessage *message) {
85 Context *context = Context::Get();
86
87 ExecuteFormat(message->message, sizeof(message->message), format, ap);
88
89 message->level = level;
90 message->source = context->source;
91 memcpy(message->name, context->name, context->name_size);
92
93 time::Time now = time::Time::Now();
94 message->seconds = now.sec();
95 message->nseconds = now.nsec();
96
97 message->sequence = context->sequence++;
98}
99
100void PrintMessage(FILE *output, const LogMessage &message) {
101 fprintf(output, "%s(%"PRId32")(%05"PRIu16"): %s at"
102 " %010"PRId32".%09"PRId32"s: %s",
103 message.name, message.source, message.sequence,
104 log_str(message.level), message.seconds, message.nseconds,
105 message.message);
106}
107
108} // namespace internal
109
110void LogImplementation::DoVLog(log_level level, const char *format, va_list ap,
111 int levels) {
112 Context *context = Context::Get();
113
114 LogImplementation *top_implementation = context->implementation;
115 LogImplementation *new_implementation = top_implementation;
116 LogImplementation *implementation = NULL;
117 assert(levels >= 1);
118 for (int i = 0; i < levels; ++i) {
119 implementation = new_implementation;
120 if (new_implementation == NULL) {
121 Die("no logging implementation to use\n");
122 }
123 new_implementation = new_implementation->next();
124 }
125 context->implementation = new_implementation;
126 implementation->DoLog(level, format, ap);
127 context->implementation = top_implementation;
128
129 if (level == FATAL) {
130 VDie(format, ap);
131 }
132}
133
134void VLog(log_level level, const char *format, va_list ap) {
135 LogImplementation::DoVLog(level, format, ap, 1);
136}
137
138void VCork(int line, const char *function, const char *format, va_list ap) {
139 Context *context = Context::Get();
140
141 const size_t message_length = strlen(context->cork_data.message);
142 if (line > context->cork_data.line_max) context->cork_data.line_max = line;
143 if (line < context->cork_data.line_min) context->cork_data.line_min = line;
144
145 if (context->cork_data.function == NULL) {
146 context->cork_data.function = function;
147 } else {
148 if (strcmp(context->cork_data.function, function) != 0) {
149 LOG(FATAL, "started corking data in function %s but then moved to %s\n",
150 context->cork_data.function, function);
151 }
152 }
153
154 ExecuteFormat(context->cork_data.message + message_length,
155 sizeof(context->cork_data.message) - message_length,
156 format, ap);
157}
158
159void VUnCork(int line, const char *function, log_level level, const char *file,
160 const char *format, va_list ap) {
161 Context *context = Context::Get();
162
163 VCork(line, function, format, ap);
164
165 log_do(level, "%s: %d-%d: %s: %s", file,
166 context->cork_data.line_min, context->cork_data.line_max, function,
167 context->cork_data.message);
168
169 context->cork_data.Reset();
170}
171
172void LogNext(log_level level, const char *format, ...) {
173 va_list ap;
174 va_start(ap, format);
175 LogImplementation::DoVLog(level, format, ap, 2);
176 va_end(ap);
177}
178
179void AddImplementation(LogImplementation *implementation) {
180 Context *context = Context::Get();
181
182 if (implementation->next() != NULL) {
183 LOG(FATAL, "%p already has a next implementation, but it's not"
184 " being used yet\n", implementation);
185 }
186
187 LogImplementation *old = context->implementation;
188 if (old != NULL) {
189 implementation->set_next(old);
190 }
191 SetGlobalImplementation(implementation);
192}
193
194void Init() {
195 static Once<void> once(DoInit);
196 once.Get();
197}
198
199void Load() {
200 Context::Get();
201}
202
203void Cleanup() {
204 Context::Delete();
205}
206
207} // namespace logging
208} // namespace aos
209
210void log_do(log_level level, const char *format, ...) {
211 va_list ap;
212 va_start(ap, format);
213 aos::logging::VLog(level, format, ap);
214 va_end(ap);
215}
216
217void log_cork(int line, const char *function, const char *format, ...) {
218 va_list ap;
219 va_start(ap, format);
220 aos::logging::VCork(line, function, format, ap);
221 va_end(ap);
222}
223
224void log_uncork(int line, const char *function, log_level level,
225 const char *file, const char *format, ...) {
226 va_list ap;
227 va_start(ap, format);
228 aos::logging::VUnCork(line, function, level, file, format, ap);
229 va_end(ap);
230}