blob: e98078bc478d326f719337702c7614d75083d9b4 [file] [log] [blame]
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <errno.h>
#include <unistd.h>
#include <limits.h>
#include <algorithm>
#include "aos/aos_core.h"
#include "aos/common/die.h"
#define DECL_LEVEL(name, value) const log_level name = value;
DECL_LEVELS
#undef DECL_LEVEL
log_level log_min = 0;
static const aos_type_sig message_sig = {sizeof(log_queue_message), 1234, 1500};
static const char *name;
static size_t name_size;
static aos_queue *queue;
static log_message corked_message;
static int cork_line_min, cork_line_max;
bool log_initted = false;
static inline void cork_init() {
corked_message.message[0] = '\0'; // make strlen of it 0
cork_line_min = INT_MAX;
cork_line_max = -1;
}
int log_init(const char *name_in){
if (log_initted) {
return 1;
}
const size_t name_in_len = strlen(name_in);
const char *last_slash = static_cast<const char *>(memrchr(name_in,
'/', name_in_len));
if (last_slash == NULL) {
name_size = name_in_len;
last_slash = name_in - 1;
} else {
name_size = name_in + name_in_len - last_slash;
}
if (name_size >= sizeof(log_message::name)) {
fprintf(stderr, "logging: error: name '%s' (going to use %zu bytes) is too long\n",
name_in, name_size);
return -1;
}
char *const tmp = static_cast<char *>(malloc(name_size + 1));
if (tmp == NULL) {
fprintf(stderr, "logging: error: couldn't malloc(%zd)\n", name_size + 1);
return -1;
}
name = tmp;
memcpy(tmp, last_slash + 1, name_size);
tmp[name_size] = 0;
queue = aos_fetch_queue("LoggingQueue", &message_sig);
if (queue == NULL) {
fprintf(stderr, "logging: error: couldn't fetch queue\n");
return -1;
}
cork_init();
log_initted = true;
return 0;
}
void log_uninit() {
free(const_cast<char *>(name));
name = NULL;
name_size = 0;
queue = NULL;
log_initted = false;
}
static inline void check_init() {
if (!log_initted) {
fprintf(stderr, "logging: warning: not initialized in %jd."
" initializing using \"<null>\" as name\n", static_cast<intmax_t>(getpid()));
log_init("<null>");
}
}
const log_message *log_read_next2(int flags, int *index) {
check_init();
return static_cast<const log_message *>(aos_queue_read_msg_index(queue, flags, index));
}
const log_message *log_read_next1(int flags) {
check_init();
const log_message *r = NULL;
do {
r = static_cast<const log_message *>(aos_queue_read_msg(queue, flags));
} while ((flags & BLOCK) && r == NULL); // not blocking means return a NULL if that's what it gets
return r;
}
void log_free_message(const log_message *msg) {
check_init();
aos_queue_free_msg(queue, msg);
}
int log_crio_message_send(log_crio_message &to_send) {
check_init();
log_crio_message *msg = static_cast<log_crio_message *>(aos_queue_get_msg(queue));
if (msg == NULL) {
fprintf(stderr, "logging: error: couldn't get a message to send '%s'\n",
to_send.message);
return -1;
}
//*msg = to_send;
static_assert(sizeof(to_send) == sizeof(*msg), "something is very wrong here");
memcpy(msg, &to_send, sizeof(to_send));
if (aos_queue_write_msg(queue, msg, OVERRIDE) < 0) {
fprintf(stderr, "logging: error: writing crio message '%s' failed\n", msg->message);
aos_queue_free_msg(queue, msg);
return -1;
}
return 0;
}
// Prints format (with ap) into output and correctly deals with the message
// being too long etc.
// Returns whether it succeeded or not.
static inline bool vsprintf_in(char *output, size_t output_size,
const char *format, va_list ap) {
static const char *continued = "...\n";
const size_t size = output_size - strlen(continued);
const int ret = vsnprintf(output, size, format, ap);
if (ret < 0) {
fprintf(stderr, "logging: error: vsnprintf failed with %d (%s)\n",
errno, strerror(errno));
return false;
} else if (static_cast<uintmax_t>(ret) >= static_cast<uintmax_t>(size)) {
// overwrite the NULL at the end of the existing one and
// copy in the one on the end of continued
memcpy(&output[size - 1], continued, strlen(continued) + 1);
}
return true;
}
static inline bool write_message(log_message *msg, log_level level) {
msg->level = level;
msg->source = getpid();
memcpy(msg->name, name, name_size + 1);
if (clock_gettime(CLOCK_REALTIME, &msg->time) == -1) {
fprintf(stderr, "logging: warning: couldn't get the current time "
"because of %d (%s)\n", errno, strerror(errno));
msg->time.tv_sec = 0;
msg->time.tv_nsec = 0;
}
static uint8_t local_sequence = -1;
msg->sequence = ++local_sequence;
if (aos_queue_write_msg(queue, msg, OVERRIDE) < 0) {
fprintf(stderr, "logging: error: writing message '%s' failed\n", msg->message);
aos_queue_free_msg(queue, msg);
return false;
}
return true;
}
static inline int vlog_do(log_level level, const char *format, va_list ap) {
log_message *msg = static_cast<log_message *>(aos_queue_get_msg(queue));
if (msg == NULL) {
fprintf(stderr, "logging: error: couldn't get a message to send '%s'\n", format);
return -1;
}
if (!vsprintf_in(msg->message, sizeof(msg->message), format, ap)) {
return -1;
}
if (!write_message(msg, level)) {
return -1;
}
if (level == FATAL) {
aos::Die("%s", msg->message);
}
return 0;
}
int log_do(log_level level, const char *format, ...) {
check_init();
va_list ap;
va_start(ap, format);
const int ret = vlog_do(level, format, ap);
va_end(ap);
return ret;
}
static inline int vlog_cork(int line, const char *format, va_list ap) {
const size_t message_length = strlen(corked_message.message);
if (line > cork_line_max) cork_line_max = line;
if (line < cork_line_min) cork_line_min = line;
return vsprintf_in(corked_message.message + message_length,
sizeof(corked_message.message) - message_length, format, ap) ? 0 : -1;
}
int log_cork(int line, const char *format, ...) {
check_init();
va_list ap;
va_start(ap, format);
const int ret = vlog_cork(line, format, ap);
va_end(ap);
return ret;
}
static inline bool log_uncork_helper(char *output, size_t output_size,
const char *format, ...) {
check_init();
va_list ap;
va_start(ap, format);
const bool ret = vsprintf_in(output, output_size, format, ap);
va_end(ap);
return ret;
}
int log_uncork(int line, log_level level, const char *begin_format,
const char *format, ...) {
check_init();
va_list ap;
va_start(ap, format);
const int ret = vlog_cork(line, format, ap);
va_end(ap);
if (ret != 0) {
return ret;
}
log_message *msg = static_cast<log_message *>(aos_queue_get_msg(queue));
if (msg == NULL) {
fprintf(stderr, "logging: error: couldn't get a message to send '%s'\n", format);
cork_init();
return -1;
}
static char new_format[LOG_MESSAGE_LEN];
if (!log_uncork_helper(new_format, sizeof(new_format), begin_format,
cork_line_min, cork_line_max)) {
cork_init();
return -1;
}
const size_t new_length = strlen(new_format);
memcpy(msg->message, new_format, new_length);
memcpy(msg->message + new_length, corked_message.message,
std::min(strlen(corked_message.message) + 1,
sizeof(msg->message) - new_length));
// in case corked_message.message was too long, it'll still be NULL-terminated
msg->message[sizeof(msg->message) - 1] = '\0';
cork_init();
if (!write_message(msg, level)) {
return -1;
}
return 0;
}