blob: e98078bc478d326f719337702c7614d75083d9b4 [file] [log] [blame]
brians343bc112013-02-10 01:53:46 +00001#include <stdarg.h>
2#include <stdio.h>
3#include <string.h>
4#include <time.h>
5#include <sys/types.h>
6#include <errno.h>
7#include <unistd.h>
8#include <limits.h>
9
10#include <algorithm>
11
12#include "aos/aos_core.h"
13#include "aos/common/die.h"
14
15#define DECL_LEVEL(name, value) const log_level name = value;
16DECL_LEVELS
17#undef DECL_LEVEL
18
19log_level log_min = 0;
20
21static const aos_type_sig message_sig = {sizeof(log_queue_message), 1234, 1500};
22static const char *name;
23static size_t name_size;
24static aos_queue *queue;
25static log_message corked_message;
26static int cork_line_min, cork_line_max;
27bool log_initted = false;
28
29static inline void cork_init() {
30 corked_message.message[0] = '\0'; // make strlen of it 0
31 cork_line_min = INT_MAX;
32 cork_line_max = -1;
33}
34int log_init(const char *name_in){
35 if (log_initted) {
36 return 1;
37 }
38
39 const size_t name_in_len = strlen(name_in);
40 const char *last_slash = static_cast<const char *>(memrchr(name_in,
41 '/', name_in_len));
42 if (last_slash == NULL) {
43 name_size = name_in_len;
44 last_slash = name_in - 1;
45 } else {
46 name_size = name_in + name_in_len - last_slash;
47 }
48 if (name_size >= sizeof(log_message::name)) {
49 fprintf(stderr, "logging: error: name '%s' (going to use %zu bytes) is too long\n",
50 name_in, name_size);
51 return -1;
52 }
53 char *const tmp = static_cast<char *>(malloc(name_size + 1));
54 if (tmp == NULL) {
55 fprintf(stderr, "logging: error: couldn't malloc(%zd)\n", name_size + 1);
56 return -1;
57 }
58 name = tmp;
59 memcpy(tmp, last_slash + 1, name_size);
60 tmp[name_size] = 0;
61 queue = aos_fetch_queue("LoggingQueue", &message_sig);
62 if (queue == NULL) {
63 fprintf(stderr, "logging: error: couldn't fetch queue\n");
64 return -1;
65 }
66
67 cork_init();
68
69 log_initted = true;
70 return 0;
71}
72void log_uninit() {
73 free(const_cast<char *>(name));
74 name = NULL;
75 name_size = 0;
76 queue = NULL;
77 log_initted = false;
78}
79
80static inline void check_init() {
81 if (!log_initted) {
82 fprintf(stderr, "logging: warning: not initialized in %jd."
83 " initializing using \"<null>\" as name\n", static_cast<intmax_t>(getpid()));
84 log_init("<null>");
85 }
86}
87
88const log_message *log_read_next2(int flags, int *index) {
89 check_init();
90 return static_cast<const log_message *>(aos_queue_read_msg_index(queue, flags, index));
91}
92const log_message *log_read_next1(int flags) {
93 check_init();
94 const log_message *r = NULL;
95 do {
96 r = static_cast<const log_message *>(aos_queue_read_msg(queue, flags));
97 } while ((flags & BLOCK) && r == NULL); // not blocking means return a NULL if that's what it gets
98 return r;
99}
100void log_free_message(const log_message *msg) {
101 check_init();
102 aos_queue_free_msg(queue, msg);
103}
104
105int log_crio_message_send(log_crio_message &to_send) {
106 check_init();
107
108 log_crio_message *msg = static_cast<log_crio_message *>(aos_queue_get_msg(queue));
109 if (msg == NULL) {
110 fprintf(stderr, "logging: error: couldn't get a message to send '%s'\n",
111 to_send.message);
112 return -1;
113 }
114 //*msg = to_send;
115 static_assert(sizeof(to_send) == sizeof(*msg), "something is very wrong here");
116 memcpy(msg, &to_send, sizeof(to_send));
117 if (aos_queue_write_msg(queue, msg, OVERRIDE) < 0) {
118 fprintf(stderr, "logging: error: writing crio message '%s' failed\n", msg->message);
119 aos_queue_free_msg(queue, msg);
120 return -1;
121 }
122
123 return 0;
124}
125
126// Prints format (with ap) into output and correctly deals with the message
127// being too long etc.
128// Returns whether it succeeded or not.
129static inline bool vsprintf_in(char *output, size_t output_size,
130 const char *format, va_list ap) {
131 static const char *continued = "...\n";
132 const size_t size = output_size - strlen(continued);
133 const int ret = vsnprintf(output, size, format, ap);
134 if (ret < 0) {
135 fprintf(stderr, "logging: error: vsnprintf failed with %d (%s)\n",
136 errno, strerror(errno));
137 return false;
138 } else if (static_cast<uintmax_t>(ret) >= static_cast<uintmax_t>(size)) {
139 // overwrite the NULL at the end of the existing one and
140 // copy in the one on the end of continued
141 memcpy(&output[size - 1], continued, strlen(continued) + 1);
142 }
143 return true;
144}
145static inline bool write_message(log_message *msg, log_level level) {
146 msg->level = level;
147 msg->source = getpid();
148 memcpy(msg->name, name, name_size + 1);
149 if (clock_gettime(CLOCK_REALTIME, &msg->time) == -1) {
150 fprintf(stderr, "logging: warning: couldn't get the current time "
151 "because of %d (%s)\n", errno, strerror(errno));
152 msg->time.tv_sec = 0;
153 msg->time.tv_nsec = 0;
154 }
155
156 static uint8_t local_sequence = -1;
157 msg->sequence = ++local_sequence;
158
159 if (aos_queue_write_msg(queue, msg, OVERRIDE) < 0) {
160 fprintf(stderr, "logging: error: writing message '%s' failed\n", msg->message);
161 aos_queue_free_msg(queue, msg);
162 return false;
163 }
164 return true;
165}
166static inline int vlog_do(log_level level, const char *format, va_list ap) {
167 log_message *msg = static_cast<log_message *>(aos_queue_get_msg(queue));
168 if (msg == NULL) {
169 fprintf(stderr, "logging: error: couldn't get a message to send '%s'\n", format);
170 return -1;
171 }
172
173 if (!vsprintf_in(msg->message, sizeof(msg->message), format, ap)) {
174 return -1;
175 }
176
177 if (!write_message(msg, level)) {
178 return -1;
179 }
180
181 if (level == FATAL) {
182 aos::Die("%s", msg->message);
183 }
184
185 return 0;
186}
187int log_do(log_level level, const char *format, ...) {
188 check_init();
189 va_list ap;
190 va_start(ap, format);
191 const int ret = vlog_do(level, format, ap);
192 va_end(ap);
193 return ret;
194}
195
196static inline int vlog_cork(int line, const char *format, va_list ap) {
197 const size_t message_length = strlen(corked_message.message);
198 if (line > cork_line_max) cork_line_max = line;
199 if (line < cork_line_min) cork_line_min = line;
200 return vsprintf_in(corked_message.message + message_length,
201 sizeof(corked_message.message) - message_length, format, ap) ? 0 : -1;
202}
203int log_cork(int line, const char *format, ...) {
204 check_init();
205 va_list ap;
206 va_start(ap, format);
207 const int ret = vlog_cork(line, format, ap);
208 va_end(ap);
209 return ret;
210}
211static inline bool log_uncork_helper(char *output, size_t output_size,
212 const char *format, ...) {
213 check_init();
214 va_list ap;
215 va_start(ap, format);
216 const bool ret = vsprintf_in(output, output_size, format, ap);
217 va_end(ap);
218 return ret;
219}
220int log_uncork(int line, log_level level, const char *begin_format,
221 const char *format, ...) {
222 check_init();
223 va_list ap;
224 va_start(ap, format);
225 const int ret = vlog_cork(line, format, ap);
226 va_end(ap);
227 if (ret != 0) {
228 return ret;
229 }
230
231 log_message *msg = static_cast<log_message *>(aos_queue_get_msg(queue));
232 if (msg == NULL) {
233 fprintf(stderr, "logging: error: couldn't get a message to send '%s'\n", format);
234 cork_init();
235 return -1;
236 }
237
238 static char new_format[LOG_MESSAGE_LEN];
239 if (!log_uncork_helper(new_format, sizeof(new_format), begin_format,
240 cork_line_min, cork_line_max)) {
241 cork_init();
242 return -1;
243 }
244 const size_t new_length = strlen(new_format);
245 memcpy(msg->message, new_format, new_length);
246 memcpy(msg->message + new_length, corked_message.message,
247 std::min(strlen(corked_message.message) + 1,
248 sizeof(msg->message) - new_length));
249 // in case corked_message.message was too long, it'll still be NULL-terminated
250 msg->message[sizeof(msg->message) - 1] = '\0';
251 cork_init();
252
253 if (!write_message(msg, level)) {
254 return -1;
255 }
256
257 return 0;
258}
259