blob: 64696dcf56cb807164ee22dea865c68718a33ef9 [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
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) {
114 fprintf(output, "%s(%"PRId32")(%05"PRIu16"): %s at"
115 " %010"PRId32".%09"PRId32"s: %s",
116 message.name, message.source, message.sequence,
117 log_str(message.level), message.seconds, message.nseconds,
118 message.message);
119}
120
121} // namespace internal
122
123void LogImplementation::DoVLog(log_level level, const char *format, va_list ap,
124 int levels) {
125 Context *context = Context::Get();
126
127 LogImplementation *top_implementation = context->implementation;
128 LogImplementation *new_implementation = top_implementation;
129 LogImplementation *implementation = NULL;
130 assert(levels >= 1);
131 for (int i = 0; i < levels; ++i) {
132 implementation = new_implementation;
133 if (new_implementation == NULL) {
134 Die("no logging implementation to use\n");
135 }
136 new_implementation = new_implementation->next();
137 }
138 context->implementation = new_implementation;
139 implementation->DoLog(level, format, ap);
140 context->implementation = top_implementation;
141
142 if (level == FATAL) {
143 VDie(format, ap);
144 }
145}
146
147void VLog(log_level level, const char *format, va_list ap) {
148 LogImplementation::DoVLog(level, format, ap, 1);
149}
150
151void VCork(int line, const char *function, const char *format, va_list ap) {
152 Context *context = Context::Get();
153
154 const size_t message_length = strlen(context->cork_data.message);
155 if (line > context->cork_data.line_max) context->cork_data.line_max = line;
156 if (line < context->cork_data.line_min) context->cork_data.line_min = line;
157
158 if (context->cork_data.function == NULL) {
159 context->cork_data.function = function;
160 } else {
161 if (strcmp(context->cork_data.function, function) != 0) {
162 LOG(FATAL, "started corking data in function %s but then moved to %s\n",
163 context->cork_data.function, function);
164 }
165 }
166
167 ExecuteFormat(context->cork_data.message + message_length,
168 sizeof(context->cork_data.message) - message_length,
169 format, ap);
170}
171
172void VUnCork(int line, const char *function, log_level level, const char *file,
173 const char *format, va_list ap) {
174 Context *context = Context::Get();
175
176 VCork(line, function, format, ap);
177
178 log_do(level, "%s: %d-%d: %s: %s", file,
179 context->cork_data.line_min, context->cork_data.line_max, function,
180 context->cork_data.message);
181
182 context->cork_data.Reset();
183}
184
185void LogNext(log_level level, const char *format, ...) {
186 va_list ap;
187 va_start(ap, format);
188 LogImplementation::DoVLog(level, format, ap, 2);
189 va_end(ap);
190}
191
192void AddImplementation(LogImplementation *implementation) {
193 Context *context = Context::Get();
194
195 if (implementation->next() != NULL) {
196 LOG(FATAL, "%p already has a next implementation, but it's not"
197 " being used yet\n", implementation);
198 }
199
200 LogImplementation *old = context->implementation;
201 if (old != NULL) {
202 implementation->set_next(old);
203 }
204 SetGlobalImplementation(implementation);
205}
206
207void Init() {
208 static Once<void> once(DoInit);
209 once.Get();
210}
211
212void Load() {
213 Context::Get();
214}
215
216void Cleanup() {
217 Context::Delete();
218}
219
220} // namespace logging
221} // namespace aos
222
223void log_do(log_level level, const char *format, ...) {
224 va_list ap;
225 va_start(ap, format);
226 aos::logging::VLog(level, format, ap);
227 va_end(ap);
228}
229
230void log_cork(int line, const char *function, const char *format, ...) {
231 va_list ap;
232 va_start(ap, format);
233 aos::logging::VCork(line, function, format, ap);
234 va_end(ap);
235}
236
237void log_uncork(int line, const char *function, log_level level,
238 const char *file, const char *format, ...) {
239 va_list ap;
240 va_start(ap, format);
241 aos::logging::VUnCork(line, function, level, file, format, ap);
242 va_end(ap);
243}