[C++][第三方库][spdlog]详细讲解

目录

  • 1.介绍
  • 2.安装
  • 3.使用
    • 1.包含头文件
    • 2.日志输出等级枚举
    • 3.日志输出格式自定义
    • 4.日志记录器类
    • 5.异步日志记录类
    • 6.日志记录器工厂类
    • 7.日志落地类
    • 8.全局接口
    • 9.记录日志
  • 4.使用示例
    • 0.makefile
    • 1.同步日志并输出到标准输出
    • 2.同步日志并输出到文件
    • 3.异步日志并输出到标准输出
  • 5.spdlog与glog组件对比
    • 0.前言
    • 1.glog
    • 2.spdlog
    • 3.对比总结


1.介绍

  • spdlog是一个高性能、超快速、零配置的C++日志库,它旨在提供简洁的API和丰富的功能,同时保持高性能的日志记录。它支持多种输出目标、格式化选项、线程安全以及异步日志记录
  • Github
  • 特点
    • 高性能spdlog专为速度而设计,即使在高负载情况下也能保持良好的性能
    • 零配置:无需复杂的配置,只需包含头文件即可在项目中使用
    • 异步日志:支持异步日志记录,减少对主线程的影响
    • 格式化:支持自定义日志消息的格式化,包括时间戳、线程ID、日志级别等
    • 多平台:跨平台兼容,支持Windows、Linux、MacOS等操作系统
    • 丰富的API:提供丰富的日志级别和操作符重载,方便记录各种类型的日志

2.安装

  • 直接命令安装sudo apt install libspdlog-dev
  • 源码安装clone后,使用cmake安装即可,详细可参照官方文档

3.使用

1.包含头文件

#include <spdlog/spdlog.h>

2.日志输出等级枚举

namespace level 
{ 
    enum level_enum : int 
    { 
        trace = SPDLOG_LEVEL_TRACE, 
        debug = SPDLOG_LEVEL_DEBUG, 
        info = SPDLOG_LEVEL_INFO, 
        warn = SPDLOG_LEVEL_WARN, 
        err = SPDLOG_LEVEL_ERROR, 
        critical = SPDLOG_LEVEL_CRITICAL, 
        off = SPDLOG_LEVEL_OFF, 
        n_levels 
    }; 
} 

3.日志输出格式自定义

  • 可以自定义日志消息的格式
    logger->set_pattern("%Y-%m-%d %H:%M:%S [%t] [%-7l] %v"); 
     
    %t - 线程ID(Thread ID)
    %n - 日志器名称(Logger name)
    %l - 日志级别名称(Level name),如 INFO, DEBUG, ERROR 等
    %v - 日志内容(message)
    %Y - 年(Year)
    %m - 月(Month)
    %d - 日(Day)
    %H - 小时(24-hour format)
    %M - 分钟(Minute)
    %S - 秒(Second)
    

4.日志记录器类

  • 创建一个基本的日志记录器,并设置日志级别和输出模式
    namespace spdlog 
    { 
    	class logger 
    	{ 
    	    logger(std::string name)logger(std::string name, sink_ptr single_sink) 
    	    logger(std::string name, sinks_init_list sinks) 
    	    
    	    void set_level(level::level_enum log_level); 
    	    void set_formatter(std::unique_ptr<formatter> f); 
    	    
    	    template<typename... Args> 
    	    void trace(fmt::format_string<Args...> fmt, Args &&...args) 
    	    
    	    template<typename... Args> 
    	    void debug(fmt::format_string<Args...> fmt, Args &&...args) 
    	    
    	    template<typename... Args> 
    	    void info(fmt::format_string<Args...> fmt, Args &&...args) 
    	    
    	    template<typename... Args> 
    	    void warn(fmt::format_string<Args...> fmt, Args &&...args) 
    	    
    	    template<typename... Args> 
    	    void error(fmt::format_string<Args...> fmt, Args &&...args) 
    	    
    	    template<typename... Args> 
    	    void critical(fmt::format_string<Args...> fmt, Args &&...args) 
    	     
    	    void flush(); //刷新日志 
    	    //策略刷新--触发指定等级日志的时候立即刷新日志的输出 
    	    void flush_on(level::level_enum log_level); 
    	}}
    

