blob: 0bec52c49555ba9973556bb8ddb44267f9531a6e [file] [log] [blame]
Brian Silvermanb0893882014-02-10 14:48:30 -08001#include "aos/common/logging/logging_impl.h"
2
Brian Silvermanb0893882014-02-10 14:48:30 -08003#include <stdarg.h>
4#include <string.h>
5
Brian Silverman88471dc2014-02-15 22:35:42 -08006#include <type_traits>
7
Brian Silvermanb0893882014-02-10 14:48:30 -08008#include "aos/common/die.h"
9
10// This file only contains the code necessary to link (ie no implementations).
11// See logging_impl.h for why this is necessary.
12
13namespace aos {
14namespace logging {
15namespace internal {
16
Brian Silverman6da04272014-05-18 18:47:48 -070017::std::atomic<LogImplementation *> global_top_implementation(NULL);
Brian Silvermanb0893882014-02-10 14:48:30 -080018
19Context::Context()
Brian Silverman6da04272014-05-18 18:47:48 -070020 : implementation(global_top_implementation.load()),
Brian Silvermanb0893882014-02-10 14:48:30 -080021 sequence(0) {
22 cork_data.Reset();
23}
24
Brian Silverman88471dc2014-02-15 22:35:42 -080025size_t ExecuteFormat(char *output, size_t output_size, const char *format,
26 va_list ap) {
27 static const char *const continued = "...\n";
Brian Silvermanb0893882014-02-10 14:48:30 -080028 const size_t size = output_size - strlen(continued);
29 const int ret = vsnprintf(output, size, format, ap);
Brian Silverman88471dc2014-02-15 22:35:42 -080030 typedef ::std::common_type<typeof(ret), typeof(size)>::type RetType;
Brian Silvermanb0893882014-02-10 14:48:30 -080031 if (ret < 0) {
Brian Silverman01be0002014-05-10 15:44:38 -070032 PLOG(FATAL, "vsnprintf(%p, %zd, %s, args) failed",
33 output, size, format);
Brian Silverman88471dc2014-02-15 22:35:42 -080034 } else if (static_cast<RetType>(ret) >= static_cast<RetType>(size)) {
Brian Silvermanb0893882014-02-10 14:48:30 -080035 // Overwrite the '\0' at the end of the existing data and
36 // copy in the one on the end of continued.
37 memcpy(&output[size - 1], continued, strlen(continued) + 1);
38 }
Brian Silverman88471dc2014-02-15 22:35:42 -080039 return ::std::min<RetType>(ret, size);
Brian Silvermanb0893882014-02-10 14:48:30 -080040}
41
Brian Silvermand6974f42014-02-14 13:39:21 -080042void RunWithCurrentImplementation(
43 int levels, ::std::function<void(LogImplementation *)> function) {
Brian Silvermanb0893882014-02-10 14:48:30 -080044 Context *context = Context::Get();
45
Brian Silvermand6974f42014-02-14 13:39:21 -080046 LogImplementation *const top_implementation = context->implementation;
Brian Silvermanb0893882014-02-10 14:48:30 -080047 LogImplementation *new_implementation = top_implementation;
48 LogImplementation *implementation = NULL;
Brian Silvermanb0893882014-02-10 14:48:30 -080049 for (int i = 0; i < levels; ++i) {
50 implementation = new_implementation;
51 if (new_implementation == NULL) {
52 Die("no logging implementation to use\n");
53 }
54 new_implementation = new_implementation->next();
55 }
56 context->implementation = new_implementation;
Brian Silvermand6974f42014-02-14 13:39:21 -080057 function(implementation);
Brian Silvermanb0893882014-02-10 14:48:30 -080058 context->implementation = top_implementation;
Brian Silvermand6974f42014-02-14 13:39:21 -080059}
Brian Silvermanb0893882014-02-10 14:48:30 -080060
Brian Silvermand6974f42014-02-14 13:39:21 -080061} // namespace internal
62
63using internal::Context;
64
Brian Silvermand6974f42014-02-14 13:39:21 -080065void LogImplementation::DoVLog(log_level level, const char *format, va_list ap,
66 int levels) {
67 internal::RunWithCurrentImplementation(
68 levels, [&](LogImplementation * implementation) {
Brian Silvermane6335e42014-02-20 20:53:06 -080069 va_list ap1;
70 va_copy(ap1, ap);
71 implementation->DoLog(level, format, ap1);
72 va_end(ap1);
Brian Silvermand6974f42014-02-14 13:39:21 -080073
74 if (level == FATAL) {
75 VDie(format, ap);
76 }
77 });
Brian Silvermanb0893882014-02-10 14:48:30 -080078}
79
80void VLog(log_level level, const char *format, va_list ap) {
81 LogImplementation::DoVLog(level, format, ap, 1);
82}
83
84void VCork(int line, const char *function, const char *format, va_list ap) {
85 Context *context = Context::Get();
86
87 const size_t message_length = strlen(context->cork_data.message);
88 if (line > context->cork_data.line_max) context->cork_data.line_max = line;
89 if (line < context->cork_data.line_min) context->cork_data.line_min = line;
90
91 if (context->cork_data.function == NULL) {
92 context->cork_data.function = function;
93 } else {
94 if (strcmp(context->cork_data.function, function) != 0) {
95 LOG(FATAL, "started corking data in function %s but then moved to %s\n",
96 context->cork_data.function, function);
97 }
98 }
99
100 internal::ExecuteFormat(context->cork_data.message + message_length,
101 sizeof(context->cork_data.message) - message_length,
102 format, ap);
103}
104
105void VUnCork(int line, const char *function, log_level level, const char *file,
106 const char *format, va_list ap) {
107 Context *context = Context::Get();
108
109 VCork(line, function, format, ap);
110
111 log_do(level, "%s: %d-%d: %s: %s", file,
112 context->cork_data.line_min, context->cork_data.line_max, function,
113 context->cork_data.message);
114
115 context->cork_data.Reset();
116}
117
118} // namespace logging
119} // namespace aos
120
121void log_do(log_level level, const char *format, ...) {
122 va_list ap;
123 va_start(ap, format);
124 aos::logging::VLog(level, format, ap);
125 va_end(ap);
126}
127
128void log_cork(int line, const char *function, const char *format, ...) {
129 va_list ap;
130 va_start(ap, format);
131 aos::logging::VCork(line, function, format, ap);
132 va_end(ap);
133}
134
135void log_uncork(int line, const char *function, log_level level,
136 const char *file, const char *format, ...) {
137 va_list ap;
138 va_start(ap, format);
139 aos::logging::VUnCork(line, function, level, file, format, ap);
140 va_end(ap);
141}