目录

日志与策略模式

Log.hpp

 class LogStrategy基类

class ConsoleLogStrategy派生类

 classFileLogStrategy派生类

日志等级

获得时间戳

localtime_r函数详解

函数原型

struct tm结构的指针

Logger类(重点)

class LogMessage 日志信息类

std::stringstream

用法

重载 流输出运算符和析构函数


日志与策略模式

什么是设计模式?

IT行业这么火,涌入的人很多.俗话说林子大了啥鸟都有,大佬和菜鸡们两极分化的越来越严重.为了让 菜鸡们不太拖大佬的后腿,于是大佬们针对⼀些经典的常见的场景,给定了⼀些对应的解决方案,这个就是设计模式

日志认识

计算机中的日志是记录系统和软件运行中发生事件的文件,主要作用是监控运行状态、记录异常信 息,帮助快速定位问题并支持程序员进行问题修复。它是系统维护、故障排查和安全管理的重要工 具。

日志格式以下几个指标是必须得有的

  • 时间戳
  • 日志等级
  • 日志内容
  • 文件名(可选)
  • 行号(可选)
  • 进程/线程id(可选)

日志有现成的解决方案,如:spdlog、glog、Boost.Log、Log4cxx等等,我们依旧采用自定义日志的方式。 这里我们采用设计模式-策略模式来进行日志的设计

我们想要的日志格式如下:

[ 可读性很好的时间 ] [ 日志等级 ] [ 进程 pid] [ 打印对应日志的文件名 ][ 行号 ] - 消息内容,支持可 变参数

Log.hpp

首先创建Log.hpp文件,命名一个LogModule的空间域,我们将来的代码就在这里面实现

#ifndef __LOG_HPP__
#define __LOG_HPP__#include <iostream>
#include <cstdio>
#include <string>
#include <filesystem> //C++17
#include <sstream>
#include <fstream>
#include <memory>
#include <ctime>
#include <unistd.h>
#include "Mutex.hpp"  //互斥锁(自己实现的)namespace LogMudoule
{}#endif

 class LogStrategy基类

策略模式就是利用多态的特性,我们先定义出一个LogStrategy的基类,后面通过我们传的不同派生类来实现不同的模式,比如向屏幕打印的ConsoleLogStrategy派生类和向文件写入的FileLogStrategy派生类

using namespace MutexModule;const std::string gsep = "\r\n";// 策略模式,C++多态特性// 2. 刷新策略 a: 显示器打印 b:向指定的文件写入//  刷新策略基类class LogStrategy{public:virtual ~LogStrategy(){}virtual void SyncLog(const std::string &message)=0;   //控制台虚函数};

gsep是换行符,因为要使用锁来保护临界资源,这里Sync是同步的意思,基类的析构函数要记得加上virture。SyncLog是纯虚函数(强制派生类重写虚函数,因为不重写实例化不出对象。)

class ConsoleLogStrategy派生类

下面的ConsoleLogStrategy派生类实现的也十分简单

 // 显示器打印日志的策略  子类class ConsoleLogStrategy : public LogStrategy{public:ConsoleLogStrategy(){}void SyncLog(const std::string &message) override{LockGuard lockguard(_mutex);std::cout << message << gsep;//向屏幕打印消息}~ConsoleLogStrategy(){}private:Mutex _mutex;};

LockGuard 是我们封装的互斥锁的类,通过lockguard对象的创建来保护临界资源,析构后释放锁

不懂互斥锁的话可以去看看博客,我们的重点是完成日志,后续不再讲解锁

override是检查派生类虚函数是否重写了基类的某个虚函数。如果对于多态的知识不怎么了解的话,我十分推荐您去看这篇多态博客,点击多态就能进去阅读

 classFileLogStrategy派生类

下面的FileLogStrategy派生类稍微复杂一点但是并不难理解

