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
encode_name(uint8_t level,const char * file,const char * func,const long line)244 uint16_t encode_name(uint8_t level, const char *file, const char *func, const long line)
245 {
246 uint16_t log_length = 0;
247 char line_num[APP_LOG_LINE_NB_LEN_MAX + 1] = { 0 };
248
249 // Encode file directory name , function name and lune number info.
250 if (app_log_is_fmt_set(level, APP_LOG_FMT_DIR | APP_LOG_FMT_FUNC | APP_LOG_FMT_LINE)) {
251 log_length += app_log_strcpy(log_length, s_log_encode_buf, "(");
252
253 if (app_log_is_fmt_set(level, APP_LOG_FMT_DIR)) {
254 log_length += app_log_strcpy(log_length, s_log_encode_buf, file);
255
256 if (app_log_is_fmt_set(level, APP_LOG_FMT_FUNC)) {
257 log_length += app_log_strcpy(log_length, s_log_encode_buf, " ");
258 } else if (app_log_is_fmt_set(level, APP_LOG_FMT_LINE)) {
259 log_length += app_log_strcpy(log_length, s_log_encode_buf, ":");
260 }
261 }
262
263 if (app_log_is_fmt_set(level, APP_LOG_FMT_FUNC)) {
264 log_length += app_log_strcpy(log_length, s_log_encode_buf, func);
265
266 if (app_log_is_fmt_set(level, APP_LOG_FMT_LINE)) {
267 log_length += app_log_strcpy(log_length, s_log_encode_buf, " Line:");
268 }
269 }
270
271 if (app_log_is_fmt_set(level, APP_LOG_FMT_LINE)) {
272 snprintf_s(line_num, sizeof (line_num), APP_LOG_LINE_NB_LEN_MAX, "%ld", line);
273 log_length += app_log_strcpy(log_length, s_log_encode_buf, line_num);
274 }
275
276 log_length += app_log_strcpy(log_length, s_log_encode_buf, ") ");
277 }
278 return log_length;
279 }
280
calculate_log_length(uint16_t log_length,int fmt_result)281 uint16_t calculate_log_length(uint16_t log_length, int fmt_result)
282 {
283 uint8_t newline_length = strlen(APP_LOG_NEWLINE_SIGN);
284 uint16_t log_len = log_length;
285 // Calculate log length
286 if ((fmt_result > -1) && (log_len + fmt_result) <= APP_LOG_LINE_BUF_SIZE) {
287 log_len += fmt_result;
288 } else {
289 log_len = APP_LOG_LINE_BUF_SIZE;
290 }
291
292 #if APP_LOG_COLOR_ENABLE
293 if (log_len + (sizeof(CSI_END) - 1) + newline_length > APP_LOG_LINE_BUF_SIZE) {
294 log_len = APP_LOG_LINE_BUF_SIZE;
295 // Reserve some space for CSI end sign.
296 log_len -= sizeof(CSI_END) - 1;
297 #else
298 if (log_len + newline_length > APP_LOG_LINE_BUF_SIZE) {
299 log_len = APP_LOG_LINE_BUF_SIZE;
300 #endif
301 log_len -= newline_length;
302 }
303
304 #if APP_LOG_COLOR_ENABLE
305 // Encode CSI end sign.
306 log_len += app_log_strcpy(log_len, s_log_encode_buf, CSI_END);
307 #endif
308
309 // Encode newline sign.
310 log_len += app_log_strcpy(log_len, s_log_encode_buf, APP_LOG_NEWLINE_SIGN);
311 return log_len;
312 }
313
314 void app_log_output(uint8_t level, const char *tag, const char *file, const char *func, const long line,
315 const char *format, ...)
316 {
317 uint16_t log_length = 0;
318 int fmt_result = 0;
319 va_list ap;
320
321 if (level > s_app_log_env.app_log_init.filter.level && s_app_log_env.is_filter_set) {
322 return;
323 }
324 #if APP_LOG_TAG_ENABLE
325 if (!strstr(tag, s_app_log_env.app_log_init.filter.tag)) {
326 return;
327 }
328 #endif
329 va_start(ap, format);
330 APP_LOG_LOCK();
331
332 #if APP_LOG_COLOR_ENABLE
333 // Encode CSI start sign and color info.
334 log_length += app_log_strcpy(log_length, s_log_encode_buf, CSI_START);
335 log_length += app_log_strcpy(log_length, s_log_encode_buf, s_log_color_output_info[level]);
336 #endif
337
338 // Encode level info.
339 if (app_log_is_fmt_set(level, APP_LOG_FMT_LVL)) {
340 log_length += app_log_strcpy(log_length, s_log_encode_buf, s_log_svt_lvl_output_info[level]);
341 }
342
343 #if APP_LOG_TAG_ENABLE
344 // Encode tag info.
345 if (app_log_is_fmt_set(level, APP_LOG_FMT_TAG)) {
346 log_length += app_log_strcpy(log_length, s_log_encode_buf, tag);
347 log_length += app_log_strcpy(log_length, s_log_encode_buf, " ");
348 }
349 #endif
350 log_length = encode_name(level, file, func, line);
351 // Encode other log data to buffer. '\0' must be added in the end by vsnprintf. */
352 fmt_result = vsnprintf_s((char *)s_log_encode_buf + log_length, sizeof (s_log_encode_buf),
353 APP_LOG_LINE_BUF_SIZE - log_length, format, ap);
354
355 va_end(ap);
356 log_length = calculate_log_length(log_length, fmt_result);
357 app_log_data_trans(s_log_encode_buf, log_length);
358 APP_LOG_UNLOCK();
359 }
360
361 void app_log_raw_info(const char *format, ...)
362 {
363 int fmt_result = 0;
364 uint16_t log_length = 0;
365 va_list ap;
366
367 va_start(ap, format);
368
369 APP_LOG_LOCK();
370
371 fmt_result = vsnprintf_s((char *)s_log_encode_buf, sizeof(s_log_encode_buf), APP_LOG_LINE_BUF_SIZE, format, ap);
372 if ((fmt_result > -1) && (fmt_result) <= APP_LOG_LINE_BUF_SIZE) {
373 log_length = fmt_result;
374 } else {
375 log_length = APP_LOG_LINE_BUF_SIZE;
376 }
377
378 app_log_data_trans(s_log_encode_buf, log_length);
379
380 APP_LOG_UNLOCK();
381 }
382
383 void app_log_hex_dump(uint8_t *p_data, uint16_t length)
384 {
385 uint16_t log_length = 0;
386 uint16_t convert_idx = 0;
387 char dump_str[8] = {0};
388
389 APP_LOG_LOCK();
390
391 for (convert_idx = 0; convert_idx < length; convert_idx++) {
392 if (log_length >= APP_LOG_LINE_BUF_SIZE) {
393 log_length = APP_LOG_LINE_BUF_SIZE;
394 break;
395 }
396
397 if (p_data[convert_idx] < ' ') {
398 s_log_encode_buf[log_length] = '.';
399 log_length++;
400 } else {
401 snprintf_s(dump_str, sizeof (dump_str), BIT_8, "%02X ", p_data[convert_idx]);
402 log_length += app_log_strcpy(log_length, s_log_encode_buf, dump_str);
403 }
404 }
405
406 app_log_data_trans(s_log_encode_buf, log_length);
407
408 APP_LOG_UNLOCK();
409 }
410
411 void app_log_flush(void)
412 {
413 if (s_app_log_env.flush_func) {
414 s_app_log_env.flush_func();
415 }
416 }
417
418 #if IO_REDIRECT == 0
419 #if defined(__CC_ARM)
420
421 struct __FILE {
422 int handle;
423 };
424
425 FILE s_stdout;
426 FILE s_stdin;
427
428 int fputc(int ch, FILE *file)
429 {
430 app_log_data_trans((uint8_t *)&ch, 1);
431
432 return 1;
433 }
434
435 #elif defined(__GNUC__)
436
437 int _write(int file, const char *buf, int len)
438 {
439 int tx_len = 0;
440 char *temp_buf = buf;
441
442 while (tx_len < len) {
443 app_log_data_trans((uint8_t *)buf, 1);
444 temp_buf++;
445 tx_len++;
446 }
447 return tx_len;
448 }
449
450 #elif defined(__ICCARM__)
451
452 size_t s_write(int handle, const unsigned char *buf, size_t size)
453 {
454 size_t len = 0;
455 unsigned char* temp_buf = buf;
456
457 while (len < size) {
458 app_log_data_trans((uint8_t *)buf, 1);
459 temp_buf++;
460 len++;
461 }
462 return len;
463 }
464
465 #endif /* defined(__CC_ARM) */
466 #endif
467
468