blob: ae8cc01d00f12bfb5b6fa775e0ceb76956467e60 [file] [log] [blame]
Brian Silvermanb0893882014-02-10 14:48:30 -08001#include "aos/common/logging/logging_impl.h"
2
3#include <assert.h>
4#include <stdarg.h>
5#include <string.h>
6
Brian Silverman88471dc2014-02-15 22:35:42 -08007#include <type_traits>
8
Brian Silvermanb0893882014-02-10 14:48:30 -08009#include "aos/common/die.h"
10
11// This file only contains the code necessary to link (ie no implementations).
12// See logging_impl.h for why this is necessary.
13
14namespace aos {
15namespace logging {
16namespace internal {
17
18LogImplementation *global_top_implementation(NULL);
19
20Context::Context()
21 : implementation(global_top_implementation),
22 sequence(0) {
23 cork_data.Reset();
24}
25
Brian Silverman88471dc2014-02-15 22:35:42 -080026size_t ExecuteFormat(char *output, size_t output_size, const char *format,
27 va_list ap) {
28 static const char *const continued = "...\n";
Brian Silvermanb0893882014-02-10 14:48:30 -080029 const size_t size = output_size - strlen(continued);
30 const int ret = vsnprintf(output, size, format, ap);
Brian Silverman88471dc2014-02-15 22:35:42 -080031 typedef ::std::common_type<typeof(ret), typeof(size)>::type RetType;
Brian Silvermanb0893882014-02-10 14:48:30 -080032 if (ret < 0) {
33 LOG(FATAL, "vsnprintf(%p, %zd, %s, args) failed with %d (%s)\n",
34 output, size, format, errno, strerror(errno));
Brian Silverman88471dc2014-02-15 22:35:42 -080035 } else if (static_cast<RetType>(ret) >= static_cast<RetType>(size)) {
Brian Silvermanb0893882014-02-10 14:48:30 -080036 // Overwrite the '\0' at the end of the existing data and
37 // copy in the one on the end of continued.
38 memcpy(&output[size - 1], continued, strlen(continued) + 1);
39 }
Brian Silverman88471dc2014-02-15 22:35:42 -080040 return ::std::min<RetType>(ret, size);
Brian Silvermanb0893882014-02-10 14:48:30 -080041}
42
Brian Silvermand6974f42014-02-14 13:39:21 -080043void RunWithCurrentImplementation(
44 int levels, ::std::function<void(LogImplementation *)> function) {
Brian Silvermanb0893882014-02-10 14:48:30 -080045 Context *context = Context::Get();
46
Brian Silvermand6974f42014-02-14 13:39:21 -080047 LogImplementation *const top_implementation = context->implementation;
Brian Silvermanb0893882014-02-10 14:48:30 -080048 LogImplementation *new_implementation = top_implementation;
49 LogImplementation *implementation = NULL;
50 assert(levels >= 1);
51 for (int i = 0; i < levels; ++i) {
52 implementation = new_implementation;
53 if (new_implementation == NULL) {
54 Die("no logging implementation to use\n");
55 }
56 new_implementation = new_implementation->next();
57 }
58 context->implementation = new_implementation;
Brian Silvermand6974f42014-02-14 13:39:21 -080059 function(implementation);
Brian Silvermanb0893882014-02-10 14:48:30 -080060 context->implementation = top_implementation;
Brian Silvermand6974f42014-02-14 13:39:21 -080061}
Brian Silvermanb0893882014-02-10 14:48:30 -080062
Brian Silvermand6974f42014-02-14 13:39:21 -080063} // namespace internal
64
65using internal::Context;
66
Brian Silvermand6974f42014-02-14 13:39:21 -080067void LogImplementation::DoVLog(log_level level, const char *format, va_list ap,
68 int levels) {
69 internal::RunWithCurrentImplementation(
70 levels, [&](LogImplementation * implementation) {
Brian Silvermane6335e42014-02-20 20:53:06 -080071 va_list ap1;
72 va_copy(ap1, ap);
73 implementation->DoLog(level, format, ap1);
74 va_end(ap1);
Brian Silvermand6974f42014-02-14 13:39:21 -080075
76 if (level == FATAL) {
77 VDie(format, ap);
78 }
79 });
Brian Silvermanb0893882014-02-10 14:48:30 -080080}
81
82void VLog(log_level level, const char *format, va_list ap) {
83 LogImplementation::DoVLog(level, format, ap, 1);
84}
85
86void VCork(int line, const char *function, const char *format, va_list ap) {
87 Context *context = Context::Get();
88
89 const size_t message_length = strlen(context->cork_data.message);
90 if (line > context->cork_data.line_max) context->cork_data.line_max = line;
91 if (line < context->cork_data.line_min) context->cork_data.line_min = line;
92
93 if (context->cork_data.function == NULL) {
94 context->cork_data.function = function;
95 } else {
96 if (strcmp(context->cork_data.function, function) != 0) {
97 LOG(FATAL, "started corking data in function %s but then moved to %s\n",
98 context->cork_data.function, function);
99 }
100 }
101
102 internal::ExecuteFormat(context->cork_data.message + message_length,
103 sizeof(context->cork_data.message) - message_length,
104 format, ap);
105}
106
107void VUnCork(int line, const char *function, log_level level, const char *file,
108 const char *format, va_list ap) {
109 Context *context = Context::Get();
110
111 VCork(line, function, format, ap);
112
113 log_do(level, "%s: %d-%d: %s: %s", file,
114 context->cork_data.line_min, context->cork_data.line_max, function,
115 context->cork_data.message);
116
117 context->cork_data.Reset();
118}
119
120} // namespace logging
121} // namespace aos
122
123void log_do(log_level level, const char *format, ...) {
124 va_list ap;
125 va_start(ap, format);
126 aos::logging::VLog(level, format, ap);
127 va_end(ap);
128}
129
130void log_cork(int line, const char *function, const char *format, ...) {
131 va_list ap;
132 va_start(ap, format);
133 aos::logging::VCork(line, function, format, ap);
134 va_end(ap);
135}
136
137void log_uncork(int line, const char *function, log_level level,
138 const char *file, const char *format, ...) {
139 va_list ap;
140 va_start(ap, format);
141 aos::logging::VUnCork(line, function, level, file, format, ap);
142 va_end(ap);
143}