5.异步日志记录类

  • 为了异步记录日志,可以使用spdlog::async::logger
    class async_logger final : public logger 
    { 
        async_logger(std::string logger_name, 
                     sinks_init_list sinks_list, 
                     std::weak_ptr<details::thread_pool> tp, 
                     async_overflow_policy overflow_policy = 
                     async_overflow_policy::block);
                     
        async_logger(std::string logger_name, 
                     sink_ptr single_sink, 
                     std::weak_ptr<details::thread_pool> tp, 
                     async_overflow_policy overflow_policy = 
                     async_overflow_policy::block); 
                      
        //异步日志输出需要异步工作线程的支持,这里是线程池类 
        class SPDLOG_API thread_pool 
        { 
            thread_pool(size_t q_max_items, 
    		            size_t threads_n, 
    		            std::function<void()> on_thread_start, 
    		            std::function<void()> on_thread_stop); 
    		            
            thread_pool(size_t q_max_items, size_t threads_n,  
    		            std::function<void()> on_thread_start); 
    		            
            thread_pool(size_t q_max_items, size_t threads_n); 
        }; 
    } 
     
    std::shared_ptr<spdlog::details::thread_pool> thread_pool() 
    { 
        return details::registry::instance().get_tp(); 
    } 
     
    //默认线程池的初始化接口 
    inline void init_thread_pool(size_t q_size, size_t thread_count) 
     
    auto async_logger = spdlog::async_logger_mt("async_logger", 
    											"logs/async_log.txt"); 
    											
    async_logger->info("This is an asynchronous info message");
    

6.日志记录器工厂类

using async_factory = async_factory_impl<async_overflow_policy::block>; 
 
template<typename Sink, typename... SinkArgs> 
inline std::shared_ptr<spdlog::logger> create_async(std::string logger_name, 
													SinkArgs &&...sink_args) 
 
 
// 创建一个彩色输出到标准输出的日志记录器,默认工厂创建同步日志记录器 
template<typename Factory = spdlog::synchronous_factory> 
std::shared_ptr<logger> stdout_color_mt(const std::string &logger_name, 
									    color_mode mode = color_mode::automatic); 
//标准错误 
template<typename Factory = spdlog::synchronous_factory> 
std::shared_ptr<logger> stderr_color_mt(const std::string &logger_name, 
										color_mode mode = color_mode::automatic); 
//指定文件  
template<typename Factory = spdlog::synchronous_factory> 
std::shared_ptr<logger> basic_logger_mt(const std::string &logger_name, 
										const filename_t &filename, 
										bool truncate = false, 
								const file_event_handlers &event_handlers = {}) 
//循环文件 
template<typename Factory = spdlog::synchronous_factory> 
std::shared_ptr<logger> rotating_logger_mt(const std::string &logger_name,  
										   const filename_t &filename,  
										   size_t max_file_size,  
										   size_t max_files,  
										   bool rotate_on_open = false) 
...

7.日志落地类

namespace spdlog 
{ 
	namespace sinks 
	{ 
	    class SPDLOG_API sink 
	    { 
	    public: 
	        virtual ~sink() = default; 
	        virtual void log(const details::log_msg &msg) = 0; 
	        virtual void flush() = 0; 
	        virtual void set_pattern(const std::string &pattern) = 0; 
	        virtual void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) = 0;
	     
	        void set_level(level::level_enum log_level); 
	    }; 
	     
	    using stdout_sink_mt; 
	    using stderr_sink_mt; 
	    using stdout_color_sink_mt; 
	    using stderr_color_sink_mt; 
	    //滚动日志文件-超过一定大小则自动重新创建新的日志文件 
	    sink_ptr rotating_file_sink(filename_t base_filename, 
			                        std::size_t max_size, 
			                        std::size_t max_files, 
			                        bool rotate_on_open = false, 
							   const file_event_handlers &event_handlers = {}); 
	    using rotating_file_sink_mt = rotating_file_sink<std::mutex>; 
	    //普通的文件落地对啊ing 
	    sink_ptr basic_file_sink(const filename_t &filename, 
			                     bool truncate = false, 
			                     const file_event_handlers &event_handlers = {}); 
	    using basic_file_sink_mt = basic_file_sink<std::mutex>; 
	     
	    using kafka_sink_mt = kafka_sink<std::mutex>; 
	    using mongo_sink_mt = mongo_sink<std::mutex>; 
	    using tcp_sink_mt = tcp_sink<std::mutex>; 
	    using udp_sink_mt = udp_sink<std::mutex>; 
	    ..... 
	    //*_st:单线程版本,不用加锁,效率更高。 
	    //*_mt:多线程版本,用于多线程程序是线程安全的。 
	} 
} 