// 文件打印日志的策略 : 子类const std::string defaultpath = "./log";const std::string defaultfile = "my.log";class FileLogStrategy : public LogStrategy{public:FileLogStrategy(const std::string &path = defaultpath, const std::string &file = defaultfile): _path(path),_file(file){LockGuard lockguard(_mutex);if (std::filesystem::exists(_path)){return;}try{std::filesystem::create_directories(_path);}catch (const std::filesystem::filesystem_error &e){std::cerr << e.what() << '\n';}}void SyncLog(const std::string &message) override{LockGuard lockguard(_mutex);const std::string &filename = _path + (_path.back() == '/' ? "" : "/") + _file; // "./log/" + "my.log"std::ofstream out(filename, std::ios::app);if (!out.is_open()){return;}out << message << gsep;out.close();}~FileLogStrategy(){}private:const std::string _path;const std::string _file;Mutex _mutex;};

理解一个类首先要去看它的私有成员,日志要向文件进行打印,首先要知道它的路径,其次就是它的文件名。所以能理解_path和_file两个成员变量

C++17 引入了一个重要的新特性:​文件系统库​ (std::filesystem),它提供了处理文件系统和路径的标准方法。这个库基于 Boost.Filesystem,并经过了标准化处理。在构造函数中完成_path和_file的初始化,然后利用std::filesystem::exists来检查路径是否存在,存在就返回,不存在就创建一个路径(在当前路径下的Log目录下)

SyncLog函数中filename就是我们要创建的文件名,它由_path和_file组合,

中间的三目操作符意思是如果 _path 的最后一个字符是 '/',那么整个三目运算符的结果就是空字符串 "",否则就是字符串 "/"。然后,这个结果被用于与 _path 和 _file 进行字符串拼接。

ofstream是 C++ 标准库中的一个类,全称为 ​Output File Stream​(输出文件流)。它用于将数据从程序写入到文件中,是文件操作的重要组成部分。

  • std::ofstream: 是C++标准库中用于文件输出的流类,定义在头文件 <fstream>中。
  • out: 是定义的输出文件流对象的名称。
  • filename: 是要打开的文件名(可以是字符串、字符数组等)。
  • std::ios::app: 是打开文件的模式标志,表示以追加模式(append)打开。
  • out.close():表示关闭文件

好了,到这里我们日志就实现了大概1/2,我们这里可以去检验一下我们写错没有

#include <iostream>
#include "Log.hpp"using namespace LogMudoule;
int main()
{// std::unique_ptr<LogStrategy> strategy = std::make_unique<ConsoleLogStrategy>();std::unique_ptr<LogStrategy> strategy = std::make_unique<FileLogStrategy>();strategy->SyncLog("hello");return 0;
}

strategy是智能指针由派生类 classFileLogStrategy完成赋值(多态)表示我们选择文件写入的模式

下面的图片也表明了我们的代码无误

日志等级

利用枚举实现日志等级

// 1. 形成日志等级
enum class LogLevel
{DEBUG,INFO,WARNING,ERROR,FATAL
};

我们使用的枚举实际上是0、1、2、3等数字,最终我们的日志是一串字符串所以我们还要类型转换一下,也是非常简单的。

std::string LeveltoStr(LogLevel level)
{switch (level){case LogLevel::DEBUG:return "DEBUG";case LogLevel::INFO:return "INFO";case LogLevel::WARNING:return "WARNING";case LogLevel::ERROR:return "ERROR";case LogLevel::FATAL:return "FATAL";default:return "UNKNOWN";}
}

获得时间戳

std::string GetTimeStamp()
{time_t curr = time(nullptr);struct tm curr_tm;localtime_r(&curr, &curr_tm);char timebuffer[128];snprintf(timebuffer, sizeof(timebuffer), "%4d-%02d-%02d %02d:%02d:%02d",curr_tm.tm_year + 1900,curr_tm.tm_mon + 1,curr_tm.tm_mday,curr_tm.tm_hour,curr_tm.tm_min,curr_tm.tm_sec);return timebuffer;
}

localtime_r函数详解

