/** * @file bflog.c * @brief * * Copyright (c) 2022 Bouffalolab team * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. The * ASF licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * */ #include #include #include #include #include "bflog.h" #include "bflog_dlist.h" /** @addtogroup std_func * @{ */ #ifndef bflogc_fopen #define bflogc_fopen fopen #endif #ifndef bflogc_fclose #define bflogc_fclose fclose #endif #ifndef bflogc_fwrite #define bflogc_fwrite fwrite #endif #ifndef bflogc_ftell #define bflogc_ftell ftell #endif #ifndef bflogc_fflush #define bflogc_fflush fflush #endif #ifndef bflogc_memcpy #define bflogc_memcpy memcpy #endif #ifndef bflogc_snprintf #define bflogc_snprintf snprintf #endif #ifndef bflogc_vsnprintf #define bflogc_vsnprintf vsnprintf #endif #ifndef bflogc_remove #define bflogc_remove remove #endif #ifndef bflogc_rename #define bflogc_rename rename #endif #ifndef bflogc_strcmp #define bflogc_strcmp strcmp #endif /** * @} */ /** @addtogroup BFLOG_LOCAL_MARCO * @{ */ #ifdef CONFIG_BFLOG_DEBUG #define _BFLOG_CHECK(_expr, _ret) \ if (!(_expr)) \ return _ret #else #define _BFLOG_CHECK(_expr, _ret) ((void)0) #endif #define _bflog_t(_ptr) ((bflog_t *)(_ptr)) #define _msg_t(_ptr) ((struct _bflog_msg *)(_ptr)) #define _tag_t(_ptr) ((struct _bflog_tag *)(_ptr)) #define _direct_t(_ptr) ((bflog_direct_t *)(_ptr)) #define _direct_buffer_t(_ptr) ((bflog_direct_buffer_t *)(_ptr)) #define _direct_stream_t(_ptr) ((bflog_direct_stream_t *)(_ptr)) #define _direct_file_t(_ptr) ((bflog_direct_file_t *)(_ptr)) #define _direct_file_time_t(_ptr) ((bflog_direct_file_time_t *)(_ptr)) #define _direct_file_size_t(_ptr) ((bflog_direct_file_size_t *)(_ptr)) #define _layout_simple_t(_ptr) ((bflog_layout_simple_t *)(_ptr)) #define _layout_format_t(_ptr) ((bflog_layout_format_t *)(_ptr)) #define _layout_yaml_t(_ptr) ((bflog_layout_yaml_t *)(_ptr)) /*!< default log record flag */ #ifndef BFLOG_FLAG_DEFAULT #define BFLOG_FLAG_DEFAULT ( \ BFLOG_FLAG_LEVEL | \ BFLOG_FLAG_TAG | \ BFLOG_FLAG_FUNC | \ BFLOG_FLAG_LINE | \ BFLOG_FLAG_FILE | \ BFLOG_FLAG_CLK | \ BFLOG_FLAG_TIME | \ BFLOG_FLAG_THREAD) #endif /*!< line buffer size (in stack) */ /*!< flush use 4xline buffer size in stack */ /*!< log use 2xline buffer size in stack */ /*!< pay attention to prevent stack overflow */ #ifndef BFLOG_LINE_BUFFER_SIZE #define BFLOG_LINE_BUFFER_SIZE 256 #endif /*!< default log record level */ #ifndef BFLOG_LEVEL_DEFAULT #define BFLOG_LEVEL_DEFAULT BFLOG_LEVEL_INFO #endif /*!< default direct print level */ #ifndef BFLOG_DIRECT_LEVEL_DEFAULT #define BFLOG_DIRECT_LEVEL_DEFAULT BFLOG_LEVEL_INFO #endif /*!< file size rotate min size */ #ifndef BFLOG_FILE_SIZE_MIN #define BFLOG_FILE_SIZE_MIN (128 * 1024) #endif /*!< file time rotate min interval */ #ifndef BFLOG_FILE_INTERVAL_MIN #define BFLOG_FILE_INTERVAL_MIN (10 * 60) #endif /** * @} */ static char *bflog_color_strings[6] = { BFLOG_COLOR_START BFLOG_COLOR_RESET BFLOG_COLOR_FATAL BFLOG_COLOR_END, BFLOG_COLOR_START BFLOG_COLOR_RESET BFLOG_COLOR_ERROR BFLOG_COLOR_END, BFLOG_COLOR_START BFLOG_COLOR_RESET BFLOG_COLOR_WARN BFLOG_COLOR_END, BFLOG_COLOR_START BFLOG_COLOR_RESET BFLOG_COLOR_INFO BFLOG_COLOR_END, BFLOG_COLOR_START BFLOG_COLOR_RESET BFLOG_COLOR_DEBUG BFLOG_COLOR_END, BFLOG_COLOR_START BFLOG_COLOR_RESET BFLOG_COLOR_TRACE BFLOG_COLOR_END }; static char *bflog_level_strings[6] = { BFLOG_LEVEL_FATAL_STRING, BFLOG_LEVEL_ERROR_STRING, BFLOG_LEVEL_WARN_STRING, BFLOG_LEVEL_INFO_STRING, BFLOG_LEVEL_DEBUG_STRING, BFLOG_LEVEL_TRACE_STRING }; static uint32_t global_filter = 0xffffffff; static char *bflog_dummy_string = ""; extern struct _bflog_tag __bflog_tags_start__; extern struct _bflog_tag __bflog_tags_end__; /** * @brief dummy function */ static int dummy(void) { return 0; } /** @addtogroup BFLOG_TIMESTAMP * @{ */ #ifdef BFLOG_TIMESTAMP_ENABLE static const uint8_t month_day[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; static const uint8_t leap_month_day[12] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; /** * @brief check leap year * @param year year * @return uint8_t 1 leap year, 0 noleap year */ static uint8_t check_leap_year(uint16_t year) { if (year % 4) { return 0; } else { if ((year % 100 == 0) && (year % 400 != 0)) { return 0; } else { return 1; } } } /** * @brief calculate week * @param time time * @return uint8_t */ static void cal_weekday(bflog_tm_t *time) { uint32_t y, m, d, w; y = time->year; m = time->mon; d = time->mday; if ((m == 1) || (m == 2)) { m += 12; y--; } w = (d + 2 * m + 3 * (m + 1) / 5 + y + y / 4 - y / 100 + y / 400 + 1) % 7; time->wday = (uint8_t)w; } /** * @brief timestamp to time * @param timestamp timestamp * @param time time */ void bflog_unix2time(uint32_t timestamp, bflog_tm_t *time) { #define BFLOG_FOUR_YEAR_DAY ((365 << 2) + 1) #define BFLOG_SEC_NUM_PER_DAY (24 * 60 * 60) #define BFLOG_SEC_NUM_PER_HOUR (60 * 60) #define BFLOG_SEC_NUM_PER_MINUTE (60) #ifndef BFLOG_TIMEZONE #define BFLOG_TIMEZONE 8 #endif uint32_t totle_day_num; uint32_t current_sec_num; uint16_t remain_day; uint16_t temp_year; const uint8_t *p = NULL; totle_day_num = timestamp / BFLOG_SEC_NUM_PER_DAY; current_sec_num = timestamp % BFLOG_SEC_NUM_PER_DAY; time->hour = current_sec_num / BFLOG_SEC_NUM_PER_HOUR; time->min = (current_sec_num % BFLOG_SEC_NUM_PER_HOUR) / BFLOG_SEC_NUM_PER_MINUTE; time->sec = (current_sec_num % BFLOG_SEC_NUM_PER_HOUR) % BFLOG_SEC_NUM_PER_MINUTE; time->hour += BFLOG_TIMEZONE; if (time->hour > 23) { time->hour -= 24; totle_day_num++; } time->year = 1970 + (totle_day_num / BFLOG_FOUR_YEAR_DAY) * 4; remain_day = totle_day_num % BFLOG_FOUR_YEAR_DAY; temp_year = check_leap_year(time->year) ? 366 : 365; while (remain_day >= temp_year) { time->year++; remain_day -= temp_year; temp_year = check_leap_year(time->year) ? 366 : 365; } p = check_leap_year(time->year) ? leap_month_day : month_day; remain_day++; time->mon = 0; while (remain_day > *(p + time->mon)) { remain_day -= *(p + time->mon); time->mon++; } time->mon++; time->mday = remain_day; time->wday = time->mday + 2 * time->mon + 3 * (time->mon + 1) / 5 + time->year + time->year / 4 - time->year / 100 + time->year / 400; cal_weekday(time); } #endif /** * @} */ /** @addtogroup BFLOG_LINE_QUEUE * @{ */ /** * @brief free oldest message in queue * @param log recorder * @param free space to be released * @return uint16_t space actually released */ static void queue_free(bflog_t *log, uint16_t free) { char *pool = (char *)(log->queue.pool); uint16_t rpos = log->queue.rpos; uint16_t space = 0; while (free > space) { /*!< oldest message size */ uint16_t size = _msg_t(pool + rpos)->size; if (rpos + size >= log->queue.size) { rpos = rpos + size - log->queue.size; } else { rpos += size; } space += size; } log->queue.rpos = rpos; log->queue.free += space; } /** * @brief enqueue * @param log recorder * @param msg store message area */ static void queue_put(bflog_t *log, struct _bflog_msg *msg) { char *pool = (char *)(log->queue.pool); uint16_t remain = log->queue.size - log->queue.wpos; /*!< max message size */ if (msg->size > log->queue.size) { msg->size = log->queue.size; } /*!< free the oldest msg to store the latest msg */ if (log->queue.free < msg->size) { queue_free(log, msg->size - log->queue.free); } /*!< ring loop */ if (msg->size >= remain) { /*!< store to ring tail */ bflogc_memcpy(pool + log->queue.wpos, msg, remain); log->queue.wpos = msg->size - remain; /*!< store to ring head */ bflogc_memcpy(pool, (char *)msg + remain, log->queue.wpos); } else { bflogc_memcpy(pool + log->queue.wpos, msg, msg->size); log->queue.wpos += msg->size; } log->queue.free -= msg->size; } /** * @brief dequeue * @param log recorder * @param msg load message area */ static void queue_get(bflog_t *log, struct _bflog_msg *msg) { char *pool = (char *)(log->queue.pool); uint16_t remain; uint16_t size; if (log->queue.free == log->queue.size) { msg->zero = 0xbd; return; } remain = log->queue.size - log->queue.rpos; size = _msg_t(pool + log->queue.rpos)->size; /*!< ring loop */ if (size >= remain) { /*!< load from ring tail */ bflogc_memcpy(msg, pool + log->queue.rpos, remain); log->queue.rpos = size - remain; /*!< load from ring head */ bflogc_memcpy((char *)msg + remain, pool, log->queue.rpos); } else { bflogc_memcpy(msg, pool + log->queue.rpos, size); log->queue.rpos += size; } log->queue.free += size; ((char *)msg)[msg->size] = '\0'; } /** * @} */ /** @addtogroup BFLOG_FILTER * @{ */ /** * @brief find the last bit set * @param word word * @return int bit index */ static int bflog_fls(uint32_t word) { int bit = 32; if (!word) { bit -= 1; } if (!(word & 0xffff0000)) { word <<= 16; bit -= 16; } if (!(word & 0xff000000)) { word <<= 8; bit -= 8; } if (!(word & 0xf0000000)) { word <<= 4; bit -= 4; } if (!(word & 0xc0000000)) { word <<= 2; bit -= 2; } if (!(word & 0x80000000)) { word <<= 1; bit -= 1; } return bit; } /** * @brief alloc a filter * @return uint32_t (0 on faild) filter bit */ static uint32_t bflog_filter_alloc(void) { uint8_t shift = bflog_fls(global_filter) - 1; if (shift >= 0) { global_filter = global_filter & ~(0x1 << shift); return 0x1 << shift; } else { return 0; } } /** * @brief free a filter * @param filter filter bit */ static void bflog_filter_free(uint32_t filter) { global_filter = global_filter | filter; } /** * @brief config filter * @param filter filter bit * @param tag_string tag string pointer * @return int */ static int bflog_filter_set(uint32_t filter, void *tag_string, uint8_t enable) { struct _bflog_tag *ps = &__bflog_tags_start__; struct _bflog_tag *pe = &__bflog_tags_end__; while (ps < pe) { if ((tag_string == ps->tag) || (0 == bflogc_strcmp(tag_string, ps->tag))) { if (enable) { ps->en |= filter; } else { ps->en &= ~filter; } return 0; } ps += 1; } return -1; } /** * @} */ /** @addtogroup BFLOG_BASE_API * @{ */ int bflog_global_filter(void *tag_string, uint8_t enable) { _BFLOG_CHECK(tag_string != NULL, -1); return bflog_filter_set(0xffffffff, tag_string, enable); } /** * @brief create recorder, thread unsafe * @param log recorder * @param pool queue pool * @param size pool size * @return int */ int bflog_create(bflog_t *log, void *pool, uint16_t size, uint8_t mode) { _BFLOG_CHECK(log != NULL, -1); _BFLOG_CHECK(pool != NULL, -1); _BFLOG_CHECK(size > 0, -1); bflog_dlist_init(&(log->direct)); log->status = BFLOG_STATUS_READY; log->flags = BFLOG_FLAG_DEFAULT; log->level = BFLOG_LEVEL_DEFAULT; log->mode = mode; log->queue.free = size; log->queue.wpos = 0; log->queue.rpos = 0; log->queue.size = size; log->queue.pool = pool; log->enter_critical = dummy; log->exit_critical = dummy; log->flush_notice = dummy; uint32_t filter = bflog_filter_alloc(); if (filter > 0) { log->filter = filter; } else { return -1; } return 0; } /** * @brief delete recorder, thread unsafe * @param log recorder * @return int */ int bflog_delete(bflog_t *log) { _BFLOG_CHECK(log, -1); struct _bflog_list *node; struct _bflog_list *next; void *direct; switch (log->status) { case BFLOG_STATUS_READY: case BFLOG_STATUS_SUSPEND: break; default: return 0; } if (log->enter_critical()) { return -1; } switch (log->status) { case BFLOG_STATUS_READY: case BFLOG_STATUS_SUSPEND: break; default: log->exit_critical(); return -1; } BFLOG_DLIST_FOREACH_NEXT_S(node, next, &(log->direct)) { direct = BFLOG_DLIST_ENTRY(node, bflog_direct_t, list); if (_direct_t(direct)->status == BFLOG_DIRECT_STATUS_RUNNING) { _direct_t(direct)->status = BFLOG_DIRECT_STATUS_READY; } bflog_dlist_remove(node); } log->status = BFLOG_STATUS_ILLEGAL; bflog_filter_free(log->filter); log->exit_critical(); return 0; } /** * @brief append directed output, thread safe * @param log recorder * @param direct directed output * @return int */ int bflog_append(bflog_t *log, bflog_direct_t *direct) { _BFLOG_CHECK(log != NULL, -1); _BFLOG_CHECK(direct != NULL, -1); switch (log->status) { case BFLOG_STATUS_READY: case BFLOG_STATUS_SUSPEND: break; default: return -1; } if (log->enter_critical()) { return -1; } switch (log->status) { case BFLOG_STATUS_READY: case BFLOG_STATUS_SUSPEND: break; default: log->exit_critical(); return -1; } if (direct->status != BFLOG_DIRECT_STATUS_READY) { log->exit_critical(); return -1; } if (!bflog_dlist_is_empty(&(direct->list))) { log->exit_critical(); return -1; } bflog_dlist_insert_before(&(log->direct), &(direct->list)); direct->status = BFLOG_DIRECT_STATUS_SUSPEND; log->exit_critical(); return 0; } /** * @brief delete directed output, thread safe * @param log recorder * @param direct directed output * @return int */ int bflog_remove(bflog_t *log, bflog_direct_t *direct) { _BFLOG_CHECK(log != NULL, -1); _BFLOG_CHECK(direct != NULL, -1); struct _bflog_list *node; switch (log->status) { case BFLOG_STATUS_READY: case BFLOG_STATUS_SUSPEND: break; default: return -1; } if (log->enter_critical()) { return -1; } switch (log->status) { case BFLOG_STATUS_READY: case BFLOG_STATUS_SUSPEND: break; default: log->exit_critical(); return -1; } if (direct->status != BFLOG_DIRECT_STATUS_SUSPEND) { log->exit_critical(); return -1; } if (bflog_dlist_is_empty(&(direct->list))) { log->exit_critical(); return -1; } BFLOG_DLIST_FOREACH_NEXT(node, &(log->direct)) { if (BFLOG_DLIST_ENTRY(node, bflog_direct_t, list) == direct) { bflog_dlist_remove(&(direct->list)); direct->status = BFLOG_DIRECT_STATUS_READY; log->exit_critical(); return 0; } } log->exit_critical(); return -1; } /** * @brief suspend recorder, thread safe * @param log recorder * @return int */ int bflog_suspend(bflog_t *log) { _BFLOG_CHECK(log != NULL, -1); switch (log->status) { case BFLOG_STATUS_SUSPEND: return 0; case BFLOG_STATUS_READY: case BFLOG_STATUS_RUNNING: break; default: return -1; } if (log->enter_critical()) { return -1; } switch (log->status) { case BFLOG_STATUS_SUSPEND: log->exit_critical(); return 0; case BFLOG_STATUS_READY: case BFLOG_STATUS_RUNNING: break; default: log->exit_critical(); return -1; } log->status = BFLOG_STATUS_SUSPEND; log->exit_critical(); return 0; } /** * @brief resume recorder, thread safe * @param log recorder * @return int */ int bflog_resume(bflog_t *log) { _BFLOG_CHECK(log != NULL, -1); switch (log->status) { case BFLOG_STATUS_RUNNING: return 0; case BFLOG_STATUS_READY: case BFLOG_STATUS_SUSPEND: break; default: return -1; } if (log->enter_critical()) { return -1; } switch (log->status) { case BFLOG_STATUS_RUNNING: log->exit_critical(); return 0; case BFLOG_STATUS_READY: case BFLOG_STATUS_SUSPEND: break; default: log->exit_critical(); return -1; } log->status = BFLOG_STATUS_RUNNING; log->exit_critical(); return 0; } /** * @brief config recorder, thread safe * @param log recorder * @param command config command * @param param config param * @return int */ int bflog_control(bflog_t *log, uint32_t command, uint32_t param) { _BFLOG_CHECK(log != NULL, -1); switch (log->status) { case BFLOG_STATUS_READY: case BFLOG_STATUS_SUSPEND: break; case BFLOG_STATUS_RUNNING: if (command != BFLOG_CMD_LEVEL) { return -1; } break; default: return -1; } switch (command) { case BFLOG_CMD_ENTER_CRITICAL: if ((void *)param == NULL) { log->enter_critical = dummy; } else { log->enter_critical = (void *)param; } return 0; case BFLOG_CMD_EXIT_CRITICAL: { if ((void *)param == NULL) { log->exit_critical = dummy; } else { log->exit_critical = (void *)param; } } return 0; default: break; } if (log->enter_critical()) { return -1; } switch (log->status) { case BFLOG_STATUS_READY: case BFLOG_STATUS_SUSPEND: break; case BFLOG_STATUS_RUNNING: if (command != BFLOG_CMD_LEVEL) { return -1; } break; default: log->exit_critical(); return -1; } switch (command) { case BFLOG_CMD_ILLEGAL: return -1; case BFLOG_CMD_FLAG: log->flags = param & 0xff; break; case BFLOG_CMD_LEVEL: log->level = param & BFLOG_LEVEL_MASK; break; case BFLOG_CMD_QUEUE_POOL: log->queue.pool = (void *)param; break; case BFLOG_CMD_QUEUE_SIZE: log->queue.size = param & 0xffff; break; case BFLOG_CMD_QUEUE_RST: log->queue.wpos = 0; log->queue.rpos = 0; log->queue.free = log->queue.size; break; case BFLOG_CMD_FLUSH_NOTICE: if ((void *)param == NULL) { log->flush_notice = dummy; } else { log->flush_notice = (void *)param; } break; case BFLOG_CMD_MODE: log->mode = param & 0xff; break; default: log->exit_critical(); return -1; } log->exit_critical(); return 0; } /** * @brief config recorder filter, thread safe * @param log recorder * @param tag_string tag string * @param enable tag enable * @return int */ int bflog_filter(bflog_t *log, void *tag_string, uint8_t enable) { _BFLOG_CHECK(log != NULL, -1); _BFLOG_CHECK(tag_string != NULL, -1); int ret; switch (log->status) { case BFLOG_STATUS_READY: case BFLOG_STATUS_RUNNING: case BFLOG_STATUS_SUSPEND: break; default: return -1; } if (log->enter_critical()) { return -1; } ret = bflog_filter_set(log->filter, tag_string, enable); if (log->exit_critical()) { return -1; } return ret; } /** * @brief record log msg, thread safe * tag, file, func only recorded pointer * @param log recorder * @param level level threshold * @param tag tag * @param file const file name * @param func const func name * @param line file line * @param format format string * @param ... format params * @return int */ int bflog(void *log, uint8_t level, void *tag, const char *const file, const char *const func, const long line, const char *format, ...) { _BFLOG_CHECK(log != NULL, -1); _BFLOG_CHECK(format != NULL, -1); int ret; va_list args; uint8_t msg[BFLOG_LINE_BUFFER_SIZE + sizeof(struct _bflog_msg)]; uint16_t size = sizeof(struct _bflog_msg); /*!< working only during running and suspend */ switch (_bflog_t(log)->status) { case BFLOG_STATUS_RUNNING: break; default: return -1; } /*!< level filter */ if ((level & BFLOG_LEVEL_MASK) > _bflog_t(log)->level) { return 0; } /*!< if advanced tag, use tag filter */ if (((uintptr_t)(&__bflog_tags_start__) <= (uintptr_t)(tag)) && ((uintptr_t)(tag) < (uintptr_t)(&__bflog_tags_end__))) { /*!< tag filter */ if ((tag != NULL) && ((_tag_t(tag)->en & _bflog_t(log)->filter) != _bflog_t(log)->filter)) { return 0; } } /*!< record clock tick */ if (_bflog_t(log)->flags & BFLOG_FLAG_CLK) { _msg_t(msg)->clk = bflog_clock(); } else { _msg_t(msg)->clk = 0; } /*!< record timestamp */ if (_bflog_t(log)->flags & BFLOG_FLAG_TIME) { _msg_t(msg)->time = bflog_time(); } else { _msg_t(msg)->time = 0; } /*!< record level */ _msg_t(msg)->level = level; /*!< record tag, only record pointer */ if (_bflog_t(log)->flags & BFLOG_FLAG_TAG) { _msg_t(msg)->tag = tag; } else { _msg_t(msg)->tag = NULL; } /*!< record func, only record pointer */ if (_bflog_t(log)->flags & BFLOG_FLAG_FUNC) { _msg_t(msg)->func = func; } else { _msg_t(msg)->func = bflog_dummy_string; } /*!< record line */ if (_bflog_t(log)->flags & BFLOG_FLAG_LINE) { _msg_t(msg)->line = line; } else { _msg_t(msg)->line = 0; } /*!< record file, only record pointer */ if (_bflog_t(log)->flags & BFLOG_FLAG_FILE) { _msg_t(msg)->file = file; } else { _msg_t(msg)->file = bflog_dummy_string; } /*!< record thread, only record pointer */ if (_bflog_t(log)->flags & BFLOG_FLAG_THREAD) { _msg_t(msg)->thread = bflog_thread(); } else { _msg_t(msg)->thread = bflog_dummy_string; } /*!< set zero */ _msg_t(msg)->zero = 0; /*!< print string to msg->string */ va_start(args, format); ret = bflogc_vsnprintf(_msg_t(msg)->string, BFLOG_LINE_BUFFER_SIZE, format, args); va_end(args); /*!< check true size */ if ((ret >= 0) && (ret <= BFLOG_LINE_BUFFER_SIZE)) { size += ret; } else { size += BFLOG_LINE_BUFFER_SIZE; } /*!< align 4 byte */ if (size & 0x3) { _msg_t(msg)->size = (size & ~0x3) + 0x4; } else { _msg_t(msg)->size = size; } if (_bflog_t(log)->enter_critical()) { return -1; } /*!< working only during running and suspend */ switch (_bflog_t(log)->status) { case BFLOG_STATUS_RUNNING: break; default: _bflog_t(log)->exit_critical(); return -1; } queue_put(_bflog_t(log), (void *)(&msg)); if (_bflog_t(log)->mode & BFLOG_MODE_ASYNC) { _bflog_t(log)->flush_notice(); _bflog_t(log)->exit_critical(); return 0; } _bflog_t(log)->exit_critical(); return bflog_flush(_bflog_t(log)); } /** * @brief flush all msg in queue, thread safe * @param log recorder * @return int */ int bflog_flush(void *log) { _BFLOG_CHECK(log != NULL, -1); uint8_t msg[BFLOG_LINE_BUFFER_SIZE + sizeof(struct _bflog_msg)]; char buf[BFLOG_LINE_BUFFER_SIZE * 2]; struct _bflog_list *node; void *direct; uint32_t filter = _bflog_t(log)->filter; /*!< working only during running */ switch (_bflog_t(log)->status) { case BFLOG_STATUS_SUSPEND: return -1; case BFLOG_STATUS_RUNNING: break; default: return -1; } do { /*!< reset msg string */ _msg_t(msg)->string[0] = '\0'; if (_bflog_t(log)->enter_critical()) { return -1; } /*!< working only during running */ switch (_bflog_t(log)->status) { case BFLOG_STATUS_SUSPEND: _bflog_t(log)->exit_critical(); return -1; case BFLOG_STATUS_RUNNING: break; default: _bflog_t(log)->exit_critical(); return -1; } /*!< get msg(on stack) from queue(on ram) */ queue_get(_bflog_t(log), (void *)(&msg)); _bflog_t(log)->exit_critical(); if (_msg_t(msg)->zero != 0) { if (_msg_t(msg)->zero == 0xbd) { /*!< no msg */ return 0; } else { /*!< error */ return -1; } } /*!< color */ char *color; color = bflog_color_strings[_msg_t(msg)->level & BFLOG_LEVEL_MASK]; /*!< level */ char *level; if (_bflog_t(log)->flags & BFLOG_FLAG_LEVEL) { level = bflog_level_strings[_msg_t(msg)->level & BFLOG_LEVEL_MASK]; } else { level = bflog_dummy_string; } #ifdef BFLOG_TIMESTAMP_ENABLE /*!< time */ bflog_tm_t tm; bflog_unix2time(_msg_t(msg)->time, &tm); #endif /*!< check if advanced tag */ uint8_t advanced_tag; if (((uintptr_t)(&__bflog_tags_start__) <= (uintptr_t)(_msg_t(msg)->tag)) && ((uintptr_t)(_msg_t(msg)->tag) < (uintptr_t)(&__bflog_tags_end__))) { advanced_tag = 1; } else { advanced_tag = 0; } _bflog_t(log)->enter_critical(); /*!< foreach direct, execute layout to buf(on stack), then output buf(on stack) */ BFLOG_DLIST_FOREACH_NEXT(node, &(_bflog_t(log)->direct)) { _bflog_t(log)->exit_critical(); int size; direct = BFLOG_DLIST_ENTRY(node, bflog_direct_t, list); /*!< check direct status */ if (_direct_t(direct)->status != BFLOG_DIRECT_STATUS_RUNNING) { continue; } /*!< level filter */ if ((_msg_t(msg)->level & BFLOG_LEVEL_MASK) > _direct_t(direct)->level) { continue; } if (advanced_tag) { /*!< tag filter */ if ((_msg_t(msg)->tag != NULL) && ((_tag_t(_msg_t(msg)->tag)->en & filter) != filter)) { continue; } } char *tag; if ((_msg_t(msg)->tag == NULL)) { tag = bflog_dummy_string; } else { tag = advanced_tag ? _tag_t(_msg_t(msg)->tag)->tag : _msg_t(msg)->tag; } /*!< raw output */ if (_msg_t(msg)->level & BFLOG_LEVEL_RAW) { size = bflogc_snprintf( buf, BFLOG_LINE_BUFFER_SIZE * 2, "%s", _msg_t(msg)->string); goto output; } /*!< nolayout */ if (_direct_t(direct)->layout == NULL) { goto simple_layout; } /*!< layout */ switch (_direct_t(direct)->layout->type) { case BFLOG_LAYOUT_TYPE_FORMAT: if (_direct_t(direct)->color) { #ifdef BFLOG_TIMESTAMP_ENABLE size = _layout_format_t(_direct_t(direct)->layout) ->snprintf( buf, BFLOG_LINE_BUFFER_SIZE * 2, color, level, tag, &tm, _msg_t(msg)); #else size = _layout_format_t(_direct_t(direct)->layout) ->snprintf( buf, BFLOG_LINE_BUFFER_SIZE * 2, color, level, tag, NULL, _msg_t(msg)); #endif } else { #ifdef BFLOG_TIMESTAMP_ENABLE size = _layout_format_t(_direct_t(direct)->layout) ->snprintf( buf, BFLOG_LINE_BUFFER_SIZE * 2, bflog_dummy_string, level, tag, &tm, _msg_t(msg)); #else size = _layout_format_t(_direct_t(direct)->layout) ->snprintf( buf, BFLOG_LINE_BUFFER_SIZE * 2, bflog_dummy_string, level, tag, NULL, _msg_t(msg)); #endif } goto output; /*!< TODO Layout yaml format */ case BFLOG_LAYOUT_TYPE_YAML: case BFLOG_LAYOUT_TYPE_SIMPLE: default: goto simple_layout; } simple_layout: if (_direct_t(direct)->color) { /*!< default and simple color format */ #ifdef BFLOG_TIMESTAMP_ENABLE size = bflogc_snprintf( buf, BFLOG_LINE_BUFFER_SIZE * 2, BFLOG_SIMPLE_LAYOUT_STRING( color, level, tag, &tm, _msg_t(msg))); #else size = bflogc_snprintf( buf, BFLOG_LINE_BUFFER_SIZE * 2, BFLOG_SIMPLE_LAYOUT_STRING( color, level, tag, NULL, _msg_t(msg))); #endif } else { /*!< default and simple no color format */ #ifdef BFLOG_TIMESTAMP_ENABLE size = bflogc_snprintf( buf, BFLOG_LINE_BUFFER_SIZE * 2, BFLOG_SIMPLE_LAYOUT_STRING( bflog_dummy_string, level, tag, &tm, _msg_t(msg))); #else size = bflogc_snprintf( buf, BFLOG_LINE_BUFFER_SIZE * 2, BFLOG_SIMPLE_LAYOUT_STRING( bflog_dummy_string, level, tag, NULL, _msg_t(msg))); #endif } output: /*!< check true size */ if ((size < 0) || (size > BFLOG_LINE_BUFFER_SIZE * 2)) { size = BFLOG_LINE_BUFFER_SIZE * 2; } if (_direct_t(direct)->lock()) { /*!< drop log message */ } else { /*!< call write */ _direct_t(direct)->write(direct, buf, size); _direct_t(direct)->unlock(); } _bflog_t(log)->enter_critical(); } _bflog_t(log)->exit_critical(); } while (1); return -1; } /** * @} */ /** @addtogroup BFLOG_DIRECT_API * @{ */ /** * @brief create directed output, thread unsafe * @param direct directed output * @param type directed output type * @return int */ int bflog_direct_create(bflog_direct_t *direct, uint8_t type, uint8_t color, void(*lock), void(*unlock)) { _BFLOG_CHECK(direct != NULL, -1); _BFLOG_CHECK( type != BFLOG_DIRECT_TYPE_BUFFER || type != BFLOG_DIRECT_TYPE_STREAM || type != BFLOG_DIRECT_TYPE_FILE || type != BFLOG_DIRECT_TYPE_FILE_TIME || type != BFLOG_DIRECT_TYPE_FILE_SIZE, -1); bflog_dlist_init(&(direct->list)); direct->write = NULL; direct->layout = NULL; direct->status = BFLOG_DIRECT_STATUS_INIT; direct->level = BFLOG_DIRECT_LEVEL_DEFAULT; direct->type = type; direct->color = color; if ((lock == NULL) || (unlock == NULL)) { direct->lock = dummy; direct->unlock = dummy; } else { direct->lock = lock; direct->unlock = unlock; } return 0; } /** * @brief delete directed output, thread unsafe * @param direct directed output * @return int */ int bflog_direct_delete(bflog_direct_t *direct) { _BFLOG_CHECK(direct != NULL, -1); switch (direct->status) { case BFLOG_DIRECT_STATUS_INIT: break; default: return -1; } if (direct->lock()) { return -1; } switch (direct->status) { case BFLOG_DIRECT_STATUS_INIT: break; default: direct->unlock(); return -1; } bflog_dlist_remove(&(direct->list)); direct->write = NULL; direct->status = BFLOG_DIRECT_STATUS_ILLEGAL; direct->type = BFLOG_DIRECT_TYPE_ILLEGAL; direct->lock = dummy; int (*unlock)(void) = direct->unlock; direct->unlock = dummy; bflog_filter_free(direct->filter); unlock(); return 0; } /** * @brief suspend directed output, thread safe * @param direct directed output * @return int */ int bflog_direct_suspend(bflog_direct_t *direct) { _BFLOG_CHECK(direct != NULL, -1); switch (direct->status) { case BFLOG_DIRECT_STATUS_RUNNING: case BFLOG_DIRECT_STATUS_SUSPEND: break; default: return -1; } direct->status = BFLOG_STATUS_SUSPEND; return 0; } /** * @brief resume directed output, thread safe * @param direct directed output * @return int */ int bflog_direct_resume(bflog_direct_t *direct) { _BFLOG_CHECK(direct != NULL, -1); switch (direct->status) { case BFLOG_DIRECT_STATUS_RUNNING: case BFLOG_DIRECT_STATUS_SUSPEND: break; default: return -1; } direct->status = BFLOG_STATUS_RUNNING; return 0; } /** * @brief link to a layout, thread unsafe * @param direct directed output * @param layout layout * @return int */ int bflog_direct_link(bflog_direct_t *direct, bflog_layout_t *layout) { _BFLOG_CHECK(direct != NULL, -1); _BFLOG_CHECK(layout != NULL, -1); if ((direct->status != BFLOG_DIRECT_STATUS_READY) && (direct->status != BFLOG_DIRECT_STATUS_SUSPEND)) { return -1; } if (layout->status != BFLOG_LAYOUT_STATUS_READY) { return -1; } direct->layout = layout; return 0; } /** * @brief config direct * @param direct * @param command * @param param * @return int */ int bflog_direct_control(bflog_direct_t *direct, uint32_t command, uint32_t param) { _BFLOG_CHECK(direct != NULL, -1); switch (direct->status) { case BFLOG_STATUS_READY: case BFLOG_STATUS_SUSPEND: break; case BFLOG_STATUS_RUNNING: if (command != BFLOG_DIRECT_CMD_LEVEL) { return -1; } break; default: return -1; } switch (command) { case BFLOG_DIRECT_CMD_LEVEL: direct->level = param & BFLOG_LEVEL_MASK; break; case BFLOG_DIRECT_CMD_COLOR: direct->color = param != 0; break; case BFLOG_DIRECT_CMD_LOCK: if ((void *)param == NULL) { direct->lock = dummy; } else { direct->lock = (void *)param; } break; case BFLOG_DIRECT_CMD_UNLOCK: if ((void *)param == NULL) { direct->unlock = dummy; } else { direct->unlock = (void *)param; } break; default: return -1; } return 0; } /** * @brief config direct filter, thread safe * @param direct direct * @param tag_string tag string * @param enable tag enable * @return int */ int bflog_direct_filter(bflog_direct_t *direct, void *tag_string, uint8_t enable) { _BFLOG_CHECK(direct != NULL, -1); _BFLOG_CHECK(tag_string != NULL, -1); int ret; switch (direct->status) { case BFLOG_STATUS_READY: case BFLOG_STATUS_RUNNING: case BFLOG_STATUS_SUSPEND: break; default: return -1; } ret = bflog_filter_set(direct->filter, tag_string, enable); return ret; } /** * @} */ /** @addtogroup BFLOG_DIRECT_TYPE_API * @{ */ /** @addtogroup BFLOG_DIRECT_BUFFER_API * @{ */ #ifdef BFLOG_DIRECT_BUFFER_ENABLE #if 0 static void bflog_driect_write_buffer(bflog_direct_t *direct, void *ptr, uint16_t size) { /*!< TODO */ return; } int bflog_direct_init_buffer_s(bflog_direct_t *direct, void *buffer, void *size) { /*!< TODO */ (void)bflog_driect_write_buffer; return 0; } int bflog_direct_deinit_buffer_s(bflog_direct_t *direct) { /*!< TODO */ return 0; } #endif #endif /** * @} */ /** @addtogroup BFLOG_DIRECT_STREAM_API * @{ */ #ifdef BFLOG_DIRECT_STREAM_ENABLE /** * @brief write data to stream * @param log recorder * @param ptr data pointer * @param size data size * @return int */ static void bflog_direct_write_stream(bflog_direct_t *direct, void *ptr, uint16_t size) { _direct_stream_t(direct)->stream_output(ptr, size); } /** * @brief init stream type direct, thread safe * @param direct directed output * @param stream_output stream output function pointer * @return int */ int bflog_direct_init_stream(bflog_direct_t *direct, uint16_t (*stream_output)(void *, uint16_t)) { _BFLOG_CHECK(direct != NULL, -1); _BFLOG_CHECK(stream_output != NULL, -1); if (direct->lock()) { return -1; } if (direct->type != BFLOG_DIRECT_TYPE_STREAM) { direct->unlock(); return -1; } if (direct->status != BFLOG_DIRECT_STATUS_INIT) { direct->unlock(); return -1; } _direct_stream_t(direct)->stream_output = stream_output; direct->status = BFLOG_DIRECT_STATUS_READY; direct->write = bflog_direct_write_stream; direct->unlock(); return 0; } /** * @brief deinit stream type direct * @param direct directed output * @return int */ int bflog_direct_deinit_stream(bflog_direct_t *direct) { _BFLOG_CHECK(direct != NULL, -1); if (direct->type != BFLOG_DIRECT_TYPE_STREAM) { return -1; } if (direct->status != BFLOG_DIRECT_STATUS_READY) { return -1; } if (direct->lock()) { return -1; } direct->status = BFLOG_DIRECT_STATUS_INIT; direct->write = NULL; _direct_stream_t(direct)->stream_output = NULL; direct->unlock(); return 0; } #endif /** * @} */ /** @addtogroup BFLOG_DIRECT_FILE_API * @{ */ #ifdef BFLOG_DIRECT_FILE_ENABLE /** * @brief write data to file * @param direct directed output * @param ptr data ptr * @param size data size */ static void bflog_direct_write_file(bflog_direct_t *direct, void *ptr, uint16_t size) { _BFLOG_CHECK(direct != NULL, ); _BFLOG_CHECK(ptr != NULL, ); if (_direct_file_t(direct)->fp == NULL) { return; } bflogc_fwrite(ptr, 1, size, _direct_file_t(direct)->fp); bflogc_fflush(_direct_file_t(direct)->fp); } /** * @brief * @param direct * @param path * @return int */ int bflog_direct_init_file(bflog_direct_t *direct, const char *path) { _BFLOG_CHECK(direct != NULL, -1); _BFLOG_CHECK(path != NULL, -1); char fullpath[256]; if (direct->lock()) { return -1; } if (direct->type != BFLOG_DIRECT_TYPE_FILE) { direct->unlock(); return -1; } if (direct->status != BFLOG_DIRECT_STATUS_INIT) { direct->unlock(); return -1; } size_t pathsize = strlen(path); bflogc_memcpy(fullpath, path, pathsize); bflogc_snprintf(fullpath + pathsize, 16, ".log"); _direct_file_t(direct)->fp = bflogc_fopen(fullpath, "a+"); if (_direct_file_t(direct)->fp == NULL) { direct->unlock(); return -1; } _direct_file_t(direct)->path = path; direct->status = BFLOG_DIRECT_STATUS_READY; direct->write = bflog_direct_write_file; direct->unlock(); return 0; } /** * @brief * @param direct * @return int */ int bflog_direct_deinit_file(bflog_direct_t *direct) { _BFLOG_CHECK(direct != NULL, -1); if (direct->type != BFLOG_DIRECT_TYPE_FILE) { return -1; } if (direct->status != BFLOG_DIRECT_STATUS_READY) { return -1; } if (direct->lock()) { return -1; } if (bflogc_fclose(_direct_file_t(direct)->fp)) { direct->unlock(); return -1; } direct->status = BFLOG_DIRECT_STATUS_INIT; direct->write = NULL; _direct_file_t(direct)->fp = NULL; _direct_file_t(direct)->path = NULL; direct->unlock(); return 0; } #endif /** * @} */ /** @addtogroup BFLOG_DIRECT_FILE_TIME_API * @{ */ #if (defined(BFLOG_DIRECT_FILE_SIZE_ENABLE) || defined(BFLOG_DIRECT_FILE_TIME_ENABLE)) /** * @brief file rotate * @param direct file_size/time directed output */ static void file_rotate(bflog_direct_file_size_t *direct) { char oldpath[256]; char newpath[256]; void *fp; size_t pathsize = strlen(direct->path); bflogc_memcpy(oldpath, direct->path, pathsize); bflogc_memcpy(newpath, direct->path, pathsize); if (direct->fp) { bflogc_fclose(direct->fp); } for (int i = direct->keep - 1; i >= 0; i--) { bflogc_snprintf(oldpath + pathsize, 16, i ? "_%d.log" : ".log", i - 1); bflogc_snprintf(newpath + pathsize, 16, "_%d.log", i); fp = bflogc_fopen(newpath, "r"); if (fp != NULL) { bflogc_fclose(fp); bflogc_remove(newpath); } fp = bflogc_fopen(oldpath, "r"); if (fp != NULL) { bflogc_fclose(fp); bflogc_rename(oldpath, newpath); } } direct->fp = bflogc_fopen(oldpath, "a+"); } #endif #ifdef BFLOG_DIRECT_FILE_TIME_ENABLE /** * @brief write data to file * @param direct * @param ptr * @param size */ static void bflog_direct_write_file_time(bflog_direct_t *direct, void *ptr, uint16_t size) { _BFLOG_CHECK(direct != NULL, ); _BFLOG_CHECK(ptr != NULL, ); uint32_t timestamp; if (_direct_file_time_t(direct)->fp == NULL) { return; } timestamp = bflog_time(); if (timestamp > _direct_file_time_t(direct)->timestamp + _direct_file_time_t(direct)->interval) { file_rotate((void *)direct); _direct_file_time_t(direct)->timestamp = timestamp; } bflogc_fwrite(ptr, 1, size, _direct_file_size_t(direct)->fp); bflogc_fflush(_direct_file_size_t(direct)->fp); } /** * @brief * @param direct directed output * @param path file path * @param interval file rotate interval * @param keep max keep file count * @return int */ int bflog_direct_init_file_time(bflog_direct_t *direct, const char *path, uint32_t interval, uint32_t keep) { _BFLOG_CHECK(direct != NULL, -1); _BFLOG_CHECK(path != NULL, -1); _BFLOG_CHECK(interval >= BFLOG_FILE_INTERVAL_MIN, -1); _BFLOG_CHECK(keep > 0, -1); if (interval < BFLOG_FILE_INTERVAL_MIN) { interval = BFLOG_FILE_INTERVAL_MIN; } char fullpath[256]; if (direct->lock()) { return -1; } if (direct->type != BFLOG_DIRECT_TYPE_FILE_TIME) { direct->unlock(); return -1; } if (direct->status != BFLOG_DIRECT_STATUS_INIT) { direct->unlock(); return -1; } size_t pathsize = strlen(path); bflogc_memcpy(fullpath, path, pathsize); bflogc_snprintf(fullpath + pathsize, 16, ".log"); _direct_file_time_t(direct)->fp = bflogc_fopen(fullpath, "a+"); if (_direct_file_time_t(direct)->fp == NULL) { direct->unlock(); return -1; } _direct_file_time_t(direct)->timestamp = bflog_time(); _direct_file_time_t(direct)->keep = keep; _direct_file_time_t(direct)->interval = interval; _direct_file_time_t(direct)->path = path; direct->status = BFLOG_DIRECT_STATUS_READY; direct->write = bflog_direct_write_file_time; direct->unlock(); return 0; } /** * @brief * @param direct * @return int */ int bflog_direct_deinit_file_time(bflog_direct_t *direct) { _BFLOG_CHECK(direct != NULL, -1); if (direct->type != BFLOG_DIRECT_TYPE_FILE_TIME) { return -1; } if (direct->status != BFLOG_DIRECT_STATUS_READY) { return -1; } if (direct->lock()) { return -1; } if (bflogc_fclose(_direct_file_time_t(direct)->fp)) { direct->unlock(); return -1; } direct->status = BFLOG_DIRECT_STATUS_INIT; direct->write = NULL; _direct_file_time_t(direct)->fp = NULL; _direct_file_time_t(direct)->path = NULL; _direct_file_time_t(direct)->keep = 0; direct->unlock(); return 0; } #endif /** * @} */ /** @addtogroup BFLOG_DIRECT_FILE_SIZE_API * @{ */ #ifdef BFLOG_DIRECT_FILE_SIZE_ENABLE /** * @brief write data to file * @param direct * @param ptr * @param size */ static void bflog_direct_write_file_size(bflog_direct_t *direct, void *ptr, uint16_t size) { _BFLOG_CHECK(direct != NULL, ); _BFLOG_CHECK(ptr != NULL, ); if (_direct_file_size_t(direct)->fp == NULL) { return; } /*!< fseek(_direct_file_size_t(direct)->fp, 0L, SEEK_END); */ size_t fsize = bflogc_ftell(_direct_file_size_t(direct)->fp); if (fsize > _direct_file_size_t(direct)->size) { file_rotate((void *)direct); } bflogc_fwrite(ptr, 1, size, _direct_file_size_t(direct)->fp); bflogc_fflush(_direct_file_size_t(direct)->fp); } /** * @brief * @param direct * @param path * @param size * @param keep * @return int */ int bflog_direct_init_file_size(bflog_direct_t *direct, const char *path, uint32_t size, uint32_t keep) { _BFLOG_CHECK(direct != NULL, -1); _BFLOG_CHECK(path != NULL, -1); _BFLOG_CHECK(keep > 0, -1); if (size < BFLOG_FILE_SIZE_MIN) { size = BFLOG_FILE_SIZE_MIN; } char fullpath[256]; if (direct->lock()) { return -1; } if (direct->type != BFLOG_DIRECT_TYPE_FILE_SIZE) { direct->unlock(); return -1; } if (direct->status != BFLOG_DIRECT_STATUS_INIT) { direct->unlock(); return -1; } size_t pathsize = strlen(path); bflogc_memcpy(fullpath, path, pathsize); bflogc_snprintf(fullpath + pathsize, 16, ".log"); _direct_file_size_t(direct)->fp = bflogc_fopen(fullpath, "a+"); if (_direct_file_size_t(direct)->fp == NULL) { direct->unlock(); return -1; } _direct_file_size_t(direct)->keep = keep; _direct_file_size_t(direct)->size = size; _direct_file_size_t(direct)->path = path; direct->status = BFLOG_DIRECT_STATUS_READY; direct->write = bflog_direct_write_file_size; direct->unlock(); return 0; } /** * @brief * @param direct * @return int */ int bflog_direct_deinit_file_size(bflog_direct_t *direct) { _BFLOG_CHECK(direct != NULL, -1); if (direct->type != BFLOG_DIRECT_TYPE_FILE_SIZE) { return -1; } if (direct->status != BFLOG_DIRECT_STATUS_READY) { return -1; } if (direct->lock()) { return -1; } if (bflogc_fclose(_direct_file_size_t(direct)->fp)) { direct->unlock(); return -1; } direct->status = BFLOG_DIRECT_STATUS_INIT; direct->write = NULL; _direct_file_size_t(direct)->fp = NULL; _direct_file_size_t(direct)->path = NULL; _direct_file_size_t(direct)->keep = 0; direct->unlock(); return 0; } #endif /** * @} */ /** * @} */ /** @addtogroup BFLOG_LAYOUT_API * @{ */ /** * @brief create layout, thread unsafe * @param layout layout * @param type layout type * @param color color enable * @return int */ int bflog_layout_create(bflog_layout_t *layout, uint8_t type) { _BFLOG_CHECK(layout != NULL, -1); _BFLOG_CHECK( type != BFLOG_LAYOUT_TYPE_SIMPLE || type != BFLOG_LAYOUT_TYPE_FORMAT || type != BFLOG_LAYOUT_TYPE_YAML, -1); switch (type) { case BFLOG_LAYOUT_TYPE_SIMPLE: layout->status = BFLOG_LAYOUT_STATUS_READY; break; case BFLOG_LAYOUT_TYPE_FORMAT: layout->status = BFLOG_LAYOUT_STATUS_INIT; break; case BFLOG_LAYOUT_TYPE_YAML: break; } layout->type = type; return 0; } /** * @brief delete layout, thread unsafe * @param layout layout * @return int */ int bflog_layout_delete(bflog_layout_t *layout) { _BFLOG_CHECK(layout != NULL, -1); layout->status = BFLOG_LAYOUT_STATUS_ILLEGAL; return 0; } /** * @brief set formatting of layout format, thread unsafe * @param layout layout * @param snprintf format * @return int */ int bflog_layout_format(bflog_layout_t *layout, int (*u_snprintf)(void *ptr, uint16_t size, char *color, char *level, char *tag, bflog_tm_t *tm, struct _bflog_msg *msg)) { _BFLOG_CHECK(layout != NULL, -1); _BFLOG_CHECK(u_snprintf != NULL, -1); if (layout->type != BFLOG_LAYOUT_TYPE_FORMAT) { return -1; } if (layout->status != BFLOG_LAYOUT_STATUS_INIT) { return -1; } _layout_format_t(layout)->snprintf = u_snprintf; layout->status = BFLOG_LAYOUT_STATUS_READY; return 0; } /** * @} */