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

Rust的高效易用日志库—tklog

很多人习惯于python,go等语言基础工具库的简单易用;在使用rust时,可能感觉比较麻烦,类似日志库这样的基础性工具库。tklog提供用法上,非常类似python等Logger的日志库用法,用法简洁;基于rust的高效性和一些优化策略,tklog的性能非常好,在压测中,可以达到 3-4 µs/op (微妙/次),这个效率比go最高的性能时候都高一些;在相同的环境下,对go进行无格式日志输出压测,可以达到 3-4µs/op,如果是格式化日志输出,则为4µs/op以上。(可以参考《 高性能日志库go-logger v2.0.3》中对各日志库的压测数据)。
在异步场景中,tklog提供了对应的方法,支持异步调用。异步方法最大的好处并非在性能上,而是它不会阻塞所在线程。但是由于 tklog的常规日志方法默认使用延迟策略,实际上也不会阻塞所在线程,或者准确说影响非常小,可以忽略不计。所以,异步场景一般也可以直接使用常规日志打印方法。
相关连接
  • 项目源码
  • 官网
  • 仓库
项目引入
[dependencies]
tklog = "0.0.2"   #使用时的实际最新版本;当前0.0.2版本

tklog是rust高性能结构化日志库
易用,高效,结构化,控制台日志,文件日志,文件切割,文件压缩,同步打印,异步打印

功能
  • 功能支持:控制台日志,文件日志,同步日志,异步日志
  • 日志级别设置:提供与标准库同级别日志打印: trace,debug,info,warn,error,fatal
  • 格式化输出:支持自定义日志的输出格式,包括日志级别标识、格式化时间、日志文件位置 等元素,并支持自定义格式调整。
  • 按时间文件切割:按小时,天,月份切割日志文件
  • 按大小文件切割:按指定大小切割日志文件
  • 文件数回滚:指定最大备份文件数,支持自动删除旧日志文件,并防止日志文件数过多。
  • 文件压缩:支持压缩归档备份日志文件。

使用方法简述

最简单常用的方法:直接调用

use tklog::{trace,debug, error, fatal, info,warn}
fn testlog() {trace!("trace>>>>", "aaaaaaaaa", 1, 2, 3, 4);debug!("debug>>>>", "bbbbbbbbb", 1, 2, 3, 5);info!("info>>>>", "ccccccccc", 1, 2, 3, 5);warn!("warn>>>>", "dddddddddd", 1, 2, 3, 6);error!("error>>>>", "eeeeeeee", 1, 2, 3, 7);fatal!("fatal>>>>", "ffffffff", 1, 2, 3, 8);
}

说明:默认打开控制台日志,没有写日志文件。打印结果:

[TRACE] 2024-05-26 11:47:22 testlog.rs 27:trace>>>>,aaaaaaaaa,1,2,3,4
[DEBUG] 2024-05-26 11:47:22 testlog.rs 28:debug>>>>,bbbbbbbbb,1,2,3,5
[INFO] 2024-05-26 11:47:22 testlog.rs 29:info>>>>,ccccccccc,1,2,3,5
[WARN] 2024-05-26 11:47:22 testlog.rs 30:warn>>>>,dddddddddd,1,2,3,6
[ERROR] 2024-05-26 11:47:22 testlog.rs 31:error>>>>,eeeeeeee,1,2,3,7
[FATAL] 2024-05-26 11:47:22 testlog.rs 32:fatal>>>>,ffffffff,1,2,3,8

说明:直接调用 debug!等宏进行打印,默认调用全局静态LOG对象。LOG对象支持初始化。

use tklog::{sync::Logger,LEVEL, LOG,Format,MODE,
};fn log_init() {LOG.set_console(true)  //设置控制台日志.set_level(LEVEL::Info)  //日志级别,默认Debug.set_format(Format::LevelFlag | Format::Time | Format::ShortFileName)  //结构化日志,定义输出的日志信息.set_cutmode_by_size("tklogsize.txt", 1<<20, 10, true)  //日志文件切割模式为文件大小,每1M文件切分一次,保留10个备份日志文件,并压缩备份日志.set_formatter("{level}{time} {file}:{message}\n")   //自定义日志输出格式。默认:{level}{time} {file}:{message}
}fn testlog() {log_init()  //调用初始化方法trace!("trace>>>>", "aaaaaaaaa", 1, 2, 3, 4);   //track日志级别小于设置的LEVEL::Info ,故无输出info!("info>>>>", "ccccccccc", 1, 2, 3, 5);
}