8.全局接口

//输出等级设置接口 
void set_level(level::level_enum log_level); 
//日志刷新策略-每隔N秒刷新一次 
void flush_every(std::chrono::seconds interval) 
//日志刷新策略-触发指定等级立即刷新 
void flush_on(level::level_enum log_level); 

9.记录日志

  • 使用日志记录器记录不同级别的日志
    logger->trace("This is a trace message"); 
    logger->debug("This is a debug message"); 
    logger->info("This is an info message"); 
    logger->warn("This is a warning message"); 
    logger->error("This is an error message"); 
    logger->critical("This is a critical message"); 
    

4.使用示例

0.makefile

all:sync_stdout sync_file async

sync_stdout:sync_stdout.cc
	g++ -o $@ $^ -std=c++17 -lspdlog -lfmt

sync_file:sync_file.cc
	g++ -o $@ $^ -std=c++17 -lspdlog -lfmt

async:async.cc
	g++ -o $@ $^ -std=c++17 -lspdlog -lfmt

.PHONY:clean
clean:
	rm sync_stdout
	rm sync_file

1.同步日志并输出到标准输出

  • main.cc
    #include <iostream>
    #include <spdlog/spdlog.h>
    #include <spdlog/sinks/stdout_color_sinks.h>
    
    int main()
    {   
    	// 1.设置全局的刷新策略
    	// (1) 每秒刷新
    	spdlog::flush_every(std::chrono::seconds(1));
    	// (2) 遇到debug以上等级的日志立即刷新
    	spdlog::flush_on(spdlog::level::level_enum::debug);
    	// (3) 设置全局的日志输出等级 -> 无所谓, 每个日治其可以独立进行设置
    	spdlog::set_level(spdlog::level::level_enum::debug);
    
    	// 2.创建同步日志器(标准输出/文件) --> 工厂接口默认创建的是同步日志器
    	auto logger = spdlog::stdout_color_mt("default-logger");
    
    	// 设置日期的刷新策略, 以及设置日志器的输出等级
    	// logger->flush_on(spdlog::level::level_enum::debug);
    	// logger->set_level(spdlog::level::level_enum::debug);
    
    	// 3.设置日志输出格式
    	logger->set_pattern("[%H:%M:%S][%t][%-8l] %v");
    
    	// 4.进行简单的日志输出
    	logger->trace("Hello {}", "SnowK");
    	logger->debug("Hello {}", "SnowK");
    	logger->info("Hello {}", "SnowK");
    	logger->warn("Hello {}", "SnowK");
    	logger->error("Hello {}", "SnowK");
    	logger->critical("Hello {}", "SnowK");
    
    	std::cout << "spdlog 日志输出完毕" << std::endl;
    
    	return 0;
    }
    
  • 输出结果
    [15:26:28][27831][debug   ] Hello SnowK
    [15:26:28][27831][info    ] Hello SnowK
    [15:26:28][27831][warning ] Hello SnowK
    [15:26:28][27831][error   ] Hello SnowK
    [15:26:28][27831][critical] Hello SnowK
    spdlog 日志输出完毕
    

2.同步日志并输出到文件

  • main.cc
    #include <iostream>
    #include <spdlog/spdlog.h>
    #include <spdlog/sinks/basic_file_sink.h>
    
    int main()
    {   
    	// 1.设置全局的刷新策略
    	// (1) 每秒刷新
    	spdlog::flush_every(std::chrono::seconds(1));
    	// (2) 遇到debug以上等级的日志立即刷新
    	spdlog::flush_on(spdlog::level::level_enum::debug);
    	// (3) 设置全局的日志输出等级 -> 无所谓, 每个日治其可以独立进行设置
    	spdlog::set_level(spdlog::level::level_enum::debug);
    
    	// 2.创建同步日志器(标准输出/文件) --> 工厂接口默认创建的是同步日志器
    	auto logger = spdlog::basic_logger_mt("file_logger", "log");
    
    	// 设置日期的刷新策略, 以及设置日志器的输出等级
    	// logger->flush_on(spdlog::level::level_enum::debug);
    	// logger->set_level(spdlog::level::level_enum::debug);
    
    	// 3.设置日志输出格式
    	logger->set_pattern("[%H:%M:%S][%t][%-8l] %v");
    
    	// 4.进行简单的日志输出
    	logger->trace("Hello {}", "SnowK");
    	logger->debug("Hello {}", "SnowK");
    	logger->info("Hello {}", "SnowK");
    	logger->warn("Hello {}", "SnowK");
    	logger->error("Hello {}", "SnowK");
    	logger->critical("Hello {}", "SnowK");
    
    	std::cout << "spdlog 日志输出完毕" << std::endl;
    
    	return 0;
    }
    
  • 输出结果
    # stdout
    spdlog 日志输出完毕
    
    # log文件
    [15:34:45][28382][debug   ] Hello SnowK
    [15:34:45][28382][info    ] Hello SnowK
    [15:34:45][28382][warning ] Hello SnowK
    [15:34:45][28382][error   ] Hello SnowK
    [15:34:45][28382][critical] Hello SnowK
    

