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) { |
Brian Silverman | 63cf241 | 2013-11-17 05:44:36 -0800 | [diff] [blame] | 60 | LOG(FATAL, "vsnprintf(%p, %zd, %s, args) failed with %d (%s)\n", |
| 61 | output, size, format, errno, strerror(errno)); |
Brian Silverman | f665d69 | 2013-02-17 22:11:39 -0800 | [diff] [blame] | 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; |
Brian Silverman | ab6615c | 2013-03-05 20:29:29 -0800 | [diff] [blame] | 104 | memcpy(message->name, context->name.c_str(), context->name.size() + 1); |
Brian Silverman | f665d69 | 2013-02-17 22:11:39 -0800 | [diff] [blame] | 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) { |
Brian Silverman | 8efe23e | 2013-07-07 23:31:37 -0700 | [diff] [blame] | 114 | fprintf(output, "%s(%" PRId32 ")(%05" PRIu16 "): %s at" |
| 115 | " %010" PRId32 ".%09" PRId32 "s: %s", |
Brian Silverman | 7aa971b | 2013-03-16 14:40:09 -0700 | [diff] [blame] | 116 | message.name, static_cast<int32_t>(message.source), message.sequence, |
Brian Silverman | f665d69 | 2013-02-17 22:11:39 -0800 | [diff] [blame] | 117 | log_str(message.level), message.seconds, message.nseconds, |
| 118 | message.message); |
| 119 | } |
| 120 | |
| 121 | } // namespace internal |
| 122 | |
Brian Silverman | 1e8ddfe | 2013-12-19 16:20:53 -0800 | [diff] [blame] | 123 | StreamLogImplementation::StreamLogImplementation(FILE *stream) |
| 124 | : stream_(stream) {} |
| 125 | |
| 126 | void StreamLogImplementation::DoLog(log_level level, const char *format, |
| 127 | va_list ap) { |
| 128 | LogMessage message; |
| 129 | internal::FillInMessage(level, format, ap, &message); |
| 130 | internal::PrintMessage(stream_, message); |
| 131 | } |
| 132 | |
Brian Silverman | f665d69 | 2013-02-17 22:11:39 -0800 | [diff] [blame] | 133 | void LogImplementation::DoVLog(log_level level, const char *format, va_list ap, |
| 134 | int levels) { |
| 135 | Context *context = Context::Get(); |
| 136 | |
| 137 | LogImplementation *top_implementation = context->implementation; |
| 138 | LogImplementation *new_implementation = top_implementation; |
| 139 | LogImplementation *implementation = NULL; |
| 140 | assert(levels >= 1); |
| 141 | for (int i = 0; i < levels; ++i) { |
| 142 | implementation = new_implementation; |
| 143 | if (new_implementation == NULL) { |
| 144 | Die("no logging implementation to use\n"); |
| 145 | } |
| 146 | new_implementation = new_implementation->next(); |
| 147 | } |
| 148 | context->implementation = new_implementation; |
| 149 | implementation->DoLog(level, format, ap); |
| 150 | context->implementation = top_implementation; |
| 151 | |
| 152 | if (level == FATAL) { |
| 153 | VDie(format, ap); |
| 154 | } |
| 155 | } |
| 156 | |
| 157 | void VLog(log_level level, const char *format, va_list ap) { |
| 158 | LogImplementation::DoVLog(level, format, ap, 1); |
| 159 | } |
| 160 | |
| 161 | void VCork(int line, const char *function, const char *format, va_list ap) { |
| 162 | Context *context = Context::Get(); |
| 163 | |
| 164 | const size_t message_length = strlen(context->cork_data.message); |
| 165 | if (line > context->cork_data.line_max) context->cork_data.line_max = line; |
| 166 | if (line < context->cork_data.line_min) context->cork_data.line_min = line; |
| 167 | |
| 168 | if (context->cork_data.function == NULL) { |
| 169 | context->cork_data.function = function; |
| 170 | } else { |
| 171 | if (strcmp(context->cork_data.function, function) != 0) { |
| 172 | LOG(FATAL, "started corking data in function %s but then moved to %s\n", |
| 173 | context->cork_data.function, function); |
| 174 | } |
| 175 | } |
| 176 | |
| 177 | ExecuteFormat(context->cork_data.message + message_length, |
| 178 | sizeof(context->cork_data.message) - message_length, |
| 179 | format, ap); |
| 180 | } |
| 181 | |
| 182 | void VUnCork(int line, const char *function, log_level level, const char *file, |
| 183 | const char *format, va_list ap) { |
| 184 | Context *context = Context::Get(); |
| 185 | |
| 186 | VCork(line, function, format, ap); |
| 187 | |
| 188 | log_do(level, "%s: %d-%d: %s: %s", file, |
| 189 | context->cork_data.line_min, context->cork_data.line_max, function, |
| 190 | context->cork_data.message); |
| 191 | |
| 192 | context->cork_data.Reset(); |
| 193 | } |
| 194 | |
| 195 | void LogNext(log_level level, const char *format, ...) { |
| 196 | va_list ap; |
| 197 | va_start(ap, format); |
| 198 | LogImplementation::DoVLog(level, format, ap, 2); |
| 199 | va_end(ap); |
| 200 | } |
| 201 | |
| 202 | void AddImplementation(LogImplementation *implementation) { |
| 203 | Context *context = Context::Get(); |
| 204 | |
| 205 | if (implementation->next() != NULL) { |
| 206 | LOG(FATAL, "%p already has a next implementation, but it's not" |
| 207 | " being used yet\n", implementation); |
| 208 | } |
| 209 | |
| 210 | LogImplementation *old = context->implementation; |
| 211 | if (old != NULL) { |
| 212 | implementation->set_next(old); |
| 213 | } |
| 214 | SetGlobalImplementation(implementation); |
| 215 | } |
| 216 | |
| 217 | void Init() { |
| 218 | static Once<void> once(DoInit); |
| 219 | once.Get(); |
| 220 | } |
| 221 | |
| 222 | void Load() { |
| 223 | Context::Get(); |
| 224 | } |
| 225 | |
| 226 | void Cleanup() { |
| 227 | Context::Delete(); |
| 228 | } |
| 229 | |
| 230 | } // namespace logging |
| 231 | } // namespace aos |
| 232 | |
| 233 | void log_do(log_level level, const char *format, ...) { |
| 234 | va_list ap; |
| 235 | va_start(ap, format); |
| 236 | aos::logging::VLog(level, format, ap); |
| 237 | va_end(ap); |
| 238 | } |
| 239 | |
| 240 | void log_cork(int line, const char *function, const char *format, ...) { |
| 241 | va_list ap; |
| 242 | va_start(ap, format); |
| 243 | aos::logging::VCork(line, function, format, ap); |
| 244 | va_end(ap); |
| 245 | } |
| 246 | |
| 247 | void log_uncork(int line, const char *function, log_level level, |
| 248 | const char *file, const char *format, ...) { |
| 249 | va_list ap; |
| 250 | va_start(ap, format); |
| 251 | aos::logging::VUnCork(line, function, level, file, format, ap); |
| 252 | va_end(ap); |
| 253 | } |