blob: 980f65a9ee5a23babe1cabbae4e608ae8a50d972 [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) {
Brian Silverman63cf2412013-11-17 05:44:36 -080060 LOG(FATAL, "vsnprintf(%p, %zd, %s, args) failed with %d (%s)\n",
61 output, size, format, errno, strerror(errno));
Brian Silvermanf665d692013-02-17 22:11:39 -080062 } 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
Brian Silvermane5d65692013-02-28 15:15:03 -080069void NewContext() {
70 Context::Delete();
71}
72
Brian Silvermanf665d692013-02-17 22:11:39 -080073void *DoInit() {
74 SetGlobalImplementation(new RootLogImplementation());
Brian Silvermane5d65692013-02-28 15:15:03 -080075
76#ifndef __VXWORKS__
77 if (pthread_atfork(NULL /*prepare*/, NULL /*parent*/,
78 NewContext /*child*/) != 0) {
79 LOG(FATAL, "pthread_atfork(NULL, NULL, %p) failed\n",
80 NewContext);
81 }
82#endif
83
Brian Silvermanf665d692013-02-17 22:11:39 -080084 return NULL;
85}
86
87} // namespace
88namespace internal {
89
90Context::Context()
91 : implementation(get_global_top_implementation()),
92 sequence(0) {
93 cork_data.Reset();
94}
95
96void FillInMessage(log_level level, const char *format, va_list ap,
97 LogMessage *message) {
98 Context *context = Context::Get();
99
100 ExecuteFormat(message->message, sizeof(message->message), format, ap);
101
102 message->level = level;
103 message->source = context->source;
Brian Silvermanab6615c2013-03-05 20:29:29 -0800104 memcpy(message->name, context->name.c_str(), context->name.size() + 1);
Brian Silvermanf665d692013-02-17 22:11:39 -0800105
106 time::Time now = time::Time::Now();
107 message->seconds = now.sec();
108 message->nseconds = now.nsec();
109
110 message->sequence = context->sequence++;
111}
112
113void PrintMessage(FILE *output, const LogMessage &message) {
Brian Silverman8efe23e2013-07-07 23:31:37 -0700114 fprintf(output, "%s(%" PRId32 ")(%05" PRIu16 "): %s at"
115 " %010" PRId32 ".%09" PRId32 "s: %s",
Brian Silverman7aa971b2013-03-16 14:40:09 -0700116 message.name, static_cast<int32_t>(message.source), message.sequence,
Brian Silvermanf665d692013-02-17 22:11:39 -0800117 log_str(message.level), message.seconds, message.nseconds,
118 message.message);
119}
120
121} // namespace internal
122
Brian Silverman1e8ddfe2013-12-19 16:20:53 -0800123StreamLogImplementation::StreamLogImplementation(FILE *stream)
124 : stream_(stream) {}
125
126void StreamLogImplementation::DoLog(log_level level, const char *format,
127 va_list ap) {
128 LogMessage message;
129 internal::FillInMessage(level, format, ap, &message);
130 internal::PrintMessage(stream_, message);
131}
132
Brian Silvermanf665d692013-02-17 22:11:39 -0800133void LogImplementation::DoVLog(log_level level, const char *format, va_list ap,
134 int levels) {
135 Context *context = Context::Get();
136
137 LogImplementation *top_implementation = context->implementation;
138 LogImplementation *new_implementation = top_implementation;
139 LogImplementation *implementation = NULL;
140 assert(levels >= 1);
141 for (int i = 0; i < levels; ++i) {
142 implementation = new_implementation;
143 if (new_implementation == NULL) {
144 Die("no logging implementation to use\n");
145 }
146 new_implementation = new_implementation->next();
147 }
148 context->implementation = new_implementation;
149 implementation->DoLog(level, format, ap);
150 context->implementation = top_implementation;
151
152 if (level == FATAL) {
153 VDie(format, ap);
154 }
155}
156
157void VLog(log_level level, const char *format, va_list ap) {
158 LogImplementation::DoVLog(level, format, ap, 1);
159}
160
161void VCork(int line, const char *function, const char *format, va_list ap) {
162 Context *context = Context::Get();
163
164 const size_t message_length = strlen(context->cork_data.message);
165 if (line > context->cork_data.line_max) context->cork_data.line_max = line;
166 if (line < context->cork_data.line_min) context->cork_data.line_min = line;
167
168 if (context->cork_data.function == NULL) {
169 context->cork_data.function = function;
170 } else {
171 if (strcmp(context->cork_data.function, function) != 0) {
172 LOG(FATAL, "started corking data in function %s but then moved to %s\n",
173 context->cork_data.function, function);
174 }
175 }
176
177 ExecuteFormat(context->cork_data.message + message_length,
178 sizeof(context->cork_data.message) - message_length,
179 format, ap);
180}
181
182void VUnCork(int line, const char *function, log_level level, const char *file,
183 const char *format, va_list ap) {
184 Context *context = Context::Get();
185
186 VCork(line, function, format, ap);
187
188 log_do(level, "%s: %d-%d: %s: %s", file,
189 context->cork_data.line_min, context->cork_data.line_max, function,
190 context->cork_data.message);
191
192 context->cork_data.Reset();
193}
194
195void LogNext(log_level level, const char *format, ...) {
196 va_list ap;
197 va_start(ap, format);
198 LogImplementation::DoVLog(level, format, ap, 2);
199 va_end(ap);
200}
201
202void AddImplementation(LogImplementation *implementation) {
203 Context *context = Context::Get();
204
205 if (implementation->next() != NULL) {
206 LOG(FATAL, "%p already has a next implementation, but it's not"
207 " being used yet\n", implementation);
208 }
209
210 LogImplementation *old = context->implementation;
211 if (old != NULL) {
212 implementation->set_next(old);
213 }
214 SetGlobalImplementation(implementation);
215}
216
217void Init() {
218 static Once<void> once(DoInit);
219 once.Get();
220}
221
222void Load() {
223 Context::Get();
224}
225
226void Cleanup() {
227 Context::Delete();
228}
229
230} // namespace logging
231} // namespace aos
232
233void log_do(log_level level, const char *format, ...) {
234 va_list ap;
235 va_start(ap, format);
236 aos::logging::VLog(level, format, ap);
237 va_end(ap);
238}
239
240void log_cork(int line, const char *function, const char *format, ...) {
241 va_list ap;
242 va_start(ap, format);
243 aos::logging::VCork(line, function, format, ap);
244 va_end(ap);
245}
246
247void log_uncork(int line, const char *function, log_level level,
248 const char *file, const char *format, ...) {
249 va_list ap;
250 va_start(ap, format);
251 aos::logging::VUnCork(line, function, level, file, format, ap);
252 va_end(ap);
253}