当前位置: 首页 > news >正文

嵌入式日志库ulog的使用和解析

嵌入式日志信息保存调试(ulog)

获取

项目地址:https://github.com/rdpoor/ulog

uLog 为嵌入式微控制器或任何资源有限的系统提供结构化的日志记录机制。它继承了流行的 Log4cLog4j 平台背后的一些概念,但开销更低。

使用方法

ulog中的ulog.culog.h文件移植进入自己的工程。

  • 打开使能ulog函数使能,在ulog.h中
#define ULOG_ENABLED
  • 日志输出等级
typedef enum {ULOG_TRACE_LEVEL=100,ULOG_DEBUG_LEVEL,ULOG_INFO_LEVEL,ULOG_WARNING_LEVEL,ULOG_ERROR_LEVEL,ULOG_CRITICAL_LEVEL,ULOG_ALWAYS_LEVEL
} ulog_level_t;
  • main函数分析

记住一个点:自定义里面的等级A,只有我们使用的ULOG_XXX函数中XXX的等级 >= A 的时候,该函数才会被真正的执行

#include <stdio.h>
#include <string.h>
#include "ulog.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>// 用户自定义用于调试的函数函数
void my_console_logger(ulog_level_t severity, char *msg)
{printf("console: %s [%s]: %s\n","time", // user defined functionulog_level_name(severity),msg);
}
// 用户自定义用于将日志存储进文件的函数
void my_file_logger(ulog_level_t severity, char *msg)
{int fd = open("file.txt",O_RWRD|O_CRATE,0655);printf(,"file: %s [%s]: %s\n","time", // user defined functionulog_level_name(severity),msg)close(fd);
}int main()
{int arg = 42;// ulog的初始化ULOG_INIT();// 订阅my_console_logger函数,给予相应的等级为 ULOG_WARNING_LEVELULOG_SUBSCRIBE(my_console_logger, ULOG_WARNING_LEVEL);// 订阅my_file_logger函数,给予相应的等级为 ULOG_DEBUG_LEVELULOG_SUBSCRIBE(my_file_logger, ULOG_DEBUG_LEVEL);// 只会执行 my_file_logger的函数,理由是 ULOG_WARNING_LEVEL > ULOG_INFO_LEVEL 的等级// 但是 ULOG_DEBUG_LEVEL < ULOG_INFO_LEVEL 的等级// 自定义函数相应等级只有小于等于的时候才会执行相应的操作函数ULOG_INFO("Info, arg=%d", arg);         // logs to file but not console// ULOG_WARNING_LEVEL > ULOG_DEBUG_LEVELULOG_CRITICAL("Critical, arg=%d", arg); // logs to file and console// 改变my_console_logger函数的操作等级为 ULOG_INFO_LEVELULOG_SUBSCRIBE(my_console_logger, ULOG_INFO_LEVEL);// 改变后两个自定义函数的操作等级都小于等于 ULOG_INFO_LEVEL,所以两个函数都会执行ULOG_INFO("Info, arg=%d", arg); // logs to file and console// 取消 my_file_logger 函数的订阅操作ULOG_UNSUBSCRIBE(my_file_logger);// 其中my_file_logger取消了,又 ULOG_INFO_LEVEL >= ULOG_INFO_LEVEL, 所以打印调试信息ULOG_INFO("Info, arg=%d", arg); 
}

自定义日志函数编写

  • windows
char* get_timestamp()
{time_t ti_t;struct tm s_tm;time(&ti_t);localtime_s(&s_tm, &ti_t);char* time_chr = (char*)malloc(sizeof(char)*20);memset(time_chr,0,20);sprintf(time_chr, "%d-%d-%d %d:%d:%d",s_tm.tm_year + 1900, s_tm.tm_mon + 1, s_tm.tm_mday, s_tm.tm_hour, s_tm.tm_min, s_tm.tm_sec);return time_chr;
}void my_console_logger(ulog_level_t severity, const char* msg) {char *get_time = get_timestamp();printf("%s [%s]: %s\n",get_time,    // user defined functionulog_level_name(severity),msg);free(get_time);
}
  • linux
char *get_timestamp()
{time_t tt;struct tm *t;time(&tt);char *time_chr = (char *)malloc(sizeof(char) * 20);memset(time_chr, 0, 20);t = localtime(&tt);sprintf(time_chr, "%d-%d-%d %d:%d:%d", t->tm_year+1900,t->tm_mon + 1,t->tm_mday,(t->tm_hour + 15)%24,t->tm_min,t->tm_sec);return time_chr;
}void my_console_logger(ulog_level_t severity, const char *msg)
{char *get_time = get_timestamp();printf("%s [%s]: %s\n",get_time, // user defined functionulog_level_name(severity),msg);free(get_time);
}void my_file_logger(ulog_level_t severity, const char *msg)
{char *get_time = get_timestamp();FILE* fp = fopen("my_test.txt", "a+"); // 打开文件my_test.txt 这里文件可以跟改为时间fprintf(fp, "%s [%s]: %s\n",get_time, // user defined functionulog_level_name(severity),msg);free(get_time);
}

执行后,文件中的内容类似以下,这个格式是按照自己喜好在日志函数中自己编写。

2023-4-18 20:23:14 [INFO]: Info, arg=42
2023-4-18 20:23:14 [CRITICAL]: main.c:67 arg=42
2023-4-18 20:23:14 [INFO]: main.c:66 arg=42

源码分析

  • ulog_message
#define ULOG_DEBUG(...) ulog_message(ULOG_DEBUG_LEVEL, __VA_ARGS__)
// 这里的...表示的是可变参数  __VA_ARGS__ 这个也是对应的表示可变参数 这里也可以改为
// #define ULOG_DEBUG(...) ulog_message(ULOG_DEBUG_LEVEL, const char* fmt, ...)void ulog_message(ulog_level_t severity, const char* fmt, ...) {// va_list 是一个字符指针,可以理解为指向当前参数的一个指针,取参必须通过这个指针进行va_list ap;int i;// 然后应该对ap 进行初始化,让它指向可变参数表里面的第一个参数,也就是const char* fmtva_start(ap, fmt);// 依次获取ap的类型,并进行sprintf函数类型功能组合成字符串,// 最大长度 ULOG_MAX_MESSAGE_LENGTH,超过会被截断vsnprintf(s_message, ULOG_MAX_MESSAGE_LENGTH, fmt, ap);// 将这个 ap 指针关掉,以免发生危险va_end(ap);for (i = 0; i < ULOG_MAX_SUBSCRIBERS; i++) {// 函数是依次执行的,那个函数先注册,那个先运行if (s_subscribers[i].fn != NULL) {// 只有使用的输出等级大于自定义函数等级的时候,就会执行if (severity >= s_subscribers[i].threshold) {s_subscribers[i].fn(severity, s_message);}}}
}
  • ulog_err_t
// 错误标志枚举
typedef enum {ULOG_ERR_NONE = 0,ULOG_ERR_SUBSCRIBERS_EXCEEDED,ULOG_ERR_NOT_SUBSCRIBED,
} ulog_err_t;
  • ulog_subscribe、ulog_unsubscribe
// 执行函数指针
typedef void (*ulog_function_t)(ulog_level_t severity, char* msg);
// 执行结构体
typedef struct {ulog_function_t fn;ulog_level_t threshold;
} subscriber_t;
// 执行结构体数组
#define ULOG_MAX_SUBSCRIBERS 6
static subscriber_t s_subscribers[ULOG_MAX_SUBSCRIBERS];// search the s_subscribers table to install or update fn
// 订阅函数,也就是将函数放入执行结构体数组中,并赋予相应的输出等级
ulog_err_t ulog_subscribe(ulog_function_t fn, ulog_level_t threshold) {int available_slot = -1;int i;for (i = 0; i < ULOG_MAX_SUBSCRIBERS; i++) {if (s_subscribers[i].fn == fn) {// 执行结构体数组已经含有该执行函数,进行更新即可// already subscribed: update threshold and return immediately.s_subscribers[i].threshold = threshold;return ULOG_ERR_NONE;}else if (s_subscribers[i].fn == NULL) {// found a free slot// 找到数组中最近的一个未使用的执行结构体,赋予标志信息available_slot = i;}}// fn is not yet a subscriber.  assign if possible.if (available_slot == -1) {// 执行结构体数组中已经存储满return ULOG_ERR_SUBSCRIBERS_EXCEEDED;}// 将执行函数信息,放入执行结构体数组s_subscribers[available_slot].fn = fn;s_subscribers[available_slot].threshold = threshold;return ULOG_ERR_NONE;
}// search the s_subscribers table to remove
// 取消订阅,就是将该函数从执行结构体数组中删除
ulog_err_t ulog_unsubscribe(ulog_function_t fn) {int i;for (i = 0; i < ULOG_MAX_SUBSCRIBERS; i++) {if (s_subscribers[i].fn == fn) {s_subscribers[i].fn = NULL;    // mark as emptyreturn ULOG_ERR_NONE;}}return ULOG_ERR_NOT_SUBSCRIBED;
}
  • ulog_level_name
// 获取相应的输出等级字符串
const char* ulog_level_name(ulog_level_t severity) {switch (severity) {case ULOG_TRACE_LEVEL: return "TRACE";case ULOG_DEBUG_LEVEL: return "DEBUG";case ULOG_INFO_LEVEL: return "INFO";case ULOG_WARNING_LEVEL: return "WARNING";case ULOG_ERROR_LEVEL: return "ERROR";case ULOG_CRITICAL_LEVEL: return "CRITICAL";case ULOG_ALWAYS_LEVEL: return "ALWAYS";default: return "UNKNOWN";}
}

相关文章:

嵌入式日志库ulog的使用和解析

嵌入式日志信息保存调试&#xff08;ulog&#xff09; 获取 项目地址&#xff1a;https://github.com/rdpoor/ulog uLog 为嵌入式微控制器或任何资源有限的系统提供结构化的日志记录机制。它继承了流行的 Log4c 和 Log4j 平台背后的一些概念&#xff0c;但开销更低。 使用方…...

自阿里P8爆出内部1031道java面试题后,在Boss直聘狂拿千份Offer

开始之前我问大家几个问题&#xff0c;看大家是如何思考的&#xff1a; 1.程序员一定要去一线城市漂泊吗&#xff1f;在自己家乡如何拿到一份满意的薪水&#xff1f; 2.程序员被裁员、找不到工作&#xff0c;代表什么&#xff1f; 3.程序员一定要进一线大厂吗&#xff1f;你…...

Java最新面试题100道,包含答案示例(41-50题)

非常抱歉&#xff0c;我理解有误。以下是第41至45题的Java面试题和答案&#xff1a; 请问Java中有哪些常用的集合类型&#xff1f; 答&#xff1a;Java中有多种常用的集合类型&#xff0c;包括List、Set、Map等。其中&#xff0c;List和Set分别代表一组元素的序列和一组无序不…...

C++之深入解析野指针和悬空指针

一、野指针 ① 什么是野指针&#xff1f; 野指针指向一个已删除的对象或未申请访问受限内存区域的指针。与空指针不同&#xff0c;野指针无法通过简单地判断是否为NULL避免&#xff0c;而只能通过养成良好的编程习惯来尽力减少&#xff0c;对野指针进行操作很容易造成程序错误…...

YOLOv7+单目测距(python)

YOLOv7单目测距&#xff08;python&#xff09; 1. 相关配置2. 测距原理3. 相机标定3.1&#xff1a;标定方法13.2&#xff1a;标定方法2 4. 相机测距4.1 测距添加4.2 主代码 5. 实验效果 相关链接 1. YOLOV5 单目测距&#xff08;python&#xff09; 2. YOLOV5 单目跟踪&…...

SYSU程设c++(第九周)函数对象、友元函数、友元类

函数对象&#xff1a; 如果一个类定义了operator()运算符函数&#xff0c;则可以使用该类的对象名为函数名调用这个函数. 函数对象是一个对象&#xff0c;但调用形式和普通函数调用一样&#xff0c;因此取名叫函数对象 (注意operator()先有个括号&#xff0c;接着才是括号(参数…...

Target品质审核零容忍问题点——上篇

【Target品质审核零容忍问题点——上篇】 Target品质验厂审核过程中共有110多个问题点&#xff0c;其中包含零容忍问题12项&#xff0c;审核当天如果出现任何零容忍项或出现很多扣分项&#xff0c;将直接影响最后的结果&#xff0c;极容易导致验厂不通过。建议工厂在遇到Target…...

使用node版本管理器gnvm

目录 一、官网 二、下载 三、查看本机node安装地址 四、将gnvm放到node安装目录 五、安装其他版本node&#xff08;以管理员身份打开CMD&#xff09; 六、使用指定版本&#xff08;以管理员身份打开CMD&#xff09; 七、查看当前版本&#xff08;以管理员身份打开CMD&…...

SpringBoot中使用redis事务

本文基于SpringBoot 2.X 事务在关系型数据库的开发中经常用到&#xff0c;其实非关系型数据库&#xff0c;比如redis也有对事务的支持&#xff0c;本文主要探讨在SpringBoot中如何使用redis事务。 事务的相关介绍可以参考&#xff1a; 0、起因 在一次线上事故中&#xff0c;我们…...

2023全网汇总PMP备考攻略(附答题技巧)

一&#xff0c;多复习和学习新版考纲 01《PMBOK》看三遍 这边建议看三遍《PMBOK》&#xff0c;更有利于我们巩固知识&#xff0c;查缺补漏。 第一遍 第一遍是老师带着我们去看。这个时候一定要非常专心&#xff0c;千万不要上课走神或者玩手机。因为这一遍老师会告诉我们&a…...

lightdb/pg reload guc 参数机制

lightdb/pg reload guc 参数机制 本文主要讲述调用pg_reload_conf 后&#xff0c;到guc被真正修改之间发送的故事。(基于pg13) pg_reload_conf 函数实现如下&#xff1a; Datum pg_reload_conf(PG_FUNCTION_ARGS) {if (kill(PostmasterPid, SIGHUP)){ereport(WARNING,(errms…...

E. Archaeology(纯思维)

Problem - E - Codeforces 爱丽丝买了一个刚果总理视频的订阅&#xff0c;正在看一部关于苏格兰卡特林湖的因子岛的考古发现的纪录片。考古学家发现了一本书&#xff0c;其年代和来源都不明。也许爱丽丝可以对它进行一些解释&#xff1f; 这本书包含一串字符 "a"、&…...

FISCO BCOS(三十四)———商品溯源(智能合约+后端)

FISCO BCOS(三十四)———商品溯源(智能合约+后端) 一、智能合约函数调用流程 注:智能合约来源(官网的合约仓库中) 但是TraceabilityFactory合约有问题,我已经做了修改,可以看原版与我的,只有一个函数不同。 官网上这套合约在TraceabilityFactory这个合约上缺少getGo…...

ts体操训练

1 实现pick type MyPick<T, K extends keyof T> {[P in K]: T[P] }2 实现readonly 让interface中所有属性变为可读 type MyReadonly<T> {readonly [K in keyof T]: T[K] }3 TupleToObject 将元组类型转换为对象类型 type tupleToObject<T extends any[]&…...

int指令

格式&#xff1a; int n,n为中断类型码&#xff0c;它的功能是引发中断过程 CPU执行过程 取中断类型码n&#xff1b;标志寄存器入栈&#xff0c;IF0&#xff0c;TF0&#xff1b;CS&#xff0c;IP入栈&#xff1b;&#xff08;IP&#xff09;&#xff08;n4&#xff09;&…...

Cycling 74 Max for Mac:音乐可视化编程软件

Cycling 74 Max是一款音乐、视觉、互动艺术等领域中广泛使用的编程语言和应用软件&#xff0c;它允许用户创作和控制实时音频和视频效果、交互式应用程序和媒体艺术品等。 Max将程序设计和可视化编程相结合&#xff0c;通过简单的拖拽和连接方式&#xff0c;用户可以将各种功能…...

ROS学习第十二节——话题通信控制小乌龟

1.基操一下 首先打开小乌龟程序和键盘控制程序 rosrun turtlesim turtlesim_node rosrun turtlesim turtle_teleop_key 查看话题列表 rostopic list 打开计算图查看具体是那个话题在起作用 rqt_graph 从上图可以看到两个节点之间的话题是 /turtle1/cmd_vel 使用以下命令获…...

matlab点云的可视化-源码复制粘贴即可(一)

一、导入并可视化一个无属性的点云 clc; clear; close; % clear everything% Import a point cloud from a plain text file (run type(Lion.xyz) to see the contents of the file) pc pointCloud(Lion.xyz);% Generate a z-colored view of the point cloud pc.plot;% Set …...

反射-Class类分析

反射相关的主要类 java.lang.Class&#xff1a;代表一个类&#xff0c;Class对象表示某个类加载后在堆中的对象java.lang.reflect.Method&#xff1a;代表类的方法&#xff0c;Method对象表示某个类的方法java.lang.reflect.Field&#xff1a;代表类的成员变量&#xff0c;Fie…...

Let’s Make C++ Great Again——string与常用字符处理函数

文章目录 string使用string类的例子&#xff0c;统计一个字符串中单词的个数&#xff1a;在算法模拟题中翻转字符串&#xff1a;判断回文字符串&#xff1a;字符串查找&#xff1a;字符串替换&#xff1a; 常用字符处理函数strlen()strcpy()strcat()strcmp()toupper() 和 tolow…...

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...

YSYX学习记录(八)

C语言&#xff0c;练习0&#xff1a; 先创建一个文件夹&#xff0c;我用的是物理机&#xff1a; 安装build-essential 练习1&#xff1a; 我注释掉了 #include <stdio.h> 出现下面错误 在你的文本编辑器中打开ex1文件&#xff0c;随机修改或删除一部分&#xff0c;之后…...

Linux简单的操作

ls ls 查看当前目录 ll 查看详细内容 ls -a 查看所有的内容 ls --help 查看方法文档 pwd pwd 查看当前路径 cd cd 转路径 cd .. 转上一级路径 cd 名 转换路径 …...

零知开源——STM32F103RBT6驱动 ICM20948 九轴传感器及 vofa + 上位机可视化教程

STM32F1 本教程使用零知标准板&#xff08;STM32F103RBT6&#xff09;通过I2C驱动ICM20948九轴传感器&#xff0c;实现姿态解算&#xff0c;并通过串口将数据实时发送至VOFA上位机进行3D可视化。代码基于开源库修改优化&#xff0c;适合嵌入式及物联网开发者。在基础驱动上新增…...

UE5 音效系统

一.音效管理 音乐一般都是WAV,创建一个背景音乐类SoudClass,一个音效类SoundClass。所有的音乐都分为这两个类。再创建一个总音乐类&#xff0c;将上述两个作为它的子类。 接着我们创建一个音乐混合类SoundMix&#xff0c;将上述三个类翻入其中&#xff0c;通过它管理每个音乐…...

Android屏幕刷新率与FPS(Frames Per Second) 120hz

Android屏幕刷新率与FPS(Frames Per Second) 120hz 屏幕刷新率是屏幕每秒钟刷新显示内容的次数&#xff0c;单位是赫兹&#xff08;Hz&#xff09;。 60Hz 屏幕&#xff1a;每秒刷新 60 次&#xff0c;每次刷新间隔约 16.67ms 90Hz 屏幕&#xff1a;每秒刷新 90 次&#xff0c;…...

Win系统权限提升篇UAC绕过DLL劫持未引号路径可控服务全检项目

应用场景&#xff1a; 1、常规某个机器被钓鱼后门攻击后&#xff0c;我们需要做更高权限操作或权限维持等。 2、内网域中某个机器被钓鱼后门攻击后&#xff0c;我们需要对后续内网域做安全测试。 #Win10&11-BypassUAC自动提权-MSF&UACME 为了远程执行目标的exe或者b…...

基于django+vue的健身房管理系统-vue

开发语言&#xff1a;Python框架&#xff1a;djangoPython版本&#xff1a;python3.8数据库&#xff1a;mysql 5.7数据库工具&#xff1a;Navicat12开发软件&#xff1a;PyCharm 系统展示 会员信息管理 员工信息管理 会员卡类型管理 健身项目管理 会员卡管理 摘要 健身房管理…...

边缘计算设备全解析:边缘盒子在各大行业的落地应用场景

随着工业物联网、AI、5G的发展&#xff0c;数据量呈爆炸式增长。但你有没有想过&#xff0c;我们生成的数据&#xff0c;真的都要发回云端处理吗&#xff1f;其实不一定。特别是在一些对响应时间、网络带宽、数据隐私要求高的行业里&#xff0c;边缘计算开始“火”了起来&#…...

JVM——对象模型:JVM对象的内部机制和存在方式是怎样的?

引入 在Java的编程宇宙中&#xff0c;“Everything is object”是最核心的哲学纲领。当我们写下new Book()这样简单的代码时&#xff0c;JVM正在幕后构建一个复杂而精妙的“数据实体”——对象。这个看似普通的对象&#xff0c;实则是JVM内存管理、类型系统和多态机制的基石。…...