1 /**
2 *****************************************************************************************
3 *
4 * @file app_log.c
5 *
6 * @brief App Log Implementation.
7 *
8 *****************************************************************************************
9 * @attention
10 #####Copyright (c) 2019 GOODIX
11 All rights reserved.
12
13 Redistribution and use in source and binary forms, with or without
14 modification, are permitted provided that the following conditions are met:
15 * Redistributions of source code must retain the above copyright
16 notice, this list of conditions and the following disclaimer.
17 * Redistributions in binary form must reproduce the above copyright
18 notice, this list of conditions and the following disclaimer in the
19 documentation and/or other materials provided with the distribution.
20 * Neither the name of GOODIX nor the names of its contributors may be used
21 to endorse or promote products derived from this software without
22 specific prior written permission.
23
24 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE
28 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 POSSIBILITY OF SUCH DAMAGE.
35 *****************************************************************************************
36 */
37
38 /*
39 * INCLUDE FILES
40 *****************************************************************************************
41 */
42 #include <string.h>
43 #include <stdarg.h>
44 #include <stdio.h>
45 #include "app_log.h"
46
47 /*
48 * DEFINE
49 *****************************************************************************************
50 */
51 #if APP_LOG_COLOR_ENABLE
52 /**@brief CSI(Control Sequence Introducer/Initiator) sign more information on
53 * https://en.wikipedia.org/wiki/ANSI_escape_code. */
54 #define CSI_START "\033["
55 #define CSI_END "\033[0m"
56
57 /**@brief Output log front color */
58 #define F_BLACK "30;"
59 #define F_RED "31;"
60 #define F_GREEN "32;"
61 #define F_YELLOW "33;"
62 #define F_BLUE "34;"
63 #define F_MAGENTA "35;"
64 #define F_CYAN "36;"
65 #define F_WHITE "37;"
66
67 /**@brief Output log background color */
68 #define B_NULL
69 #define B_BLACK "40;"
70 #define B_RED "41;"
71 #define B_GREEN "42;"
72 #define B_YELLOW "43;"
73 #define B_BLUE "44;"
74 #define B_MAGENTA "45;"
75 #define B_CYAN "46;"
76 #define B_WHITE "47;"
77
78 /**@brief Output log fonts style */
79 #define S_BOLD "1m"
80 #define S_UNDERLINE "4m"
81 #define S_BLINK "5m"
82 #define S_NORMAL "22m"
83
84 /**@brief Output log default color definition: [front color] + [background color] + [show style] */
85 #ifndef APP_LOG_COLOR_ERROR
86 #define APP_LOG_COLOR_ERROR (F_YELLOW B_NULL S_NORMAL)
87 #endif
88
89 #ifndef APP_LOG_COLOR_WARNING
90 #define APP_LOG_COLOR_WARNING (F_CYAN B_NULL S_NORMAL)
91 #endif
92
93 #ifndef APP_LOG_COLOR_INFO
94 #define APP_LOG_COLOR_INFO (F_GREEN B_NULL S_NORMAL)
95 #endif
96
97 #ifndef APP_LOG_COLOR_DEBUG
98 #define APP_LOG_COLOR_DEBUG (F_WHITE B_NULL S_NORMAL)
99 #endif
100
101 #endif
102
103 #define BIT_8 8
104
105 /*
106 * STRUCTURES
107 *****************************************************************************************
108 */
109 /**@brief App log environment variable. */
110 struct app_log_env_t {
111 app_log_init_t app_log_init; /**< App log initialization variables. */
112 bool is_filter_set; /**< App log filter is set or not. */
113 app_log_trans_func_t trans_func; /**< App log transmit function. */
114 app_log_flush_func_t flush_func; /**< App log flush function. */
115 };
116
117 /*
118 * LOCAL VARIABLE DEFINITIONS
119 *****************************************************************************************
120 */
121 static uint8_t s_log_encode_buf[APP_LOG_LINE_BUF_SIZE]; /**< App log data encode buffer. */
122
123 static const char *s_log_svt_lvl_output_info[] = { /**< App log severity level outpout information. */
124 [APP_LOG_LVL_ERROR] = "APP_E: ",
125 [APP_LOG_LVL_WARNING] = "APP_W: ",
126 [APP_LOG_LVL_INFO] = "APP_I: ",
127 [APP_LOG_LVL_DEBUG] = "APP_D: ",
128 };
129
130 #if APP_LOG_COLOR_ENABLE
131 static const char *s_log_color_output_info[] = { /**< App log level outpout color information. */
132 [APP_LOG_LVL_ERROR] = APP_LOG_COLOR_ERROR,
133 [APP_LOG_LVL_WARNING] = APP_LOG_COLOR_WARNING,
134 [APP_LOG_LVL_INFO] = APP_LOG_COLOR_INFO,
135 [APP_LOG_LVL_DEBUG] = APP_LOG_COLOR_DEBUG,
136 };
137 #endif
138
139 static struct app_log_env_t s_app_log_env; /**< App log environment variable. */
140
141
142 /*
143 * LOCAL FUNCTION DEFINITIONS
144 *****************************************************************************************
145 */
146 /**
147 *****************************************************************************************
148 * @brief App log string copy.
149 *
150 * @param[in] wr_idx: Write index of app log buffer.
151 * @param[in] p_log_buff: Pointer to app log cache buffer.
152 * @param[in] p_log_data: Pointer to app log data.
153 *
154 * @return Length of copy.
155 *****************************************************************************************
156 */
app_log_strcpy(uint16_t wr_idx,uint8_t * p_log_buff,const char * p_log_data)157 static uint16_t app_log_strcpy(uint16_t wr_idx, uint8_t *p_log_buff, const char *p_log_data)
158 {
159 uint16_t cpy_length = 0;
160 char *temp_p_log_data = p_log_data;
161
162 if (!p_log_buff || !temp_p_log_data) {
163 return cpy_length;
164 }
165
166 while (*temp_p_log_data != 0) {
167 if ((wr_idx + cpy_length) < APP_LOG_LINE_BUF_SIZE) {
168 p_log_buff[wr_idx + cpy_length] = *temp_p_log_data++;
169 cpy_length++;
170 } else {
171 break;
172 }
173 }
174
175 return cpy_length;
176 }
177
178 /**
179 *****************************************************************************************
180 * @brief Check app log format is set or not.
181 *
182 * @param[in] level: App log level.
183 * @param[in] fmt: Format.
184 *
185 * @return Result of check.
186 *****************************************************************************************
187 */
app_log_is_fmt_set(uint8_t level,uint8_t fmt)188 static bool app_log_is_fmt_set(uint8_t level, uint8_t fmt)
189 {
190 if (s_app_log_env.app_log_init.fmt_set[level] & fmt) {
191 return true;
192 } else {
193 return false;
194 }
195 }
196
197 /**
198 *****************************************************************************************
199 * @brief Transmit app log data.
200 *
201 * @param[in] p_data: Pointer to log data.
202 * @param[in] length: Length of log data.
203 *
204 * @return Result of check.
205 *****************************************************************************************
206 */
app_log_data_trans(uint8_t * p_data,uint16_t length)207 static void app_log_data_trans(uint8_t *p_data, uint16_t length)
208 {
209 if (p_data == NULL || length == 0) {
210 return;
211 }
212
213 if (s_app_log_env.trans_func) {
214 s_app_log_env.trans_func(p_data, length);
215 }
216
217 #if APP_LOG_STORE_ENABLE
218 app_log_store_save(p_data, length);
219 #endif
220 }
221
222 /*
223 * GLOBAL FUNCTION DEFINITIONS
224 *****************************************************************************************
225 */
app_log_init(app_log_init_t * p_log_init,app_log_trans_func_t trans_func,app_log_flush_func_t flush_func)226 sdk_err_t app_log_init(app_log_init_t *p_log_init, app_log_trans_func_t trans_func, app_log_flush_func_t flush_func)
227 {
228 if (NULL == p_log_init) {
229 s_app_log_env.is_filter_set = false;
230 memset_s(&s_app_log_env.app_log_init, sizeof (s_app_log_env.app_log_init), 0, sizeof(app_log_init_t));
231 } else if (p_log_init->filter.level <= APP_LOG_LVL_DEBUG) {
232 s_app_log_env.is_filter_set = true;
233 memset_s(&s_app_log_env.app_log_init, sizeof (s_app_log_env.app_log_init), p_log_init, sizeof(app_log_init_t));
234 } else {
235 return SDK_ERR_INVALID_PARAM;
236 }
237
238 s_app_log_env.trans_func = trans_func;
239 s_app_log_env.flush_func = flush_func;
240
241 return SDK_SUCCESS;
242 }
243
app_log_output(uint8_t level,const char * tag,const char * file,const char * func,const long line,const char * format,...)244 void app_log_output(uint8_t level, const char *tag, const char *file, const char *func, const long line,
245 const char *format, ...)
246 {
247 uint16_t log_length = 0;
248 uint8_t newline_length = strlen(APP_LOG_NEWLINE_SIGN);
249 int fmt_result = 0;
250 char line_num[APP_LOG_LINE_NB_LEN_MAX + 1] = { 0 };
251 va_list ap;
252
253 if (level > s_app_log_env.app_log_init.filter.level && s_app_log_env.is_filter_set) {
254 return;
255 }
256
257 #if APP_LOG_TAG_ENABLE
258 if (!strstr(tag, s_app_log_env.app_log_init.filter.tag)) {
259 return;
260 }
261 #endif
262
263 va_start(ap, format);
264
265 APP_LOG_LOCK();
266
267 #if APP_LOG_COLOR_ENABLE
268 // Encode CSI start sign and color info.
269 log_length += app_log_strcpy(log_length, s_log_encode_buf, CSI_START);
270 log_length += app_log_strcpy(log_length, s_log_encode_buf, s_log_color_output_info[level]);
271 #endif
272
273 // Encode level info.
274 if (app_log_is_fmt_set(level, APP_LOG_FMT_LVL)) {
275 log_length += app_log_strcpy(log_length, s_log_encode_buf, s_log_svt_lvl_output_info[level]);
276 }
277
278 #if APP_LOG_TAG_ENABLE
279 // Encode tag info.
280 if (app_log_is_fmt_set(level, APP_LOG_FMT_TAG)) {
281 log_length += app_log_strcpy(log_length, s_log_encode_buf, tag);
282 log_length += app_log_strcpy(log_length, s_log_encode_buf, " ");
283 }
284 #endif
285
286 // Encode file directory name , function name and lune number info.
287 if (app_log_is_fmt_set(level, APP_LOG_FMT_DIR | APP_LOG_FMT_FUNC | APP_LOG_FMT_LINE)) {
288 log_length += app_log_strcpy(log_length, s_log_encode_buf, "(");
289
290 if (app_log_is_fmt_set(level, APP_LOG_FMT_DIR)) {
291 log_length += app_log_strcpy(log_length, s_log_encode_buf, file);
292
293 if (app_log_is_fmt_set(level, APP_LOG_FMT_FUNC)) {
294 log_length += app_log_strcpy(log_length, s_log_encode_buf, " ");
295 } else if (app_log_is_fmt_set(level, APP_LOG_FMT_LINE)) {
296 log_length += app_log_strcpy(log_length, s_log_encode_buf, ":");
297 }
298 }
299
300 if (app_log_is_fmt_set(level, APP_LOG_FMT_FUNC)) {
301 log_length += app_log_strcpy(log_length, s_log_encode_buf, func);
302
303 if (app_log_is_fmt_set(level, APP_LOG_FMT_LINE)) {
304 log_length += app_log_strcpy(log_length, s_log_encode_buf, " Line:");
305 }
306 }
307
308 if (app_log_is_fmt_set(level, APP_LOG_FMT_LINE)) {
309 snprintf_s(line_num, sizeof (line_num), APP_LOG_LINE_NB_LEN_MAX, "%ld", line);
310 log_length += app_log_strcpy(log_length, s_log_encode_buf, line_num);
311 }
312
313 log_length += app_log_strcpy(log_length, s_log_encode_buf, ") ");
314 }
315
316 // Encode other log data to buffer. '\0' must be added in the end by vsnprintf. */
317 fmt_result = vsnprintf_s((char *)s_log_encode_buf + log_length, sizeof (s_log_encode_buf),
318 APP_LOG_LINE_BUF_SIZE - log_length, format, ap);
319
320 va_end(ap);
321
322 // Calculate log length
323 if ((fmt_result > -1) && (log_length + fmt_result) <= APP_LOG_LINE_BUF_SIZE) {
324 log_length += fmt_result;
325 } else {
326 log_length = APP_LOG_LINE_BUF_SIZE;
327 }
328
329 #if APP_LOG_COLOR_ENABLE
330 if (log_length + (sizeof(CSI_END) - 1) + newline_length > APP_LOG_LINE_BUF_SIZE) {
331 log_length = APP_LOG_LINE_BUF_SIZE;
332 // Reserve some space for CSI end sign.
333 log_length -= sizeof(CSI_END) - 1;
334 #else
335 if (log_length + newline_length > APP_LOG_LINE_BUF_SIZE) {
336 log_length = APP_LOG_LINE_BUF_SIZE;
337 #endif
338 log_length -= newline_length;
339 }
340
341 #if APP_LOG_COLOR_ENABLE
342 // Encode CSI end sign.
343 log_length += app_log_strcpy(log_length, s_log_encode_buf, CSI_END);
344 #endif
345
346 // Encode newline sign.
347 log_length += app_log_strcpy(log_length, s_log_encode_buf, APP_LOG_NEWLINE_SIGN);
348
349 app_log_data_trans(s_log_encode_buf, log_length);
350
351 APP_LOG_UNLOCK();
352 }
353
354 void app_log_raw_info(const char *format, ...)
355 {
356 int fmt_result = 0;
357 uint16_t log_length = 0;
358 va_list ap;
359
360 va_start(ap, format);
361
362 APP_LOG_LOCK();
363
364 fmt_result = vsnprintf_s((char *)s_log_encode_buf, sizeof(s_log_encode_buf), APP_LOG_LINE_BUF_SIZE, format, ap);
365 if ((fmt_result > -1) && (fmt_result) <= APP_LOG_LINE_BUF_SIZE) {
366 log_length = fmt_result;
367 } else {
368 log_length = APP_LOG_LINE_BUF_SIZE;
369 }
370
371 app_log_data_trans(s_log_encode_buf, log_length);
372
373 APP_LOG_UNLOCK();
374 }
375
376 void app_log_hex_dump(uint8_t *p_data, uint16_t length)
377 {
378 uint16_t log_length = 0;
379 uint16_t convert_idx = 0;
380 char dump_str[8] = {0};
381
382 APP_LOG_LOCK();
383
384 for (convert_idx = 0; convert_idx < length; convert_idx++) {
385 if (log_length >= APP_LOG_LINE_BUF_SIZE) {
386 log_length = APP_LOG_LINE_BUF_SIZE;
387 break;
388 }
389
390 if (p_data[convert_idx] < ' ') {
391 s_log_encode_buf[log_length] = '.';
392 log_length++;
393 } else {
394 snprintf_s(dump_str, sizeof (dump_str), BIT_8, "%02X ", p_data[convert_idx]);
395 log_length += app_log_strcpy(log_length, s_log_encode_buf, dump_str);
396 }
397 }
398
399 app_log_data_trans(s_log_encode_buf, log_length);
400
401 APP_LOG_UNLOCK();
402 }
403
404 void app_log_flush(void)
405 {
406 if (s_app_log_env.flush_func) {
407 s_app_log_env.flush_func();
408 }
409 }
410
411 #if IO_REDIRECT == 0
412 #if defined(__CC_ARM)
413
414 struct __FILE {
415 int handle;
416 };
417
418 FILE s_stdout;
419 FILE s_stdin;
420
421 int fputc(int ch, FILE *file)
422 {
423 app_log_data_trans((uint8_t *)&ch, 1);
424
425 return 1;
426 }
427
428 #elif defined(__GNUC__)
429
430 int _write(int file, const char *buf, int len)
431 {
432 int tx_len = 0;
433 char *temp_buf = buf;
434
435 while (tx_len < len) {
436 app_log_data_trans((uint8_t *)buf, 1);
437 temp_buf++;
438 tx_len++;
439 }
440 return tx_len;
441 }
442
443 #elif defined(__ICCARM__)
444
445 size_t s_write(int handle, const unsigned char *buf, size_t size)
446 {
447 size_t len = 0;
448 unsigned char* temp_buf = buf;
449
450 while (len < size) {
451 app_log_data_trans((uint8_t *)buf, 1);
452 temp_buf++;
453 len++;
454 }
455 return len;
456 }
457
458 #endif /* defined(__CC_ARM) */
459 #endif
460
461