1
0
mirror of https://github.com/NevermindZZT/letter-shell.git synced 2025-01-01 09:58:41 +08:00

新增 日志组件

This commit is contained in:
Letter 2020-08-02 13:07:27 +08:00
parent 0532f7529b
commit 9015128b06
4 changed files with 607 additions and 0 deletions

BIN
doc/img/log_preview.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

202
extensions/log/log.c Normal file
View File

@ -0,0 +1,202 @@
/**
* @file log.c
* @author Letter (nevermindzzt@gmail.com)
* @brief log
* @version 1.0.0
* @date 2020-07-30
*
* @copyright (c) 2020 Letter
*
*/
#include "log.h"
#include "stdio.h"
#include "stdarg.h"
#include "shell.h"
#if LOG_USING_COLOR == 1
#define memPrintHead CSI(31) \
" Offset: 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F" \
CSI(39)
#define memPrintAddr CSI(31)"0x%08x: "CSI(39)
#else
#define memPrintHead " Offset: 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F"
#define memPrintAddr "0x%08x: "
#endif
Log *logList[LOG_MAX_NUMBER] = {0};
/**
* @brief log对象
*
* @param log log对象
*/
void logRegister(Log *log, Shell *shell)
{
#if SHELL_USING_COMPANION == 1
if (shell)
{
log->shell = shell;
shellCompanionAdd(shell, SHELL_COMPANION_ID_LOG, log);
}
#endif
for (short i = 0; i < LOG_MAX_NUMBER; i++)
{
if (logList[i] == 0)
{
logList[i] = log;
return;
}
}
}
/**
* @brief log对象
*
* @param log log对象
*/
void logUnRegister(Log *log)
{
for (short i = 0; i < LOG_MAX_NUMBER; i++)
{
if (logList[i] == log)
{
logList[i] = 0;
return;
}
}
}
/**
* @brief log日志级别
*
* @param log log对象
* @param level
*/
void logSetLevel(Log *log, LogLevel level)
{
logAssert(log, return);
log->level = level;
}
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC),
logSetLevel, logSetLevel, set log level);
/**
* @brief log格式化写入数据
*
* @param log log对象
* @param level log级别
* @param fmt
* @param ...
*/
void logWrite(Log *log, LogLevel level, char *fmt, ...)
{
char buffer[LOG_BUFFER_SIZE];
va_list vargs;
short len;
va_start(vargs, fmt);
len = vsnprintf(buffer, LOG_BUFFER_SIZE - 1, fmt, vargs);
va_end(vargs);
if (log == LOG_ALL_OBJ)
{
for (short i = 0; i < LOG_MAX_NUMBER; i++)
{
if (logList[i]
&& logList[i]->active
&& logList[i]->level >= level)
{
logList[i]->write(buffer, len);
}
}
}
else if (log && log->active && log->level >= level)
{
log->write(buffer, len);
}
}
/**
* @brief 16
*
* @param log log对象
* @param base
* @param length
*/
void logHexDump(Log *log, void *base, unsigned int length)
{
unsigned char *address;
unsigned int len = length;
if (length == 0)
{
return;
}
logWrite(log, LOG_NONE, "memory of 0x%08x, size: %d:\r\n", (unsigned int)base, length);
address = (unsigned char *)((unsigned int)base & (~0x0000000F));
length += (unsigned int)base - (unsigned int)address;
length = (length + 15) & (~0x0000000F);
logWrite(log, LOG_NONE, memPrintHead"\r\n");
while (length)
{
logWrite(log, LOG_NONE, memPrintAddr, (unsigned int)address);
for (int i = 0; i < 16; i++)
{
if ((unsigned int)(address + i) < (unsigned int)base
|| (unsigned int)(address + i) >= (unsigned int)base + len)
{
logWrite(log, LOG_NONE, " ");
}
else
{
logWrite(log, LOG_NONE, "%02x ", *(address + i));
}
}
logWrite(log, LOG_NONE, "| ");
for (int i = 0; i < 16; i++)
{
if ((unsigned int)(address + i) < (unsigned int)base
|| (unsigned int)(address + i) >= (unsigned int)base + len)
{
logWrite(log, LOG_NONE, " ");
}
else
{
if (*(address + i) >= 32 && *(address + i) <= 126)
{
logWrite(log, LOG_NONE, "%c", *(address + i));
}
else
{
logWrite(log, LOG_NONE, ".");
}
}
}
logWrite(log, LOG_NONE, " |\r\n");
address += 16;
length -= 16;
}
}
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC),
hexdump, logHexDump, hex dump\r\n hexdump [log] [base] [len]);
#if SHELL_USING_COMPANION == 1
void logSwitchLevel(Shell *shell)
{
Log *log = shellCompanionGet(shell, SHELL_COMPANION_ID_LOG);
SHELL_ASSERT(log, return);
log->level = (LogLevel)(log->level >= LOG_ALL ? LOG_NONE : (log->level + 1));
logPrintln("set log level: %d", log->level);
}
SHELL_EXPORT_KEY(SHELL_CMD_PERMISSION(0), 0x04000000, logSwitchLevel, switch log level);
#endif /** SHELL_USING_COMPANION == 1 */