localtime_r是一个用于将时间戳转换为本地时间的线程安全函数。它是 localtime函数的可重入(reentrant)版本,在多线程编程中特别重要。

函数原型

struct tm *localtime_r(const time_t *timep, struct tm *result);

参数说明

  • timep: 指向 time_t类型时间的指针,表示从 1970-01-01 00:00:00 UTC 开始的秒数
  • result: 指向 struct tm结构的指针,用于存储转换后的时间信息

返回值

  • 成功时返回指向 result的指针
  • 失败时返回 NUL

struct tm结构的指针

struct tm {int tm_sec;    // 秒 [0, 59]int tm_min;    // 分 [0, 59]int tm_hour;   // 时 [0, 23]int tm_mday;   // 日 [1, 31]int tm_mon;    // 月 [0, 11] (0 = 一月)int tm_year;   // 年 (从1900开始)int tm_wday;   // 星期 [0, 6] (0 = 周日)int tm_yday;   // 年中的日 [0, 365]int tm_isdst;  // 夏令时标志 (正数: 是, 0: 否, 负数: 未知)
};

snprintf格式化输入函数,我们期望的年是4位,月是两位(不足补0——%02d)天、时分秒也是如此

实现效果如下

Logger类(重点)

我们在Logger类中完成整个日志的实现它的结构如下

class Logger
{
public:Logger(){SelectConsoleLogStrategy();}void SelectFileLogStrategy(){_fflush_strategy = std::make_unique<FileLogStrategy>();}void SelectConsoleLogStrategy(){_fflush_strategy = std::make_unique<ConsoleLogStrategy>();}class LogMessage{public:private:std::string _curr_time;LogLevel _level;pid_t _pid;std::string _src_name;int _line_number;std::string _loginfo; // 合并之后,一条完整的信息Logger &_logger;};LogMessage operator()(LogLevel level, std::string name, int line){return LogMessage(level, name, line, *this);}~Logger(){}private:std::unique_ptr<LogStrategy> _fflush_strategy;
};

为什么要在Logger类里实现一个内部类LogMessage最后再谈

这里Logger的构造函数是选择一种模式,我们这里设置的是向屏幕打印

Logger的私有成员变量是指向基类LogStrategy的智能指针(多态行为)在SelectConsoleLogStrategy()函数中完成赋值就是选择对应模式

我们在Logger类中还完成了一个仿函数-故意没写引用&,这个仿函数的作用是体现在接下来的LogMessage类里重载<<中

LogMessage operator()实际使用方式

// 传统方式可能这样写:
logger.log(DEBUG, "main.cpp", 42) << "This is a debug message";// 使用 operator() 可以这样写:
logger(DEBUG, "main.cpp", 42) << "This is a debug message";

class LogMessage 日志信息类

class LogMessage
{
public:LogMessage(LogLevel &level, std::string &src_name, int line_number, Logger &logger): _curr_time(GetTimeStamp()),_level(level),_pid(getpid()),_src_name(src_name),_line_number(line_number),_logger(logger){// 日志的左边部分,合并起来std::stringstream ss;ss << "[" << _curr_time << "] "<< "[" << LeveltoStr(_level) << "] "<< "[" << _pid << "] "<< "[" << _src_name << "] "<< "[" << _line_number << "] "<< "- ";_loginfo = ss.str();}template <typename T>LogMessage &operator<<(const T &info){// 日志的右半部分,可变的std::stringstream ss;ss << info;_loginfo += ss.str();return *this;}~LogMessage(){if (_logger._fflush_strategy){_logger._fflush_strategy->SyncLog(_loginfo);}}private:std::string _curr_time;LogLevel _level;pid_t _pid;std::string _src_name;int _line_number;std::string _loginfo; // 合并之后,一条完整的信息Logger &_logger;
};

老样子先看私有成员前五个对应开头提到的我们希望日志中包含的信息即

