Brian Silverman | f665d69 | 2013-02-17 22:11:39 -0800 | [diff] [blame] | 1 | #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 | |
| 10 | namespace aos { |
| 11 | namespace logging { |
| 12 | namespace { |
| 13 | |
| 14 | using internal::Context; |
| 15 | |
| 16 | LogImplementation *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. |
| 19 | Mutex global_top_implementation_mutex; |
| 20 | LogImplementation *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. |
| 29 | class 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 | |
| 42 | void 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. |
| 54 | void 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 Silverman | e5d6569 | 2013-02-28 15:15:03 -0800 | [diff] [blame] | 69 | void NewContext() { |
| 70 | Context::Delete(); |
| 71 | } |
| 72 | |
Brian Silverman | f665d69 | 2013-02-17 22:11:39 -0800 | [diff] [blame] | 73 | void *DoInit() { |
| 74 | SetGlobalImplementation(new RootLogImplementation()); |
Brian Silverman | e5d6569 | 2013-02-28 15:15:03 -0800 | [diff] [blame] | 75 | |
| 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 Silverman | f665d69 | 2013-02-17 22:11:39 -0800 | [diff] [blame] | 84 | return NULL; |
| 85 | } |
| 86 | |
| 87 | } // namespace |
| 88 | namespace internal { |
| 89 | |
| 90 | Context::Context() |
| 91 | : implementation(get_global_top_implementation()), |
| 92 | sequence(0) { |
| 93 | cork_data.Reset(); |
| 94 | } |
| 95 | |
| 96 | void 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; |
| 104 | memcpy(message->name, context->name, context->name_size); |
| 105 | |
| 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 | |
| 113 | void 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 | |
| 123 | void 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 | |
| 147 | void VLog(log_level level, const char *format, va_list ap) { |
| 148 | LogImplementation::DoVLog(level, format, ap, 1); |
| 149 | } |
| 150 | |
| 151 | void 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 | |
| 172 | void 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 | |
| 185 | void 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 | |
| 192 | void 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 | |
| 207 | void Init() { |
| 208 | static Once<void> once(DoInit); |
| 209 | once.Get(); |
| 210 | } |
| 211 | |
| 212 | void Load() { |
| 213 | Context::Get(); |
| 214 | } |
| 215 | |
| 216 | void Cleanup() { |
| 217 | Context::Delete(); |
| 218 | } |
| 219 | |
| 220 | } // namespace logging |
| 221 | } // namespace aos |
| 222 | |
| 223 | void 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 | |
| 230 | void 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 | |
| 237 | void 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 | } |