John Park | 33858a3 | 2018-09-28 23:05:48 -0700 | [diff] [blame] | 1 | #include "aos/logging/interface.h" |
Brian Silverman | b089388 | 2014-02-10 14:48:30 -0800 | [diff] [blame] | 2 | |
Brian Silverman | b089388 | 2014-02-10 14:48:30 -0800 | [diff] [blame] | 3 | #include <stdarg.h> |
Austin Schuh | 044e18b | 2015-10-21 20:17:09 -0700 | [diff] [blame] | 4 | #include <stdio.h> |
Brian Silverman | b089388 | 2014-02-10 14:48:30 -0800 | [diff] [blame] | 5 | #include <string.h> |
| 6 | |
Brian Silverman | 88471dc | 2014-02-15 22:35:42 -0800 | [diff] [blame] | 7 | #include <type_traits> |
Austin Schuh | 044e18b | 2015-10-21 20:17:09 -0700 | [diff] [blame] | 8 | #include <functional> |
Brian Silverman | 88471dc | 2014-02-15 22:35:42 -0800 | [diff] [blame] | 9 | |
John Park | 33858a3 | 2018-09-28 23:05:48 -0700 | [diff] [blame] | 10 | #include "aos/die.h" |
| 11 | #include "aos/logging/context.h" |
Brian Silverman | b089388 | 2014-02-10 14:48:30 -0800 | [diff] [blame] | 12 | |
Brian Silverman | b089388 | 2014-02-10 14:48:30 -0800 | [diff] [blame] | 13 | namespace aos { |
| 14 | namespace logging { |
| 15 | namespace internal { |
| 16 | |
Brian Silverman | 88471dc | 2014-02-15 22:35:42 -0800 | [diff] [blame] | 17 | size_t ExecuteFormat(char *output, size_t output_size, const char *format, |
| 18 | va_list ap) { |
| 19 | static const char *const continued = "...\n"; |
Brian Silverman | b089388 | 2014-02-10 14:48:30 -0800 | [diff] [blame] | 20 | const size_t size = output_size - strlen(continued); |
| 21 | const int ret = vsnprintf(output, size, format, ap); |
Alex Perry | cb7da4b | 2019-08-28 19:35:56 -0700 | [diff] [blame^] | 22 | typedef ::std::common_type<int, size_t>::type RetType; |
Brian Silverman | b089388 | 2014-02-10 14:48:30 -0800 | [diff] [blame] | 23 | if (ret < 0) { |
Austin Schuh | f257f3c | 2019-10-27 21:00:43 -0700 | [diff] [blame] | 24 | AOS_PLOG(FATAL, "vsnprintf(%p, %zd, %s, args) failed", |
Brian Silverman | 01be000 | 2014-05-10 15:44:38 -0700 | [diff] [blame] | 25 | output, size, format); |
Brian Silverman | 88471dc | 2014-02-15 22:35:42 -0800 | [diff] [blame] | 26 | } else if (static_cast<RetType>(ret) >= static_cast<RetType>(size)) { |
Brian Silverman | b089388 | 2014-02-10 14:48:30 -0800 | [diff] [blame] | 27 | // Overwrite the '\0' at the end of the existing data and |
| 28 | // copy in the one on the end of continued. |
| 29 | memcpy(&output[size - 1], continued, strlen(continued) + 1); |
| 30 | } |
Brian Silverman | 88471dc | 2014-02-15 22:35:42 -0800 | [diff] [blame] | 31 | return ::std::min<RetType>(ret, size); |
Brian Silverman | b089388 | 2014-02-10 14:48:30 -0800 | [diff] [blame] | 32 | } |
| 33 | |
Brian Silverman | d6974f4 | 2014-02-14 13:39:21 -0800 | [diff] [blame] | 34 | void RunWithCurrentImplementation( |
| 35 | int levels, ::std::function<void(LogImplementation *)> function) { |
Brian Silverman | b089388 | 2014-02-10 14:48:30 -0800 | [diff] [blame] | 36 | Context *context = Context::Get(); |
| 37 | |
Brian Silverman | d6974f4 | 2014-02-14 13:39:21 -0800 | [diff] [blame] | 38 | LogImplementation *const top_implementation = context->implementation; |
Brian Silverman | b089388 | 2014-02-10 14:48:30 -0800 | [diff] [blame] | 39 | LogImplementation *new_implementation = top_implementation; |
| 40 | LogImplementation *implementation = NULL; |
Brian Silverman | b089388 | 2014-02-10 14:48:30 -0800 | [diff] [blame] | 41 | for (int i = 0; i < levels; ++i) { |
| 42 | implementation = new_implementation; |
| 43 | if (new_implementation == NULL) { |
| 44 | Die("no logging implementation to use\n"); |
| 45 | } |
| 46 | new_implementation = new_implementation->next(); |
| 47 | } |
| 48 | context->implementation = new_implementation; |
Brian Silverman | d6974f4 | 2014-02-14 13:39:21 -0800 | [diff] [blame] | 49 | function(implementation); |
Brian Silverman | b089388 | 2014-02-10 14:48:30 -0800 | [diff] [blame] | 50 | context->implementation = top_implementation; |
Brian Silverman | d6974f4 | 2014-02-14 13:39:21 -0800 | [diff] [blame] | 51 | } |
Brian Silverman | b089388 | 2014-02-10 14:48:30 -0800 | [diff] [blame] | 52 | |
Brian Silverman | d6974f4 | 2014-02-14 13:39:21 -0800 | [diff] [blame] | 53 | } // namespace internal |
| 54 | |
| 55 | using internal::Context; |
| 56 | |
Brian Silverman | d6974f4 | 2014-02-14 13:39:21 -0800 | [diff] [blame] | 57 | void LogImplementation::DoVLog(log_level level, const char *format, va_list ap, |
| 58 | int levels) { |
Austin Schuh | 3970080 | 2016-11-26 21:23:56 -0800 | [diff] [blame] | 59 | auto log_impl = [&](LogImplementation *implementation) { |
Brian Silverman | e6335e4 | 2014-02-20 20:53:06 -0800 | [diff] [blame] | 60 | va_list ap1; |
| 61 | va_copy(ap1, ap); |
| 62 | implementation->DoLog(level, format, ap1); |
| 63 | va_end(ap1); |
Brian Silverman | d6974f4 | 2014-02-14 13:39:21 -0800 | [diff] [blame] | 64 | |
| 65 | if (level == FATAL) { |
| 66 | VDie(format, ap); |
| 67 | } |
Austin Schuh | 3970080 | 2016-11-26 21:23:56 -0800 | [diff] [blame] | 68 | }; |
| 69 | internal::RunWithCurrentImplementation(levels, ::std::ref(log_impl)); |
Brian Silverman | b089388 | 2014-02-10 14:48:30 -0800 | [diff] [blame] | 70 | } |
| 71 | |
| 72 | void VLog(log_level level, const char *format, va_list ap) { |
| 73 | LogImplementation::DoVLog(level, format, ap, 1); |
| 74 | } |
| 75 | |
| 76 | void VCork(int line, const char *function, const char *format, va_list ap) { |
| 77 | Context *context = Context::Get(); |
| 78 | |
| 79 | const size_t message_length = strlen(context->cork_data.message); |
| 80 | if (line > context->cork_data.line_max) context->cork_data.line_max = line; |
| 81 | if (line < context->cork_data.line_min) context->cork_data.line_min = line; |
| 82 | |
| 83 | if (context->cork_data.function == NULL) { |
| 84 | context->cork_data.function = function; |
| 85 | } else { |
| 86 | if (strcmp(context->cork_data.function, function) != 0) { |
Austin Schuh | f257f3c | 2019-10-27 21:00:43 -0700 | [diff] [blame] | 87 | AOS_LOG(FATAL, |
| 88 | "started corking data in function %s but then moved to %s\n", |
| 89 | context->cork_data.function, function); |
Brian Silverman | b089388 | 2014-02-10 14:48:30 -0800 | [diff] [blame] | 90 | } |
| 91 | } |
| 92 | |
| 93 | internal::ExecuteFormat(context->cork_data.message + message_length, |
| 94 | sizeof(context->cork_data.message) - message_length, |
| 95 | format, ap); |
| 96 | } |
| 97 | |
| 98 | void VUnCork(int line, const char *function, log_level level, const char *file, |
| 99 | const char *format, va_list ap) { |
| 100 | Context *context = Context::Get(); |
| 101 | |
| 102 | VCork(line, function, format, ap); |
| 103 | |
| 104 | log_do(level, "%s: %d-%d: %s: %s", file, |
| 105 | context->cork_data.line_min, context->cork_data.line_max, function, |
| 106 | context->cork_data.message); |
| 107 | |
| 108 | context->cork_data.Reset(); |
| 109 | } |
| 110 | |
| 111 | } // namespace logging |
| 112 | } // namespace aos |
| 113 | |
| 114 | void log_do(log_level level, const char *format, ...) { |
| 115 | va_list ap; |
| 116 | va_start(ap, format); |
| 117 | aos::logging::VLog(level, format, ap); |
| 118 | va_end(ap); |
| 119 | } |
| 120 | |
| 121 | void log_cork(int line, const char *function, const char *format, ...) { |
| 122 | va_list ap; |
| 123 | va_start(ap, format); |
| 124 | aos::logging::VCork(line, function, format, ap); |
| 125 | va_end(ap); |
| 126 | } |
| 127 | |
| 128 | void log_uncork(int line, const char *function, log_level level, |
| 129 | const char *file, const char *format, ...) { |
| 130 | va_list ap; |
| 131 | va_start(ap, format); |
| 132 | aos::logging::VUnCork(line, function, level, file, format, ap); |
| 133 | va_end(ap); |
| 134 | } |