EasyLogger | Una biblioteca de registro ligera y de alto rendimiento

Columnas seleccionadas de proyectos de código abierto integrados

Esta columna fue creada por Mculover666, el contenido principal es encontrar proyectos de código abierto de alta calidad en el campo incrustado, uno es ayudar a los desarrolladores a usar proyectos de código abierto para lograr más funciones, y el otro es aprender el código y las ideas de implementación detrás de estos proyectos de código abierto Para mejorar su propio nivel de código, en comparación con otras columnas, las ventajas de esta columna son:

No solo presenta el proyecto para compartir, sino que también incluye el proceso de compartir la práctica personal del autor, e incluso una interpretación de las ideas de diseño detrás de él .

Los proyectos de código abierto actualmente incluidos en esta columna son:

Si el proyecto de código abierto que escribió o encontró es bueno, ¡bienvenido a dejar un mensaje o un mensaje privado en esta columna para compartir el doble de felicidad!

1. EasyLogger

El proyecto de código abierto que se le presentó en este número es EasyLogger, una biblioteca de registro ligera y de alto rendimiento , autor armink, que actualmente cosecha 1,1K estrellas, siguiendo el acuerdo de licencia de código abierto del MIT.

EasyLogger es una biblioteca de registro C / C ++ ultraligera y de alto rendimiento, que es muy adecuada para proyectos de software sensibles a los recursos. Por el contrario, EasyLogger tiene funciones más simples, menos interfaces para los usuarios y será más rápido comenzar. , Las funciones más prácticas admiten la expansión dinámica en forma de complementos.

Actualmente EasyLogger admite las siguientes funciones:

  • El modo de salida de registro admite puerto serie, flash, archivo, etc.
  • El contenido del registro puede incluir nivel, marca de tiempo, información de subprocesos, información de proceso, etc.
  • Admite múltiples sistemas operativos, admite metal desnudo;
  • Los diferentes niveles de registros admiten diferentes colores;

Dirección del proyecto: https://github.com/armink/EasyLogger

2. Puerto EasyLogger

2.1. Portar ideas

Durante el proceso de migración, hay dos referencias principales: el documento Léame del proyecto y el proyecto de demostración.

Para estos proyectos de código abierto, de hecho, solo hay dos pasos para portar:

  • ① Agregar código fuente al proyecto de metal desnudo;
  • ② La interfaz requerida se puede realizar;

2.2. Preparación del proyecto de metal desnudo

En este artículo, estoy usando el kit de desarrollo IoT de Winnie the Pooh. El chip de control principal es STM32L431RCT6:

antes de trasplantar, necesito preparar un proyecto de metal desnudo. Uso STM32CubeMX para generar, usar el método de consulta USART1 para enviar datos y redirigir printf a USART1 . El proceso específico Por favor refiérase a:

El puerto serie USART1 se configura de la siguiente manera:

después de que se genera el proyecto, el código de redirección de printf es el siguiente:

#include <stdio.h>

int fputc(int ch, FILE *stream)
{
    /* 堵塞判断串口是否发送完成 */
    while((USART1->ISR & 0X40) == 0);

    /* 串口发送完成,将该字符发送 */
    USART1->TDR = (uint8_t) ch;

    return ch;
}

Una vez que el proyecto de metal desnudo esté listo, se trasplantará easylogger.

2.3. Agregar elog al proyecto

① Copie el código fuente al proyecto:

② Agregue el archivo de código fuente del componente easylogge en keil:

  • port/elog_port.c: Archivo de interfaz del puerto Elog;
  • src/elog.c: Código fuente de la función central de elog;
  • src/elog_utils.c: Algunas implementaciones de funciones de herramientas de biblioteca c utilizadas por elog;
  • src/elog_buf.c(Adición opcional): código fuente del modo de salida del búfer elog;
  • src/elog_async.c(Adición opcional): código fuente del modo de salida asíncrono elog;

③ Agregue la easylogger/incruta del archivo de encabezado a keil:

2.4 Implemente el puerto elog

Se ha escrito el puerto de elog. En el elog_port.carchivo, solo necesita agregar código en el cuerpo de la función.

Interface interfaz de inicialización elog