3.异步日志并输出到标准输出

  • main.cc
    #include <iostream>
    #include <spdlog/spdlog.h>
    #include <spdlog/sinks/stdout_color_sinks.h>
    #include <spdlog/async.h>
    
    int main()
    {   
    	// 1.设置全局的刷新策略
    	// (1) 每秒刷新
    	spdlog::flush_every(std::chrono::seconds(1));
    	// (2) 遇到debug以上等级的日志立即刷新
    	spdlog::flush_on(spdlog::level::level_enum::debug);
    	// (3) 设置全局的日志输出等级 -> 无所谓, 每个日治其可以独立进行设置
    	spdlog::set_level(spdlog::level::level_enum::debug);
    
    	// 2.创建异步日志器(标准输出/文件) --> 工厂接口默认创建的是同步日志器
    	spdlog::init_thread_pool(2048, 1);
    	auto logger = spdlog::stdout_color_mt<spdlog::async_factory>("async-logger");
    
    	// 设置日期的刷新策略, 以及设置日志器的输出等级
    	// logger->flush_on(spdlog::level::level_enum::debug);
    	// logger->set_level(spdlog::level::level_enum::debug);
    
    	// 3.设置日志输出格式
    	logger->set_pattern("[%H:%M:%S][%t][%-8l] %v");
    
    	// 4.进行简单的日志输出
    	logger->trace("Hello {}", "SnowK");
    	logger->debug("Hello {}", "SnowK");
    	logger->info("Hello {}", "SnowK");
    	logger->warn("Hello {}", "SnowK");
    	logger->error("Hello {}", "SnowK");
    	logger->critical("Hello {}", "SnowK");
    
    	std::cout << "spdlog 日志输出完毕" << std::endl;
    
    	return 0;
    }
    
  • 输出结果
    spdlog 日志输出完毕
    [15:41:47][28793][debug   ] Hello SnowK
    [15:41:47][28793][info    ] Hello SnowK
    [15:41:47][28793][warning ] Hello SnowK
    [15:41:47][28793][error   ] Hello SnowK
    [15:41:47][28793][critical] Hello SnowK
    

5.spdlog与glog组件对比

0.前言

  • glogspdlog都是流行的C++日志库,它们各自具有不同的特点和优势
  • 以下是对这两个库的对比分析,包括性能测试的结果和使用场景的考量

1.glog

  • glog是由Google开发的一个开源C++日志库,它提供了丰富的日志功能,包括多种日志级别、条件日志记录、日志文件管理、信号处理、自定义日志格式等
  • glog默认情况下是同步记录日志的,这意味着每次写日志操作都会阻塞直到日志数据被写入磁盘
  • 性能:根据张生荣的性能对比测试分析,glog在同步调用的场景下的性能较spdlog
    • 在一 台低配的服务器上,glog耗时1.027秒处理十万笔日志数据,而在固态硬盘上的耗时 为0.475

2.spdlog

  • spdlog是一个开源的、高性能的C++日志库,它支持异步日志记录,允许在不影响主线程的情况下进行日志写入
  • 性能:在同样的性能测试中,spdlog在同步调用的场景下比glog
    • 在低配服务器上的耗时为0.135秒,而在固态硬盘上的耗时为0.057秒
    • 此外,spdlog还提供了异步日志记录的功能,其简单异步模式的耗时为0.158秒