以上是全局单实例打印的示例。tklog支持自定义多实例打印。多实例一般应用在系统要求不同打印结构的场景中。

多实例打印
use tklog::{debugs, errors, fatals, infos,sync::Logger,LEVEL, LOG,traces, warns, Format, MODE,
};
fn testmutlilog() {let mut log = Logger::new();log.set_console(true).set_level(LEVEL::Debug) //定义日志级别为Debug.set_cutmode_by_time("tklogs.log", MODE::DAY, 10, true)   //分割日志文件的方式为按天分割,保留最多10个备份,并压缩备份文件.set_formatter("{message} | {time} {file}{level}\n")   //自定义日志结构信息的输入顺序与附加内容let mut logger = Arc::clone(&Arc::new(Mutex::new(log)));let log = logger.borrow_mut();traces!(log, "traces>>>>", "AAAAAAAAA", 1, 2, 3, 4);debugs!(log, "debugs>>>>", "BBBBBBBBB", 1, 2, 3, 5);infos!(log, "infos>>>>", "CCCCCCCCC", 1, 2, 3, 5);warns!(log, "warns>>>>", "DDDDDDDDDD", 1, 2, 3, 6);errors!(log, "errors>>>>", "EEEEEEEE", 1, 2, 3, 7);fatals!(log, "fatals>>>>", "FFFFFFFF", 1, 2, 3, 8);thread::sleep(Duration::from_secs(1))
}

执行结果:

debugs>>>>,BBBBBBBBB,1,2,3,5 | 2024-05-26 14:13:25 testlog.rs 70[DEBUG]
infos>>>>,CCCCCCCCC,1,2,3,5 | 2024-05-26 14:13:25 testlog.rs 71[INFO]
warns>>>>,DDDDDDDDDD,1,2,3,6 | 2024-05-26 14:13:25 testlog.rs 72[WARN]
errors>>>>,EEEEEEEE,1,2,3,7 | 2024-05-26 14:13:25 testlog.rs 73[ERROR]
fatals>>>>,FFFFFFFF,1,2,3,8 | 2024-05-26 14:13:25 testlog.rs 74[FATAL]

注意:以上输入结构化信息由 "{message} | {time} {file}{level}\nformatter决定。formatter中除了关键标识 {message}  {time}  {file}  {level} 外,其他内容原样输出,如 | , 空格,换行  等。


tklog使用详细说明

1. 日志级别 : Trace < Debug < Info < Warn < Error < Fatal

示例:

LOG.set_level(LEVEL::Info)  //日志级别,设置为Info
2. 控制台日志

调用 .set_console(bool) 函数

LOG.set_console(false)   // false表示不打印控制台日志。默认为true
3. 日志格式
  • Format::Nano                            无格式
  • Format::Date                             输出日期 :2024-05-26
  • Format::Time                             输出时间,精确到秒:14:13:25
  • Format::Microseconds              输出时间,精确到微妙:18:09:17.462245    
  • Format::LongFileName             长文件信息+行号:tests estlog.rs 25
  • Format::ShortFileName             短文件信息+行号:testlog.rs 25
  • Format::LevelFlag                      日志级别信息: [Debug]
LOG.set_format(Format::LevelFlag | Format::Time | Format::ShortFileName)  
4.自定义格式输出

默认:"{level}{time} {file}:{message} \n"

  • {level}            日志级别信息:如[Debug]
  • {time}            日志时间信息
  • {file}               文件位置行号信息
  • {message}      日志内容
LOG.set_formatter("{message} | {time} {file}{level}\n");  //自定义日志结构信息的输入顺序与附加内容

说明:除了关键标识 {message}  {time}  {file}  {level} 外,其他内容原样输出,如 | , 空格,换行  等。

5.按时间分割日志文件

时间标识:MODE::HOUR,MODE::DAY,MODE::MONTH

分别是:小时,天,月份

调用 .set_cutmode_by_time() 函数,参数:

  1. 文件路径
  2. 时间模式
  3. 最大备份日志文件数
  4. 是否压缩备份的日志文件

示例

let mut log = Logger::new();
log.set_cutmode_by_time("/usr/local/tklogs.log", MODE::DAY, 0, false)

说明:备份文件路径为: /usr/local/tklogs.log  ,时间模式为:按天备份,参数0表示不限制备份文件数,false表示不压缩备份的日志文件