ElogErrCode elog_port_init(void);

Si implica la inicialización posterior de los recursos utilizados por elog, como solicitar dinámicamente la asignación de memoria de búfer, puede colocarlo en esta interfaz y mantener el valor predeterminado en este artículo.

② interfaz de salida elog (énfasis)

//开头添加
#include <stdio.h>

……

//接口实现
void elog_port_output(const char *log, size_t size) {
	//日志使用printf输出,printf已经重定向到串口USART1
	printf("%.*s", size, log);
}

Aquí hay un pequeño punto de conocimiento, lo que %ssignifica que la salida de cadena .<十进制数>es un carácter de formato de control de precisión. Cuando se emiten caracteres, significa el número de caracteres de salida. Durante el control de precisión, el número decimal después del punto decimal se puede utilizar *para ocupar un lugar. El valor específico del control de precisión.

③ Interfaz de bloqueo / desbloqueo de salida de registro

Esta interfaz puede bloquear / desbloquear la interfaz de salida del registro para garantizar la exactitud del registro durante la salida concurrente. El programa básico se utiliza en este artículo, así que use la interrupción global para cerrar el bloqueo y la interrupción global para desbloquear:

//开头添加
#include <stm32l4xx_hal.h>

……

//接口实现
void elog_port_output_lock(void) {
    
    //关闭全局中断
	__set_PRIMASK(1);
  
}
void elog_port_output_unlock(void) {
    
    //开启全局中断
	__set_PRIMASK(0);
    
}

STM32 tiene muchas formas de cambiar las interrupciones globales. En este artículo, opere directamente el registro PRIMASK para proteger / abrir rápidamente las interrupciones globales.

https://blog.csdn.net/working24hours/article/details/88323241

④ Interfaz de adquisición de información del sistema

elog proporciona tres interfaces para obtener el tiempo actual, obtener el número de proceso y obtener el número de subproceso. Debido a que este artículo está portado al proyecto de metal desnudo y no proporciona soporte de tiempo, las tres interfaces devuelven cadenas vacías, de la siguiente manera:

const char *elog_port_get_time(void) {
    
	return "";
    
}
const char *elog_port_get_p_info(void) {

	return "";
    
}
const char *elog_port_get_t_info(void) {

	return "";
    
}

2.5. Configurar elog

Las funciones principales de elog, definición de macro y definición de macro de parámetro central se encuentran en el archivo de configuración elog_cfg.hEn este artículo, solo se describen las definiciones de macro importantes.

Interruptor maestro de salida de registro:

/* enable log output. */
#define ELOG_OUTPUT_ENABLE

La definición de macro de nueva línea se modifica de la siguiente manera:

/* output newline sign */
#define ELOG_NEWLINE_SIGN                        "\r\n"

Interruptor de salida de registro con color:

/* enable log color */
#define ELOG_COLOR_ENABLE

El código fuente para la salida asíncrona y la salida del búfer no se agrega durante la migración, por lo tanto, apague estas dos funciones:

en este punto, la configuración de la migración está completa y puede comenzar a usarla felizmente.

3. Use easylogger

3.1. Inicializando elog

Elog debe inicializarse antes de su uso. Hay tres pasos en el proceso:
① Inicializar elog

ElogErrCode elog_init(void);

② Establecer formato de salida de registro

void elog_set_fmt(uint8_t level, size_t set);

El primer parámetro indica el formato de salida correspondiente al nivel de salida de registro establecido. Elija una de las siguientes definiciones de macro:

/* output log's level */
#define ELOG_LVL_ASSERT                      0
#define ELOG_LVL_ERROR                       1
#define ELOG_LVL_WARN                        2
#define ELOG_LVL_INFO                        3
#define ELOG_LVL_DEBUG                       4
#define ELOG_LVL_VERBOSE                     5

El segundo parámetro es el formato de salida del registro, dado por la enumeración, que puede combinarse y combinarse libremente:

/* all formats index */
typedef enum {
    ELOG_FMT_LVL    = 1 << 0, /**< level */
    ELOG_FMT_TAG    = 1 << 1, /**< tag */
    ELOG_FMT_TIME   = 1 << 2, /**< current time */
    ELOG_FMT_P_INFO = 1 << 3, /**< process info */
    ELOG_FMT_T_INFO = 1 << 4, /**< thread info */
    ELOG_FMT_DIR    = 1 << 5, /**< file directory and name */
    ELOG_FMT_FUNC   = 1 << 6, /**< function name */
    ELOG_FMT_LINE   = 1 << 7, /**< line number */
} ElogFmtIndex;

/* macro definition for all formats */
#define ELOG_FMT_ALL    (ELOG_FMT_LVL|ELOG_FMT_TAG|ELOG_FMT_TIME|ELOG_FMT_P_INFO|ELOG_FMT_T_INFO| ELOG_FMT_DIR|ELOG_FMT_FUNC|ELOG_FMT_LINE)

③ Iniciar elog

void elog_start(void);

A continuación, después de la función de inicialización usart1 en la función principal, escriba el código de inicialización elog antes de while (1):

/* USER CODE BEGIN 2 */
/* 初始化elog */
elog_init();

/* 设置每个级别的日志输出格式 */
//输出所有内容
elog_set_fmt(ELOG_LVL_ASSERT, ELOG_FMT_ALL);
//输出日志级别信息和日志TAG
elog_set_fmt(ELOG_LVL_ERROR, ELOG_FMT_LVL | ELOG_FMT_TAG);
elog_set_fmt(ELOG_LVL_WARN, ELOG_FMT_LVL | ELOG_FMT_TAG);
elog_set_fmt(ELOG_LVL_INFO, ELOG_FMT_LVL | ELOG_FMT_TAG);
//除了时间、进程信息、线程信息之外,其余全部输出
elog_set_fmt(ELOG_LVL_DEBUG, ELOG_FMT_ALL & ~(ELOG_FMT_TIME | ELOG_FMT_P_INFO | ELOG_FMT_T_INFO));
//输出所有内容
elog_set_fmt(ELOG_LVL_VERBOSE, ELOG_FMT_ALL);

/* 启动elog */
elog_start();

/* USER CODE END 2 */

3.2. Salida de registro de Elog

Cada nivel en elog tiene una forma completa, dos formas simplificadas, puede elegir cuándo lo usa:

#define elog_assert(tag, ...) 
#define elog_a(tag, ...) //简化方式1,每次需填写 LOG_TAG
#define log_a(...)       //简化方式2,LOG_TAG 在文件顶部定义,使用前无需填写 LOG_TAG

#define elog_error(tag, ...)
#define elog_e(tag, ...)
#define log_e(...)

#define elog_warn(tag, ...)
#define elog_w(tag, ...)
#define log_w(...)

#define elog_info(tag, ...)
#define elog_i(tag, ...)
#define log_i(...)

#define elog_debug(tag, ...)
#define elog_d(tag, ...)
#define log_d(...)

#define elog_verbose(tag, ...)
#define elog_v(tag, ...)
#define log_v(...)

Los dos primeros solo necesitan incluir el <elog.h>archivo de encabezado al usarlo . Además del archivo de encabezado, el tercer método también necesita definir la definición de macro TAG al comienzo del archivo, que es lo mismo que printf, así que utilizo el tercer método Demo

Primero defina la macro TAG en el archivo main.c, incluido el archivo de encabezado:

/* USER CODE BEGIN Includes */
#define LOG_TAG    "main"

#include <elog.h>

/* USER CODE END Includes */

Luego, después del código de inicialización de elog escrito en la función principal, continúe agregando código para probar el uso de elog:

log_a("Hello EasyLogger!");
log_e("Hello EasyLogger!");
log_w("Hello EasyLogger!");
log_i("Hello EasyLogger!");
log_d("Hello EasyLogger!");
log_v("Hello EasyLogger!");

Compile, grabe y use el terminal en serie (Mobaxterm) para ver la salida en serie:

3.3. Salida colorida

Para elog_cfg.hhabilitar registros coloridos, no es suficiente habilitar la salida de color solo en . También debe habilitar la salida usando la API:

void elog_set_text_color_enabled(bool enabled);

Habilite la salida de color del texto al inicializar elog:

compile, descargue y vuelva a ver la salida:

el color de primer plano, el color de fondo y la fuente de cada nivel de registro se pueden elog_cfg.hmodificar en la definición de macro, el valor de la definición de macro elog.cse proporciona en Ver, por ejemplo, aquí estoy modificando el registro de nivel de ERROR para actualizar la fuente:

compilar, descargar, ver salida:

3.4 Uso de memoria antes y después de la migración

El proyecto básico antes de la migración solo tiene la función de transceptor usart1, y la comparación de memoria entre los dos después de trasplantar easylogger es la siguiente:

3.5 Características avanzadas de elog

Además de la función de registro básica, elog también proporciona algunas funciones avanzadas, como:

  • Función de filtrado de salida de registro: el registro se puede filtrar por nivel, TAG, palabras clave;
  • Modo de salida en búfer;
  • Modo de salida asíncrono;

La forma de utilizar estas funciones se describe en detalle en el documento Léame del proyecto. Este artículo se limita al espacio. Estas funciones avanzadas no se describen en detalle. Si le interesa en profundidad, puede estudiarlo usted mismo.

4. Interpretación de ideas de diseño.

4.1. Procesamiento de datos

La diferencia más básica entre usar el componente de impresión de registro y usar printf es que genera más información que es útil para la depuración, que puede entenderse como un procesamiento de los datos de salida.

La información del archivo, el nombre de la función y el número de línea donde se encuentra la declaración de impresión utiliza las funciones de las macros integradas del compilador :

  • __FILE__: Nombre de archivo
  • __FUNCTION__: Nombre de la función
  • __LINE__: Número de línea

La salida de caracteres de colores en el terminal es usar el código de escape ANSI, que es el código de control de la pantalla de secuencia de escape. Para obtener una explicación detallada y ejemplos de estos dos puntos de conocimiento, lea:

La función para procesar el contenido de salida en elog es:

/**
 * output the log
 *
 * @param level level
 * @param tag tag
 * @param file file name
 * @param func function name
 * @param line line number
 * @param format output format
 * @param ... args
 *
 */
void elog_output(uint8_t level, const char *tag, const char *file, const char *func,const long line, const char *format, ...) ;

4.2 Modo de salida de registro

Como dice el refrán, el maestro abre la puerta y la práctica es personal. Este artículo describe solo las funciones básicas del componente de impresión de registros. Usando printf para implementar directamente la interfaz de salida de registro, por lo que no hay diferencia entre el modo de salida de registro y la salida de printf, pero hay más información .

elog admite el modo de salida asíncrono . Cuando el modo de salida asíncrono está activado, mejorará la eficiencia de ejecución de las aplicaciones del usuario. Cuando la aplicación realiza la salida del registro, puede regresar directamente sin esperar a que el registro se envíe completamente .

elog también es compatible con el modo de salida almacenado en búfer Después de activar el modo de salida almacenado en búfer, si el búfer no está lleno, el subproceso de usuario puede regresar directamente sin esperar la salida completa del registro cuando sale el registro . Sin embargo, cuando el búfer de registro está lleno, ocupará subprocesos de usuario y generará automáticamente todos los registros en el búfer limpio.

Estos dos modos de salida de registro que no necesitan esperar y regresar directamente son muy importantes cuando se imprime una gran cantidad de información de registro. El código que imprime el registro tiene menos impacto en las aplicaciones normales.

5. Proyecto de ingeniería de adquisición de código fuente y comunicación de problemas

En la actualidad, cargué el código fuente de Easylogger y el código fuente del proyecto que trasplanté a la placa de desarrollo Xiongpai STM32L431RCT6 al grupo QQ (incluidas varias copias de la biblioteca HAL, y la velocidad relativa del QQ es más rápida). Se puede descargar en el grupo QQ. Intercambio, por supuesto, todos pueden compartir sus propios proyectos trasplantados en el grupo QQ :

ingrese el código QR del grupo QR:

Para recibir más artículos interesantes y recursos, suscríbase a mi cuenta pública de WeChat: "mculover666".

Publicado 277 artículos originales · elogiado 992 · 350,000 visitas

Supongo que te gusta

Origin blog.csdn.net/Mculover666/article/details/105371993
Recomendado
Clasificación