3.对比总结

  • 性能:从性能测试结果来看,spdlog在同步调用场景下的性能优于glog,当涉及到大量日志数据时,spdlog显示出更快的处理速度。
  • 异步日志spdlog支持异步日志记录,这在处理高负载应用程序时非常有用,可以减少日志操作对主线程的影响。
  • 易用性spdlog提供了更简单的集成和配置方式,只需包含头文件即可使用,而glog可能需要额外的编译和配置步骤。
  • 功能glog提供了一些特定的功能,如条件日志记录和信号处理,这些在某些场景下可能非常有用
  • 使用场景
    • glog可能更适合那些对日志性能要求不是特别高,但需要一些特定功能的场景
    • spdlog则适合需要高性能日志记录和异步日志能力的应用程序
    • 在选择日志库时,开发者应根据项目的具体需求和性能要求来决定使用哪个库
      • 如果项目对日志性能有较高要求,或者需要异步日志记录来避免阻塞主线程,spdlog可能是更好的选择
      • 如果项目需要一些特定的日志功能,或者已经在使用glog且没有显著的性能问题,那么继续使用glog也是合理的

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/886599.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

关于 JVM 个人 NOTE

目录 1、JVM 的体系结构 2、双亲委派机制 3、堆内存调优 4、关于GC垃圾回收机制 4.1 GC中的复制算法 4.2 GC中的标记清除算法 1、JVM 的体系结构 "堆"中存在垃圾而"栈"中不存在垃圾的原因: 堆(Heap) 用途:堆主要用于存储对象实例和数组。在Java中…

Linux --入门学习笔记

文章目录 Linux概述基础篇Linux 的安装教程 ⇒ 太简单了&#xff0c;百度一搜一大堆。此处略……Linux 的目录结构常用的连接 linux 的开源软件vi 和 vim 编辑器Linux 的关机、开机、重启用户登录和注销用户管理添加用户 ⇒ ( useradd 用户名 ) &#xff08; useradd -d 制定目…

【Unity踩坑】Unity更新Google Play结算库

一、问题描述&#xff1a; 在Google Play上提交了app bundle后&#xff0c;提示如下错误。 我使用的是Unity 2022.01.20f1&#xff0c;看来用的Play结算库版本是4.0 查了一下文档&#xff0c;Google Play结算库的维护周期是两年。现在需要更新到至少6.0。 二、更新过程 1. 下…

Python | Leetcode Python题解之第454题四数相加II

题目&#xff1a; 题解&#xff1a; class Solution:def fourSumCount(self, A: List[int], B: List[int], C: List[int], D: List[int]) -> int:countAB collections.Counter(u v for u in A for v in B)ans 0for u in C:for v in D:if -u - v in countAB:ans countAB…

C++ | Leetcode C++题解之第454题四数相加II

题目&#xff1a; 题解&#xff1a; class Solution { public:int fourSumCount(vector<int>& A, vector<int>& B, vector<int>& C, vector<int>& D) {unordered_map<int, int> countAB;for (int u: A) {for (int v: B) {count…

Python并发编程(1)——Python并发编程的几种实现方式

更多精彩内容&#xff0c;请关注同名公众&#xff1a;一点sir&#xff08;alittle-sir&#xff09; Python 并发编程是指在 Python 中编写能够同时执行多个任务的程序。并发编程在任何一门语言当中都是比较难的&#xff0c;因为会涉及各种各样的问题&#xff0c;在Python当中也…

C0010.Qt5.15.2下载及安装方法

1. 下载及安装 Qt 添加链接描述下载地址&#xff1a;http://download.qt.io/ 选择 archive 目录 安装Qt **注意&#xff1a;**本人使用的是Qt5.15.2版本&#xff0c;可以按如下方法找到该版本&#xff1b;

Android Studio 新版本 Logcat 的使用详解

点击进入官方Logcat介绍 一个好的Android程序员要会使用AndroidStudio自带的Logcat查看日志&#xff0c;会Log定位也是查找程序bug的第一关键。同时Logcat是一个查看和处理日志消息的工具&#xff0c;它可以更快的帮助开发者调试应用程序。 步入正题&#xff0c;看图说话。 点…

msys2+gdb-multiarch+jlinkGDBServer的nrf52调试环境搭建

前言 刚拿到一块nrf52840的板子&#xff0c;为了方便以后的开发&#xff0c;先搭建一个调试环境&#xff0c;为方便以后回忆记录一下过程。 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 1.msys2命令行调用jlink工具 将jlink工具路径加入msys2的PAT…

华为云LTS日志上报至观测云最佳实践

华为云LTS简介 华为云云日志服务&#xff08;Log Tank Service&#xff0c;简称 LTS&#xff09;&#xff0c;用于收集来自主机和云服务的日志数据&#xff0c;通过海量日志数据的分析与处理&#xff0c;可以将云服务和应用程序的可用性和性能最大化&#xff0c;为您提供实时、…