备份的文件格式:

  1. 按天备份日期文件,如:
    • tklogs_20240521.log
    • tklogs_20240522.log
  2. 按小时备份日志文件,如:
    • tklogs_2024052110.log
    • tklogs_2024052211.log
  3. 按月份备份日志文件,如:
    • tklogs_202403.log
    • tklogs_202404.log
6.按大小分割日志文件

调用 .set_cutmode_by_size() 函数,参数:

  1. 文件路径
  2. 指定文件滚动大小
  3. 最大备份日志文件数
  4. 是否压缩备份的日志文件

示例

let mut log = Logger::new();
log.set_cutmode_by_time("tklogs.log", 100<<20, 10, true)

说明:备份文件路径为:tklogs.log  ,按100M大小备份文件,参数10表示只保留最新10个备份文件,true表示压缩备份的日志文件

备份的文件格式:

  • tklogs_1.log.gz
  • tklogs_2.log.gz
  • tklogs_3.log.gz

tklog提供常规日志打印 方法为:

  • 全局单例打印
    1. trace!
    2. debug!
    3. info!
    4. warn!
    5. error!
    6. fatal!
  • 多实例打印
    1. traces!
    2. debugs!
    3. infos!
    4. warns!
    5. errors!
    6. fatals!

异步日志

  • 全局异步单例打印
    1. async_trace!
    2. async_debug!
    3. async_info!
    4. async_warn!
    5. async_error!
    6. async_fatal!
  • 多实例异步打印
    1. async_traces!
    2. async_debugs!
    3. async_infos!
    4. async_warns!
    5. async_errors!
    6. async_fatals!
异步方法使用示例

全局单例异步调用