  • 时间戳
  • 日志等级
  • 日志内容
  • 文件名(可选)
  • 行号(可选)
  • 进程/线程id(可选)

我们需要把这五个信息合并成一条信息,这就是_loginfo的作用

我们希望logger(DEBUG, "main.cpp", 42) << "This is a debug message";时通过_logger来实现在屏幕上的打印,具体后面详说,所以在LogMessage中我们还定义Logger &_logger成员

LogMessage的构造函数就是输出一条完整信息,这里我们利用std::stringstream

std::stringstream

std::stringstream是 C++ 标准库中的一个类,它提供了内存中的字符串流处理功能,结合了字符串的灵活性和流的操作接口。它是 <sstream>头文件的一部分。

用法

std::stringstream ss;// 向流中插入数据
ss << "Hello, " << 42 << " " << 3.14 << " " << std::boolalpha << true;// 获取完整的字符串
std::string result = ss.str();
std::cout << result; // 输出: Hello, 42 3.14 true

在我们LogMessage中它的作用有两个

//1_loginfo = ss.str(); // 获取格式化后的字符串//2ss << info;          // 将任意类型转换为字符串_loginfo += ss.str(); // 追加到日志信息

重载 流输出运算符和析构函数

template <typename T>
LogMessage &operator<<(const T &info)
{// 日志的右半部分,可变的std::stringstream ss;ss << info;_loginfo += ss.str();return *this;
}
~LogMessage()
{if (_logger._fflush_strategy){_logger._fflush_strategy->SyncLog(_loginfo);}

日志的右半部分是可变的,也许是整数,也许是字符串还可能是其他类型,这里我们要设置一个模板

通过stringstream类的ss对象来实现类型转换并且追加到字符串_loginfo中,返回类型是LogMessage是因为我们期望<<能实现连续使用即下面这样

logger(LogLevel::DEBUG, "main.cc", 10) << "hello world" << 3.143;

我们在上面实现的仿函数没有使用引用,当我们向上面代码这样使用时,logger返回的是一份临时对象(类型是LogMessage包含_loginfo的信息)临时对象在下一行代码就会被析构,析构时就会调用SyncLog函数打印对应的信息在屏幕上

最后收尾工作

// 全局日志对象
Logger logger;// 使用宏,简化用户操作,获取文件名和行号
#define LOG(level) logger(level, __FILE__, __LINE__)
#define Select_Console_Log_Strategy() logger.SelectConsoleLogStrategy()
#define Select_File_Log_Strategy() logger.SelectFileLogStrategy()

定义全局对象这样我们就能直接选择模式了

Select_Console_Log_Strategy();
LOG(LogLevel::DEBUG) << "hello world" << 3.141;
LOG(LogLevel::DEBUG) << "hello world" << 3.142;Select_File_Log_Strategy();
LOG(LogLevel::DEBUG) << "hello world" << 3.143;
LOG(LogLevel::DEBUG) << "hello world" << 3.144;

这就是日志代码的讲解,下面是完整代码

#ifndef __LOG_HPP__
#define __LOG_HPP__#include <iostream>
#include <cstdio>
#include <string>
#include <filesystem> //C++17
#include <sstream>
#include <fstream>
#include <memory>
#include <ctime>
#include <unistd.h>
#include "Mutex.hpp"namespace LogMudoule
{using namespace MutexModule;const std::string gsep = "\r\n";// 策略模式,C++多态特性// 2. 刷新策略 a: 显示器打印 b:向指定的文件写入//  刷新策略基类class LogStrategy{public:virtual ~LogStrategy(){}virtual void SyncLog(const std::string &message)=0;   };// 显示器打印日志的策略  子类class ConsoleLogStrategy : public LogStrategy{public:ConsoleLogStrategy(){}void SyncLog(const std::string &message) override{LockGuard lockguard(_mutex);std::cout << message << gsep;}~ConsoleLogStrategy(){}private:Mutex _mutex;};// 文件打印日志的策略 : 子类const std::string defaultpath = "./log";const std::string defaultfile = "my.log";class FileLogStrategy : public LogStrategy{public:FileLogStrategy(const std::string &path = defaultpath, const std::string &file = defaultfile): _path(path),_file(file){LockGuard lockguard(_mutex);if (std::filesystem::exists(_path)){return;}try{std::filesystem::create_directories(_path);}catch (const std::filesystem::filesystem_error &e){std::cerr << e.what() << '\n';}}void SyncLog(const std::string &message) override{LockGuard lockguard(_mutex);const std::string &filename = _path + (_path.back() == '/' ? "" : "/") + _file; // "./log/" + "my.log"std::ofstream out(filename, std::ios::app);if (!out.is_open()){return;}out << message << gsep;out.close();}~FileLogStrategy(){}private:const std::string _path;const std::string _file;Mutex _mutex;};// 1. 形成日志等级enum class LogLevel{DEBUG,INFO,WARNING,ERROR,FATAL};std::string LeveltoStr(LogLevel level){switch (level){case LogLevel::DEBUG:return "DEBUG";case LogLevel::INFO:return "INFO";case LogLevel::WARNING:return "WARNING";case LogLevel::ERROR:return "ERROR";case LogLevel::FATAL:return "FATAL";default:return "UNKNOWN";}}std::string GetTimeStamp(){time_t curr = time(nullptr);struct tm curr_tm;localtime_r(&curr, &curr_tm);char timebuffer[128];snprintf(timebuffer, sizeof(timebuffer), "%4d-%02d-%02d %02d:%02d:%02d",curr_tm.tm_year + 1900,curr_tm.tm_mon + 1,curr_tm.tm_mday,curr_tm.tm_hour,curr_tm.tm_min,curr_tm.tm_sec);return timebuffer;}class Logger{public:Logger(){SelectFileLogStrategy();}void SelectFileLogStrategy(){_fflush_strategy = std::make_unique<FileLogStrategy>();}void SelectConsoleLogStrategy(){_fflush_strategy = std::make_unique<ConsoleLogStrategy>();}class LogMessage{public:LogMessage(LogLevel &level, std::string &src_name, int line_number, Logger &logger): _curr_time(GetTimeStamp()),_level(level),_pid(getpid()),_src_name(src_name),_line_number(line_number),_logger(logger){// 日志的左边部分,合并起来std::stringstream ss;ss << "[" << _curr_time << "] "<< "[" << LeveltoStr(_level) << "] "<< "[" << _pid << "] "<< "[" << _src_name << "] "<< "[" << _line_number << "] "<< "- ";_loginfo = ss.str();}template <typename T>LogMessage& operator <<(const T &info){// 日志的右半部分,可变的std::stringstream ss;ss << info;_loginfo += ss.str();return *this;}~LogMessage(){if(_logger._fflush_strategy){_logger._fflush_strategy->SyncLog(_loginfo);}}private:std::string _curr_time;LogLevel _level;pid_t _pid;std::string _src_name;int _line_number;std::string _loginfo; // 合并之后,一条完整的信息Logger &_logger;};LogMessage operator()(LogLevel level, std::string name, int line){return LogMessage(level, name, line, *this);}~Logger(){}private:std::unique_ptr<LogStrategy> _fflush_strategy;};// 全局日志对象Logger logger;// 使用宏,简化用户操作,获取文件名和行号#define LOG(level) logger(level, __FILE__, __LINE__)#define Select_Console_Log_Strategy() logger.SelectConsoleLogStrategy()#define Select_File_Log_Strategy() logger.SelectFileLogStrategy()
}
#endif

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【论文阅读】Sparse4D v2:Recurrent Temporal Fusion with Sparse Model

标题&#xff1a; Sparse4D v2&#xff1a;Recurrent Temporal Fusion with Sparse Model 作者&#xff1a; Xuewu Lin, Tianwei Lin, Zixiang Pei, Lichao Huang, Zhizhong Su motivation 在v1的基础上&#xff0c;作者发现长时序有更好的效果&#xff0c;但v1的计算量太大&am…

构建免费的音视频转文字工具:支持多语言的语音识别项目

在当今数字时代&#xff0c;音视频内容越来越多&#xff0c;但如何快速将其转换为文字一直是一个挑战。本项目提供了一个免费的解决方案&#xff0c;支持将视频和音频文件转换为文字&#xff0c;并且支持多语言识别。 一个支持中英文的音视频转文字工具&#xff0c;集成了 Vos…

【开题答辩全过程】以 基于SpringBootVue的智能敬老院管理系统为例,包含答辩的问题和答案

个人简介一名14年经验的资深毕设内行人&#xff0c;语言擅长Java、php、微信小程序、Python、Golang、安卓Android等开发项目包括大数据、深度学习、网站、小程序、安卓、算法。平常会做一些项目定制化开发、代码讲解、答辩教学、文档编写、也懂一些降重方面的技巧。感谢大家的…

Linux 830 shell:expect,ss -ant ,while IFS=read -r line,

[rootsamba caozx26]# scp /home/caozx26/pub root192.168.235.3:~/ root192.168.235.3s password: /home/caozx26/pub: not a regular file [rootsamba caozx26]# ls app km nntp.sh ntp.sh until1.sh 公共 图片 音乐 find.sh l2 ntp1.sh pub u…

​​​​​​​GPT-5发布引爆争议,奥特曼连夜回应!付费充值的Plus用户成最大赢家?

摘要&#xff1a; GPT-5发布后&#xff0c;社区口碑两极分化&#xff0c;从“强无敌”到“还我4o”的呼声并存。面对技术故障和用户质疑&#xff0c;OpenAI CEO萨姆奥尔特曼及团队火速回应&#xff0c;公布了一系列补救措施和未来计划。本文将带你速览这场风波始末&#xff0c;…

Python 操作 Redis 的客户端 - Redis Stream

Python 操作 Redis 的客户端 - Redis Stream1. Redis Stream2. Redis Commands2.1. CoreCommands.xadd() (生产端)2.2. CoreCommands.xlen() (生产端)2.3. CoreCommands.xdel() (生产端)2.4. CoreCommands.xrange() (生产端)2.5. RedisClusterCommands.delete()3. Redis Stream…

【Qt开发】按钮类控件(一)-> QPushButton

目录 1 -> 什么是 PushButton&#xff1f; 2 -> 相关属性 3 -> 代码示例 3.1 -> 带有图标的按钮 3.2 -> 带有快捷键的按钮 4 -> 总结 1 -> 什么是 PushButton&#xff1f; 在 Qt 框架中&#xff0c;QPushButton 是最基础且最常用的按钮控件之一&am…

Citrix 零日漏洞自五月起遭积极利用

安全研究员 Kevin Beaumont 披露了有关 CVE-2025-6543 的惊人细节&#xff0c;这是一个严重的 Citrix NetScaler 漏洞&#xff0c;在该公司发布补丁之前的几个月里&#xff0c;该漏洞被积极利用作为零日攻击。 Citrix 最初将其轻描淡写为简单的“拒绝服务”漏洞&#xff0c;但…

【系列08】端侧AI:构建与部署高效的本地化AI模型 第7章:架构设计与高效算子

第7章&#xff1a;架构设计与高效算子 要将AI模型成功部署到端侧&#xff0c;除了对现有模型进行压缩和优化&#xff0c;更根本的方法是在设计之初就考虑其在资源受限环境下的运行效率。本章将深入探讨如何设计高效的网络架构&#xff0c;以及如何理解并优化常用的核心算子。高…

42-Ansible-Inventory

文章目录Ansible基本概述手动运维时代&#xff08;原始社会&#xff09;自动化运维时代自动化运维工具的优势Ansible的功能及优点Ansible的架构Ansible的执行流程安装AnsibleAnsible配置文件生效顺序Ansible inventory主机清单Ansible基于免秘钥方式管理客户端小结Ansible-Adho…

Go语言runtime/trace工具全面解析

基本概念与功能 Go语言的runtime/trace是Go标准库中内置的性能分析工具,主要用于追踪和可视化Go程序的运行时行为。它能够记录程序执行期间的各种事件,包括goroutine调度、系统调用、垃圾回收(GC)、网络I/O、锁等待等关键信息。 trace工具的核心功能包括: goroutine生命周期…

Docker(自写)

Docker程序是跑在操作系统上的&#xff0c;而操作系统上又装了各种不同版本的依赖库和配置程序依赖环境&#xff0c;环境不同&#xff0c;程序就可能跑不起来&#xff0c;如果我们能将环境和程序一起打包docker就是可以将程序和环境一起打包并运行的工具软件基础镜像DockerFile…

深度拆解 OpenHarmony 位置服务子系统:从 GNSS 到分布式协同定位的全链路实战

1. 系统概述 OpenHarmony 的“定位子系统”就是硬件服务子系统集里的 “位置服务子系统”(Location SubSystem)。它向下对接 GNSS/GPS、基站、Wi-Fi 等定位模组,向上以 标准位置 API 形式为应用提供 实时位置、轨迹、地理围栏 等能力,并可与分布式软总线联动,实现 跨设备…

React Native基本用法

1&#xff0c;index调用registerComponent,把appName注入到React Native的根节点。 2&#xff0c;package.json是全局大管家&#xff0c;package-lock.json锁定版本&#xff0c;不会手动编辑&#xff0c;通过install安装 3&#xff0c; bebal.config.json bebal.config.json是翻…

LoraConfig target modules加入embed_tokens(64)

LoraConfig target modules加入embed_tokens 更好且成本更低的方法 嵌入层(embedding layer)的 lora_embedding_A 和 lora_embedding_B 头部(head)是否需加入目标模块列表 用户警告 解除权重绑定 解绑以后是随机权重,怎么办 更好且成本更低的方法 “有没有一种更好且成本…

笔记共享平台|基于Java+vue的读书笔记共享平台系统(源码+数据库+文档)

笔记共享平台|读书笔记共享平台系统 目录 基于Javavue的读书笔记共享平台系统 一、前言 二、系统设计 三、系统功能设计 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取 博主介绍&#xff1a;✌️大厂码农|毕设布道师&#xff…

【VSCode】VSCode为Java C/S项目添加图形用户界面

为Java C/S项目添加图形用户界面 现在我们来为它添加图形用户界面(GUI)。我将使用Java Swing库创建一个简单的GUI&#xff0c;因为它内置于Java标准库中&#xff0c;无需额外依赖。 客户端GUI实现 首先&#xff0c;我们将修改客户端代码&#xff0c;添加一个Swing GUI界面&…

【云原生】Docker 搭建Kafka服务两种方式实战操作详解

目录 一、前言 二、Docker 搭建kafka介绍 2.1 Docker 命令部署 2.2 使用Docker Compose 部署 2.3 使用 Docker Swarm 2.4 使用 Kubernetes 2.5 部署建议 三、Docker 搭建kafka操作方式一 3.1 前置准备 3.2 完整操作过程 3.2.1 创建docker网络 3.2.2 启动zookeeper容…

DBeaver中禁用PostgreSQL SSL的配置指南

在DBeaver中为PostgreSQL连接禁用SSL是一个常见的配置&#xff0c;特别是当你的数据库服务器未启用SSL或遇到连接问题时。我来为你详细讲解操作步骤和注意事项。 &#x1f6e0;️ DBeaver中禁用PostgreSQL SSL的配置指南 详细步骤 打开驱动设置&#xff1a;在DBeaver中创建新的…

数组去重【JavaScript】

数组去重&#xff0c;并且key和val相同的对象视为相同的&#xff0c;需要去重。主函数&#xff1a;/*** 数组去重* 两个属性相同的对象也认为是相同的* param {Array} arr* return {Array} */ function uniqueArray(arr) {const result []// outer: 标签&#xff0c;标记外层循…