【51单片机】点亮LED之经典流水灯

开发环境 开发板&#xff1a;普中51-单核-A2单片机&#xff1a;STC89C52RC&#xff08;双列直插40引脚 DIP40&#xff09;Keil uVision5 v9.61 最新版破解方法自行百度&#xff0c;相关文档和视频资料很多&#xff0c;我自己将这一操作记录下来当做博客发布&#xff0c;CSDN以…

【数据结构强化】应用题打卡

应用题打卡 数组的应用 对称矩阵的压缩存储 注意&#xff1a; 1. 2.上三角的行优先存储及下三角的列优先存储与数组的下表对应 上/下三角矩阵的压缩存储 注意&#xff1a; 上/下三角压缩存储是将0元素统一压缩存储&#xff0c;而不是将对角线元素统一压缩存储 三对角矩阵的…

King3399 SDK(ubuntu文件系统)编译简明教程

该文章仅供参考&#xff0c;编写人不对任务实验设备、人员及测量结果负责&#xff01;&#xff01;&#xff01; 0 引言 文章主要介绍King3399&#xff08;瑞芯微rk3399开发板&#xff0c;荣品&#xff09;官方SDK&#xff08;Ubuntu文件系统&#xff09;编译过程&#xff0c…

GaussDB关键技术原理:高弹性(六)

书接上文GaussDB关键技术原理&#xff1a;高弹性&#xff08;五&#xff09;从日志多流和事务相关方面对hashbucket扩容技术进行了解读&#xff0c;本篇将从扩容实践方面继续介绍GaussDB高弹性技术。 5 扩容实践 5.1 工具介绍 5.1.1 TPC-C TPC-C(全称Transaction Proces…

Leetcode 540. 有序数组中的单一元素

1.题目基本信息 1.1.题目描述 给你一个仅由整数组成的有序数组&#xff0c;其中每个元素都会出现两次&#xff0c;唯有一个数只会出现一次。 请你找出并返回只出现一次的那个数。 你设计的解决方案必须满足 O(log n) 时间复杂度和 O(1) 空间复杂度。 1.2.题目地址 https:…

[3.4]【机器人运动学MATLAB实战分析】PUMA560机器人逆运动学MATLAB计算

PUMA560是六自由度关节型机器人,其6个关节都是转动副,属于6R型操作臂。各连杆坐标系如图1,连杆参数如表1所示。 图1 PUMA560机器人的各连杆坐标系 表1 PUMA560机器人的连杆参数 用代数法对其进行运动学反解。具体步骤如下: 1、求θ1 PMUMA56

CSS 圆形边框与阴影

目录 1. 圆角边框 1.1 正圆 1.2 圆角矩形 1.3 任意圆角 1.4 某个圆角 2. 盒子阴影 3. 文字阴影 1. 圆角边框 1.1 正圆 1.2 圆角矩形 1.3 任意圆角 1.4 某个圆角 2. 盒子阴影 3. 文字阴影

Vue Mini基于 Vue 3 的小程序框架

新的小程序框架 https://vuemini.org/ Vue Mini 是一个基于 Vue 3 的小程序框架&#xff0c;它允许开发者利用 Vue 3 的强大功能来构建微信小程序。Vue Mini 的核心优势在于它的响应式系统和组合式 API&#xff0c;这些特性让开发者能够以一种更声明式、更高效的方式来编写和…

实景三维赋能城镇数字化规划

在数字化浪潮的推动下&#xff0c;城镇规划正经历着前所未有的变革。实景三维技术以其独特的优势&#xff0c;为城镇数字化规划提供了强大的技术支持。今天&#xff0c;我们将深入探讨实景三维技术如何赋能城镇数字化规划。 一、城镇规划面临的挑战 随着城镇化进程的加快&…

2024年研究生数学建模“华为杯”E题——肘部法则、k-means聚类、目标检测(python)、ARIMA、逻辑回归、混淆矩阵(附:目标检测代码)

文章目录 一、情况介绍二、思路情况二、代码展示三、感受 一、情况介绍 前几天也是参加了研究生数学建模竞赛&#xff08;也就是华为杯&#xff09;&#xff0c;也是和本校的两个数学学院的朋友在网上组的队伍。昨天&#xff08;9.25&#xff09;通宵干完论文&#xff08;一条…