191
extensions/log/log.h Normal file
View File

@ -0,0 +1,191 @@
/**
* @file log.h
* @author Letter (nevermindzzt@gmail.com)
* @brief log
* @version 1.0.0
* @date 2020-07-30
*
* @copyright (c) 2020 Letter
*
*/
#ifndef __LOG_H__
#define __LOG_H__
#include "shell.h"
#define LOG_VERSION "1.0.0"
#define SHELL_COMPANION_ID_LOG -2
#define LOG_BUFFER_SIZE 256 /**< log输出缓冲大小 */
#define LOG_USING_COLOR 1 /**< 是否使用颜色 */
#define LOG_MAX_NUMBER 5 /**< 允许注册的最大log对象数量 */
#define LOG_AUTO_TAG 1 /**< 是否自动添加TAG */
#define LOG_END "\r\n" /**< log信息结尾 */
#define LOG_TAG __FUNCTION__ /**< 自定添加的TAG */
#define LOG_TIME_STAMP 0 /**< 设置获取系统时间戳 */
#ifndef LOG_ENABLE
#define LOG_ENABLE 1 /**< 使能log */
#endif
#define LOG_ALL_OBJ ((Log *)-1) /**< 所有已注册的log对象 */
/**
*
*/
#define CSI_BLACK 30 /**< 黑色 */
#define CSI_RED 31 /**< 红色 */
#define CSI_GREEN 32 /**< 绿色 */
#define CSI_YELLOW 33 /**< 黄色 */
#define CSI_BLUE 34 /**< 蓝色 */
#define CSI_FUCHSIN 35 /**< 品红 */
#define CSI_CYAN 36 /**< 青色 */
#define CSI_WHITE 37 /**< 白色 */
#define CSI_BLACK_L 90 /**< 亮黑 */
#define CSI_RED_L 91 /**< 亮红 */
#define CSI_GREEN_L 92 /**< 亮绿 */
#define CSI_YELLOW_L 93 /**< 亮黄 */
#define CSI_BLUE_L 94 /**< 亮蓝 */
#define CSI_FUCHSIN_L 95 /**< 亮品红 */
#define CSI_CYAN_L 96 /**< 亮青 */
#define CSI_WHITE_L 97 /**< 亮白 */
#define CSI_DEFAULT 39 /**< 默认 */
#define CSI(code) "\033["#code"m" /**< ANSI CSI指令 */
/**
* log级别字符()
*/
#if LOG_USING_COLOR == 1
#define ERROR_TEXT CSI(31)"E(%d) %s:"CSI(39) /**< 错误标签 */
#define WARNING_TEXT CSI(33)"W(%d) %s:"CSI(39) /**< 警告标签 */
#define INFO_TEXT CSI(32)"I(%d) %s:"CSI(39) /**< 信息标签 */
#define DEBUG_TEXT CSI(34)"D(%d) %s:"CSI(39) /**< 调试标签 */
#define VERBOSE_TEXT CSI(36)"V(%d) %s:"CSI(39) /**< 冗余信息标签 */
#else
#define ERROR_TEXT "E(%d) %s:"
#define WARNING_TEXT "W(%d) %s:"
#define INFO_TEXT "I(%d) %s:"
#define DEBUG_TEXT "D(%d) %s:"
#define VERBOSE_TEXT "V(%d) %s:"
#endif
/**
* @brief
*
*/
typedef enum
{
LOG_NONE = 0, /**< 无级别 */
LOG_ERROR = 1, /**< 错误 */
LOG_WRANING = 2, /**< 警告 */
LOG_INFO = 3, /**< 消息 */
LOG_DEBUG = 4, /**< 调试 */
LOG_VERBOSE = 5, /**< 冗余 */
LOG_ALL = 6, /**< 所有日志 */
} LogLevel;
/**
* @brief log对象定义
*
*/
typedef struct
{
void (*write)(char *, short); /**< 写buffer */
char active; /**< 是否激活 */
LogLevel level; /**< 日志级别 */
Shell *shell; /**< 关联shell对象 */
} Log;
/**
* @brief log打印()
*
* @param fmt
* @param ...
*/
#define logPrintln(format, ...) \
logWrite(LOG_ALL_OBJ, LOG_NONE, format"\r\n", ##__VA_ARGS__)
/**
* @brief
*
* @param text
* @param level
* @param fmt
* @param ...
*/
#define logFormat(text, level, fmt, ...) \
if (LOG_ENABLE) {\
logWrite(LOG_ALL_OBJ, level, text" "fmt""LOG_END, \
LOG_TIME_STAMP, LOG_TAG, ##__VA_ARGS__); }
/**
* @brief log输出
*
* @param fmt
* @param ...
*/
#define logError(fmt, ...) \
logFormat(ERROR_TEXT, LOG_ERROR, fmt, ##__VA_ARGS__)
/**
* @brief log输出
*
* @param fmt
* @param ...
*/
#define logWarning(fmt, ...) \
logFormat(WARNING_TEXT, LOG_WRANING, fmt, ##__VA_ARGS__)
/**
* @brief log输出
*
* @param fmt
* @param ...
*/
#define logInfo(fmt, ...) \
logFormat(INFO_TEXT, LOG_INFO, fmt, ##__VA_ARGS__)
/**
* @brief log输出
*
* @param fmt
* @param ...
*/
#define logDebug(fmt, ...) \
logFormat(DEBUG_TEXT, LOG_DEBUG, fmt, ##__VA_ARGS__)
/**
* @brief log输出
*
* @param fmt
* @param ...
*/
#define logVerbose(fmt, ...) \
logFormat(VERBOSE_TEXT, LOG_VERBOSE, fmt, ##__VA_ARGS__)
/**
* @brief
*
* @param expr
* @param action
*/
#define logAssert(expr, action) \
if (!(expr)) { \
logError("\"" #expr "\" assert failed at file: %s, line: %d", __FILE__, __LINE__); \
action; \
}
void logRegister(Log *log, Shell *shell);
void logUnRegister(Log *log);
void logSetLevel(Log *log, LogLevel level);
void logWrite(Log *log, LogLevel level, char *fmt, ...);
void logHexDump(Log *log, void *base, unsigned int length);
#endif

214
extensions/log/readme.md Normal file
View File

@ -0,0 +1,214 @@
# log
![version](https://img.shields.io/badge/version-1.0.0-brightgreen.svg)
![standard](https://img.shields.io/badge/standard-c99-brightgreen.svg)
![build](https://img.shields.io/badge/build-2020.08.02-brightgreen.svg)
![license](https://img.shields.io/badge/license-MIT-brightgreen.svg)
嵌入式日志打印工具
![preview](../../doc/img/log_preview.png)
- [log](#log)
- [简介](#简介)
- [使用](#使用)
- [配置](#配置)
- [API](#api)
- [logPrintln](#logprintln)
- [logError](#logerror)
- [logWarning](#logwarning)
- [logInfo](#loginfo)
- [logDebug](#logdebug)
- [logVerbose](#logverbose)
- [logAssert](#logassert)
- [logRegister](#logregister)
- [logSetLevel](#logsetlevel)
- [logHexDump](#loghexdump)
- [结合letter shell尾行模式](#结合letter-shell尾行模式)
- [其他用法](#其他用法)
- [单独控制某个文件日志](#单独控制某个文件日志)
## 简介
log是一个用于嵌入式系统的日志打印工具可以为日志定义不同的级别然后设置日志工具的打印级别可以进行日志打印的控制
此外log通过letter shell的伴生对象功能可以和letter shell结合实现log和shell的绑定等功能
## 使用
1. 实现log写buffer函数
```C
void uartLogWrite(char *buffer, short len)
{
serialTransmit(&debugSerial, (uint8_t *)buffer, len, 0x100);
}
```
2. 定义log对象
```C
Log uartLog = {
.write = uartLogWrite,
.active = true,
.level = LOG_DEBUG
};
```
3. 注册log对象
```C
logRegister(&uartLog, NULL);
```
## 配置
通过修改log.h文件中的宏可以对log工具进行配置
| 宏 | 意义 |
| --------------- | ------------------------- |
| LOG_BUFFER_SIZE | log输出缓冲大小 |
| LOG_USING_COLOR | 是否使用颜色 |
| LOG_MAX_NUMBER | 允许注册的最大log对象数量 |
| LOG_AUTO_TAG | 是否自动添加TAG |
| LOG_END | log信息结尾 |
| LOG_TAG | 自定添加的TAG |
| LOG_TIME_STAMP | 设置获取系统时间戳 |
## API
以下是log工具部分API的说明
### logPrintln
宏声明,用于一般的打印输出
```C
#define logPrintln(format, ...)
```
- 参数
- `format` 输出格式
- `...` 可变参数
### logError
宏声明,错误日志级别输出
```C
#define logError(fmt, ...)
```
- 参数
- `fmt` 输出格式
- `...` 可变参数
### logWarning
宏声明,警告日志级别输出,函数原型及参数说明参考`logError`
### logInfo
宏声明,信息日志级别输出,函数原型及参数说明参考`logError`
### logDebug
宏声明,调试日志级别输出,函数原型及参数说明参考`logError`
### logVerbose
宏声明,冗余日志级别输出,函数原型及参数说明参考`logError`
### logAssert
宏声明,断言
```C
#define logAssert(expr, action)
```
- 参数
- `expr` 表达式
- `action` 断言失败执行操作
### logRegister
注册log对象
```C
void logRegister(Log *log, Shell *shell)
```
- 参数
- `log` log对象
- `shell` 关联的shell对象
### logSetLevel
设置日志级别
```C
void logSetLevel(Log *log, LogLevel level)
```
- 参数
- `log` log对象
- `level` 日志级别
### logHexDump
数据16进制打印
```C
void logHexDump(Log *log, void *base, unsigned int length)
```
- 参数
- `log` log对象
- `base` 数据基址
- `length` 数据长度
## 结合letter shell尾行模式
log工具可以结合letter shell的尾行模式实现log和shell共用一个终端但不影响shell交互体验
1. 使用`shellWriteEndLine`事项log写buffer函数
```C
void uartLogWrite(char *buffer, short len)
{
if (uartLog.shell)
{
shellWriteEndLine(uartLog.shell, buffer, len);
}
}
```
2. 定义log对象
```C
Log uartLog = {
.write = uartLogWrite,
.active = true,
.level = LOG_DEBUG
};
```
3. 注册log对象
```C
logRegister(&uartLog, &shell);
```
## 其他用法
### 单独控制某个文件日志
log工具可以单独对某个文件的日志进行打印控制使用时在对应的.c文件中加入
```C
#undef LOG_ENABLE
#define LOG_ENABLE 1
```
即可单独控制某个文件的日志开关