use tklog::{async_debug,  async_error,  async_fatal,  async_info,  async_trace,  async_warn,  LEVEL, Format, ASYNC_LOG};async fn async_log_init() {// 全局单例设置参数ASYNC_LOG.set_console(false)   //控制台.set_level(LEVEL::Trace)  //日志级别.set_format(Format::LevelFlag | Format::Time | Format::ShortFileName)  //结构化日志,定义输出的日志信息.set_cutmode_by_size("tklog_async.txt", 10000, 10, false).await;  //日志文件切割模式为文件大小,每10000字节切割一次,保留10个备份日志文件#[tokio::test]
async fn testlog() {async_log_init().await;  //参数设置async_trace!("trace>>>>", "aaaaaaa", 1, 2, 3);async_debug!("debug>>>>", "aaaaaaa", 1, 2, 3);async_info!("info>>>>", "bbbbbbbbb", 1, 2, 3);async_warn!("warn>>>>", "cccccccccc", 1, 2, 3);async_error!("error>>>>", "ddddddddddddd", 1, 2, 3);async_fatal("fatal>>>>", "eeeeeeeeeeeeee", 1, 2, 3);tokio::time::sleep(tokio::time::Duration::from_secs(3)).await;
}

输出结果:

[TRACE] 20:03:32 testasynclog.rs 20:trace>>>>,aaaaaaa,1,2,3
[DEBUG] 20:03:32 testasynclog.rs 21:debug>>>>,aaaaaaa,1,2,3
[INFO] 20:03:32 testasynclog.rs 22:info>>>>,bbbbbbbbb,1,2,3
[WARN] 20:03:32 testasynclog.rs 23:warn>>>>,cccccccccc,1,2,3
[ERROR] 20:03:32 testasynclog.rs 24:error>>>>,ddddddddddddd,1,2,3
[FATAL] 20:03:32 testasynclog.rs 25:fatal>>>>,eeeeeeeeeeeeee,1,2,3

多实例异步

use std::sync::Arc;use tklog::{async_debugs,  async_errors,  async_fatals,  async_infos,  async_traces,  async_warns, LEVEL, Format, ASYNC_LOG, MODE
};
#[tokio::test]
async fn testmultilogs() {//新建 Async::Logger 对象let mut log = tklog::Async::Logger::new();log.set_console(false).set_level(LEVEL::Debug).set_cutmode_by_time("tklogasync.log", MODE::DAY, 10, true) .await.set_formatter("{message} | {time} {file}{level}");let mut logger = Arc::clone(&Arc::new(Mutex::new(log)));let log = logger.borrow_mut();async_traces!(log, "async_traces>>>>", "AAAAAAAAAA", 1, 2, 3);async_debugs!(log, "async_debugs>>>>", "BBBBBBBBBB", 1, 2, 3);async_infos!(log, "async_infos>>>>", "CCCCCCCCCC", 1, 2, 3);async_warns!(log, "async_warns>>>>", "DDDDDDDDDD", 1, 2, 3);async_errors!(log, "async_errors>>>>", "EEEEEEEEEEE", 1, 2, 3);async_fatals!(log, "async_fatals>>>>", "FFFFFFFFFFFF", 1, 2, 3);tokio::time::sleep(tokio::time::Duration::from_secs(3)).await;
}

输出结果:

async_debugs>>>>,BBBBBBBBBB,1,2,3 | 2024-05-26 20:10:24 testasynclog.rs 45[DEBUG]
async_infos>>>>,CCCCCCCCCC,1,2,3 | 2024-05-26 20:10:24 testasynclog.rs 46[INFO]
async_warns>>>>,DDDDDDDDDD,1,2,3 | 2024-05-26 20:10:24 testasynclog.rs 47[WARN]
async_errors>>>>,EEEEEEEEEEE,1,2,3 | 2024-05-26 20:10:24 testasynclog.rs 48[ERROR]
async_fatals>>>>,FFFFFFFFFFFF,1,2,3 | 2024-05-26 20:10:24 testasynclog.rs 49[FATAL]


基准压力测试
test_debug              time:   [3.3747 µs 3.4599 µs 3.5367 µs]change: [-69.185% -68.009% -66.664%] (p = 0.00 < 0.05)Performance has improved.
Found 9 outliers among 100 measurements (9.00%)6 (6.00%) high mild3 (3.00%) high severe

说明:时间范围给出了三个数据点,分别代表了测试执行时间的最小值(3.3747微秒)、平均值附近的值(3.4599微秒)、以及最大值(3.5367微秒)

test_debug              time:   [3.8377 µs 3.8881 µs 3.9408 µs]change: [-66.044% -65.200% -64.363%] (p = 0.00 < 0.05)Performance has improved.
Found 2 outliers among 100 measurements (2.00%)2 (2.00%) high mild

说明:测试运行的时间范围是从3.8377微秒到3.9408微秒,覆盖了一个大概的分布情况,其中3.8881微秒大约是这段时间内的平均或中位数执行时间

结论:日志打印函数性能:3 µs/op — 4 µs/op   (微妙/次)

相关文章:

Rust的高效易用日志库—tklog

很多人习惯于python&#xff0c;go等语言基础工具库的简单易用&#xff1b;在使用rust时&#xff0c;可能感觉比较麻烦&#xff0c;类似日志库这样的基础性工具库。tklog提供用法上&#xff0c;非常类似python等Logger的日志库用法&#xff0c;用法简洁&#xff1b;基于rust的高…...

LabVIEW调用外部DLL(动态链接库)

LabVIEW调用外部DLL&#xff08;动态链接库&#xff09; LabVIEW调用外部DLL&#xff08;动态链接库&#xff09;可以扩展其功能&#xff0c;使用外部库实现复杂计算、硬件控制等任务。通过调用节点&#xff08;Call Library Function Node&#xff09;配置DLL路径、函数名称和…...

Python图形界面(GUI)Tkinter笔记(十六):Radiobutton选项功能按钮(单选按钮)

在tkinter库中,选项功能按钮Radiobutton是一个常用的控件,用于从多个选项中选择一个,从而实现相关的交互功能。 其余笔记:【Python图形界面(GUI)Tkinter笔记(总目录)】 【一】书写:tkinter.Radiobutton(父窗口对象,参数1,参数2,...) 【二】Radiobutton控件常用参数…...

静态路由原理与配置

文章目录 路由器的工作原理路由根据路由表转发数据 路由表的形成路由表路由表的形成 静态路由和默认路由静态路由默认路由 路由器转发数据包的封装过程源目地址变化 交换与路由对比路由工作在网络层交换工作在数据链路层 静态路由和默认路由的配置 路由器的工作原理 路由 路由…...

Android 开机动画的启动过程BootAnimation(基于Android10.0.0-r41)

文章目录 Android 开机动画的启动过程BootAnimation(基于Android10.0.0-r41)1.开机动画的启动过程概述2.为什么设置了属性之后就会播放&#xff1f; Android 开机动画的启动过程BootAnimation(基于Android10.0.0-r41) 1.开机动画的启动过程概述 下面就是BootAnimation的重要部…...

Redis 中的 Zset 数据结构详解

目录 用法 1. 增 2. 删 3. 查 4. 交&#xff0c;并 编码方式 应用场景 Redis 中的 Zset&#xff08;有序集合&#xff09;是一种将元素按照分数进行排序的数据结构。与上篇写的SetRedis 中的 Set 数据结构详解不同&#xff0c;Zset 中的每个元素都关联一个浮点数类型的…...

Python网页处理与爬虫实战:使用Requests库进行网页数据抓取

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…...

HOW - vscode 使用指南

目录 一、基本介绍1. 安装 VS Code2. 界面介绍3. 扩展和插件4. 设置和自定义 二、常用界面功能和快捷操作&#xff08;重点&#xff09;常用界面功能快捷操作 三、资源和支持 Visual Studio Code&#xff08;VS Code&#xff09;是一款由微软开发的免费、开源的代码编辑器&…...

刚刚!《国家科学技术奖励条例》迎来最新修订

【SciencePub学术】《国务院关于修改〈国家科学技术奖励条例〉的决定》已经于2024年5月11日国务院第32次常务会议通过&#xff0c;现予公布&#xff1a; 国务院决定对《国家科学技术奖励条例》作如下修改&#xff1a; 一、将第二条修改为&#xff1a;“国家设立下列国家科学技术…...

MySQL -- SQL笔试题相关

1.银行代缴花费bank_bill 字段名描述serno流水号date交易日期accno账号name姓名amount金额brno缴费网点 serno: 一个 BIGINT UNSIGNED 类型的列&#xff0c;作为主键&#xff0c;且不为空。该列是自动增量的&#xff0c;每次插入新行时&#xff0c;都会自动递增生成一个唯一的…...

VB6 MQTT为什么在物联网应用中使用 MQTT 而不是 HTTP?

有需要VBA,VB6,VB.NET等方面的MQTT的可以找我 一、MQTT简介 MQTT被广泛用于物联网(IoT:Internet of Things)领域&#xff0c;其中大量的设备需要进行实时通信和数据交换。它采用了一种发布/订阅(publish/subscribe)模型&#xff0c;其中消息的发送者&#xff08;发布者&#…...

软设之希尔排序

假设有n个元素&#xff0c;先取一个小于n的整数d1作为一个增量&#xff0c;把文件的全部记录分成d1个组。所有距离为d1的倍数的记录放在同一个组中。先在各组中进行直接插入排序;然后&#xff0c;取第二个增量d2<d1重复上诉的分组和排序&#xff0c;直到所取得增量dt1&#…...

WPF Binding对象

在WinForm中&#xff0c;我们要想对控件赋值&#xff0c;需要在后台代码中拿到控件对象进行操作&#xff0c;这种赋值形式&#xff0c;从根本上是无法实现界面与逻辑分离的。 在WPF中&#xff0c;微软引入了Binding对象&#xff0c;通过Binding&#xff0c;我们可以直接将控件与…...

Educational Codeforces Round 127 D. Insert a Progression

Insert a Progression time limit per test: 2 second memory limit per test: 256 megabytes input: standard input output: standard output You are given a sequence of n n n integers a 1 , a 2 , … , a n a_1, a_2, \dots, a_n a1​,a2​,…,an​. You are also giv…...

树莓集团:构筑全国数字影像生态链

在数字化浪潮席卷全球的今天&#xff0c;数字影像技术正以前所未有的速度改变着我们的生活。成都树莓集团以远见卓识和坚定步伐&#xff0c;专注于全国数字影像生态链的建设&#xff0c;不断推动着文创产业的创新与发展。 树莓集团致力于打造一个完整的数字影像生态链&#xff…...

物联网——TIM定时器、PWM驱动呼吸灯、舵机和直流电机

定时器概念&#xff08;常用于输出PWM波形&#xff0c;驱动电机&#xff09; 时间脉冲数时钟周期&#xff1b; 这里的脉冲数6553665536&#xff0c;支持定时器级联&#xff0c;从而延长定时 定时器类型 基本定时器原理图&#xff08;UI:更新中断&#xff0c; U:更新事件&#…...

Elasticsearch 认证模拟题 -2

一、题目 有一个索引 task3&#xff0c;其中有 fielda&#xff0c;fieldb&#xff0c;fieldc&#xff0c;fielde 现要求对 task3 重建索引&#xff0c;重建后的索引新增一个字段 fieldg 其值是fielda&#xff0c;fieldb&#xff0c;fieldc&#xff0c;fielde 的值拼接而成。 …...

Java-----Comparable接口和Comparator接口

在Java中&#xff0c;我们会经常使用到自定义类&#xff0c;那我们如何进行自定义类的比较呢? 1.Comparable接口 普通数据的比较 int a10;int b91;System.out.println(a<b); 那自定义类型可不可以这样比较呢&#xff1f;看一下代码 我们发现会报错&#xff0c;因为自定义…...

通信技术体会

比如 pcie可以看成是全连接的ahb bus&#xff0c;但又不是。 因为pcie还是axi&#xff08;神似split/cutthrough&#xff09;。&#xff08;axi更多是接口而不是bus&#xff09;。 pcie虽然物理层和usb都是serdes&#xff0c;但transaction layer就是上面这样的&#xff0c;也就…...

Linux系统安全及其应用

文章目录 一、用户账号安全管理1.1 系统账号的清理1.2 对用户账号的操作1.2.1 锁定和解锁用户1.2.2 删除无用账号 1.3 对重要文件进行锁定1.4 密码安全控制1.4.1 新建用户1.4.2 已有用户 二、历史命令管理2.1 历史命令限制2.2 自动清空历史命令 三、设置终端登录的安全管理3.1 …...

8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂

蛋白质结合剂&#xff08;如抗体、抑制肽&#xff09;在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上&#xff0c;高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术&#xff0c;但这类方法普遍面临资源消耗巨大、研发周期冗长…...

iPhone密码忘记了办?iPhoneUnlocker,iPhone解锁工具Aiseesoft iPhone Unlocker 高级注册版​分享

平时用 iPhone 的时候&#xff0c;难免会碰到解锁的麻烦事。比如密码忘了、人脸识别 / 指纹识别突然不灵&#xff0c;或者买了二手 iPhone 却被原来的 iCloud 账号锁住&#xff0c;这时候就需要靠谱的解锁工具来帮忙了。Aiseesoft iPhone Unlocker 就是专门解决这些问题的软件&…...

工程地质软件市场:发展现状、趋势与策略建议

一、引言 在工程建设领域&#xff0c;准确把握地质条件是确保项目顺利推进和安全运营的关键。工程地质软件作为处理、分析、模拟和展示工程地质数据的重要工具&#xff0c;正发挥着日益重要的作用。它凭借强大的数据处理能力、三维建模功能、空间分析工具和可视化展示手段&…...

ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放

简介 前面两期文章我们介绍了I2S的读取和写入&#xff0c;一个是通过INMP441麦克风模块采集音频&#xff0c;一个是通过PCM5102A模块播放音频&#xff0c;那如果我们将两者结合起来&#xff0c;将麦克风采集到的音频通过PCM5102A播放&#xff0c;是不是就可以做一个扩音器了呢…...

Keil 中设置 STM32 Flash 和 RAM 地址详解

文章目录 Keil 中设置 STM32 Flash 和 RAM 地址详解一、Flash 和 RAM 配置界面(Target 选项卡)1. IROM1(用于配置 Flash)2. IRAM1(用于配置 RAM)二、链接器设置界面(Linker 选项卡)1. 勾选“Use Memory Layout from Target Dialog”2. 查看链接器参数(如果没有勾选上面…...

解决本地部署 SmolVLM2 大语言模型运行 flash-attn 报错

出现的问题 安装 flash-attn 会一直卡在 build 那一步或者运行报错 解决办法 是因为你安装的 flash-attn 版本没有对应上&#xff0c;所以报错&#xff0c;到 https://github.com/Dao-AILab/flash-attention/releases 下载对应版本&#xff0c;cu、torch、cp 的版本一定要对…...

Python如何给视频添加音频和字幕

在Python中&#xff0c;给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加&#xff0c;包括必要的代码示例和详细解释。 环境准备 在开始之前&#xff0c;需要安装以下Python库&#xff1a;…...

IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)

文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...

实现弹窗随键盘上移居中

实现弹窗随键盘上移的核心思路 在Android中&#xff0c;可以通过监听键盘的显示和隐藏事件&#xff0c;动态调整弹窗的位置。关键点在于获取键盘高度&#xff0c;并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...

selenium学习实战【Python爬虫】

selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...