C++ 日志系统实战第四步:设计与代码实现详解
全是通俗易懂的讲解,如果你本节之前的知识都掌握清楚,那就速速来看我的项目笔记吧~
本文将加入项目代码编写!
目录
日志系统框架设计
模块划分
模块关系图
代码设计
实用类设计
日志等级设计
日志消息类
日志输出格式
日志落地(LogSink)类设计(简单工厂模式)
日志系统框架设计
本项目实现的是一个多日志器日志系统,主要实现的功能是让程序员能够轻松的将程序运行日志信息落地到指定的位置,且支持同步与异步两种方式的日志落地方式。
项目的框架设计将项目分为以下几个模块来实现。
模块划分
- 日志等级模块:对输出日志的等级进行划分,以便于控制日志的输出,并提供等级枚举转字符串功能。
- OFF:关闭
- DEBUG:调试,调试时的关键信息输出。
- INFO:提示,普通的提示型日志信息。
- WARN:警告,不影响运行,但是需要注意一下的日志。
- ERROR:错误,程序运行出现错误的日志
- FATAL:致命,一般是代码异常导致程序无法继续推进运行的日志
- 日志消息模块:中间存储日志输出所需的各项要素信息
- 时间:描述本条日志的输出时间。
- 线程 ID:描述本条日志是哪个线程输出的。
- 日志等级:描述本条日志的等级。
- 日志数据:本条日志的有效载荷数据。
- 日志文件名:描述本条日志在哪个源码文件中输出的。
- 日志行号:描述本条日志在源码文件的哪一行输出的。
- 日志消息格式化模块:设置日志输出格式,并提供对日志消息进行格式化功能。
- 系统的默认日志输出格式:% d {% H:% M:% S}% T% t% T% p% T% c% T% f:% l% T% m% n
- 示例:-> 13:26:32 [2343223321] [FATAL] [root] main.c:76 套接字创建失败 \n
- % d {% H:% M:% S}:表示日期时间,花括号中的内容表示日期时间的格式。
- % T:表示制表符缩进。
- % t:表示线程 ID
- % p:表示日志级别
- % c:表示日志器名称,不同的开发组可以创建自己的日志器进行日志输出,小组之间互不影响。
- % f:表示日志输出时的源代码文件名。
- % l:表示日志输出时的源代码行号。
- % m:表示给与的日志有效载荷数据
- % n:表示换行
- 设计思想:设计不同的子类,不同的子类从日志消息中取出不同的数据进行处理。
- 日志消息落地模块:决定了日志的落地方向,可以是标准输出,也可以是日志文件,也可以滚动文件输出……
- 标准输出:表示将日志进行标准输出的打印。
- 日志文件输出:表示将日志写入指定的文件末尾。
- 滚动文件输出:当前以文件大小进行控制,当一个日志文件大小达到指定大小,则切换下一个文件进行输出
- 后期,也可以扩展远程日志输出,创建客户端,将日志消息发送给远程的日志分析服务器。
- 设计思想:设计不同的子类,不同的子类控制不同的日志落地方向。
- 日志器模块:
- 此模块是对以上几个模块的整合模块,用户通过日志器进行日志的输出,有效降低用户的使用难度。
- 包含有:日志消息落地模块对象,日志消息格式化模块对象,日志输出等级
- 日志器管理模块:
- 为了降低项目开发的日志耦合,不同的项目组可以有自己的日志器来控制输出格式以及落地方向,因此本项目是一个多日志器的日志系统。
- 管理模块就是对创建的所有日志器进行统一管理。并提供一个默认日志器提供标准输出的日志输出。
- 异步线程模块:
- 实现对日志的异步输出功能,用户只需要将输出日志任务放入任务池,异步线程负责日志的落地输出功能,以此提供更加高效的非阻塞日志输出。
模块关系图
代码设计
实用类设计
/*
实用工具类的实现
1.获取系统时间
2.判断文件是否存在
3.获取文件所在路径
4.创建目录
*/#include <iostream>
#include <ctime>namespace mylog
{namespace util{// 获取系统时间class Date{public:static size_t getTime()//返回当前时间戳{return (size_t)time(nullptr);}};// 判断文件是否存在class File{public://静态接口,直接使用,不用实例化对象static bool exists(const std::string& pathname);static std::string path(const std::string& filename);static bool createDir(const std::string& dirname);};}
}
判断文件是否存在函数(exists):
access
函数是 C 语言中的一个文件访问权限检测函数,定义在 <unistd.h>
头文件中 。
- 函数原型:
int access(const char *pathname, int mode);
- 参数
pathname
:要检测的文件或目录的路径名。mode
:指定访问模式,常用取值有:R_OK
:检查是否有读权限。W_OK
:检查是否有写权限。X_OK
:检查是否有执行权限。F_OK
:检查文件是否存在。
- 返回值:调用成功时返回 0 ;调用失败时返回 -1 ,同时会设置
errno
来指示错误原因,比如ENOENT
表示文件不存在等。
这是Linux下的系统调用,为了项目在各个操作系统都支持。我们可以使用stat函数
stat
函数是 C 语言中用于获取文件状态信息的函数 ,定义在<sys/types.h>
、<sys/stat.h>
和<unistd.h>
头文件中。
函数原型
int stat(const char *pathname, struct stat *buf);
参数
pathname
:指向要获取状态信息的文件路径名。buf
:是一个struct stat
结构体指针,用于存储获取到的文件状态信息。struct stat
包含文件的 inode 编号、文件类型、权限、大小、修改时间等众多属性。
返回值
成功调用时返回 0 ;失败时返回 -1 ,并设置errno
来指示错误类型,如ENOENT
表示文件不存在。
#include <sys/stat.h>static bool exists(const std::string& pathname)
{// return access(pathname.c_str(), F_OK) != -1; Linux下可用struct stat buffer;if (stat(pathname.c_str(), &buffer) == 0) {return true;} else {return false;}
}
获取文件的所在路径函数(path):
就是查找最后一个‘/’
static std::string path(const std::string& filename)
{size_t pos = filename.find_last_of("/\\");if (pos == std::string::npos) {return "."; // 当前目录下} else {return filename.substr(0, pos);}
}
创建目录(creatdir):
对于.abc/b/a.text 要找到前面的才可以创建后面的
mkdir
函数用于创建一个新的目录,在不同操作系统的 C 语言标准库中均有提供。
函数原型
在 POSIX 系统(如 Linux、macOS)中,其原型定义在 <sys/stat.h>
和 <sys/types.h>
头文件中:
int mkdir(const char *pathname, mode_t mode);
参数说明
pathname
:要创建的目录的路径名,可以是绝对路径或相对路径。mode
:指定新目录的访问权限,是一个八进制数,如0777
表示所有用户都有读、写、执行权限。
返回值
- 成功时返回 0。
- 失败时返回 -1,并设置
errno
以指示错误原因,常见的错误包括:
EEXIST
:指定路径的目录已存在。EACCES
:没有足够权限创建目录。ENOENT
:路径中的父目录不存在。
static bool createDir(const std::string &dirname){if (dirname.empty()){return false; // 空字符串无法创建目录}int pos = 0, index = 0;while (index <= dirname.size()){pos = dirname.find_first_of("/\\", index);if (pos == std::string::npos){// 没有/或\,说明是当前目录if (mkdir(dirname.c_str(), 0777) != 0){std::cerr << "Failed to create directory: " << dirname << std::endl;return false;}index = dirname.size() + 1;}else{std::string parent_str = dirname.substr(0, pos + 1);if (!exists(parent_str)){if (mkdir(parent_str.c_str(), 0777) != 0){std::cerr << "Failed to create directory: " << parent_str << std::endl;return false;}}index = pos + 1;}}return true;}
日志等级设计
#pragma oncenamespace logs {enum class Level{Unknown=0,Debug=1,Info=2,Warning=3,Error=4,Fatal=5,Off=6};class LevelHelper{public:static const char* ToString(Level level){switch(level){case Level::Debug:return "Debug";case Level::Info:return "Info"; case Level::Warning:return "Warning";case Level::Error:return "Error";case Level::Fatal:return "Fatal";case Level::Off:return "Off";default:return "Unknown";}}};
} // namespace logs
日志消息类
message.hpp
#pragma once#include<iostream>
#include<string>
#include"util.hpp"
#include"level.hpp"
#include<thread>namespace mylog {struct LogMessage {size_t ctime_;// creation timeLevel level_;// log levelstd::thread::id tid_;// process idstd::string file_;// source file namesize_t line_;// source line numberstd::string message_;// log messagestd::string logger_name_;// logger nameLogMessage(Level level, const char* file, size_t line, const char* message, const char* logger_name): ctime_(util::Date::getTime()), level_(level), tid_(std::this_thread::get_id()), file_(file), line_(line), message_(message), logger_name_(logger_name){}};
}
日志输出格式
格式化子串实现:
format.hpp:
#pragma once#include "message.hpp"
#include <memory>
#include <ctime>
#include <cassert>
#include <unordered_set>
#include <sstream>
#include <vector>namespace mylog
{// 基类用来定义日志格式class Format{public:using ptr = std::shared_ptr<Format>;virtual void format(std::ostream &os, const LogMessage &msg) = 0;};// 具体的日志格式class MsgFormat : public Format{public:void format(std::ostream &os, const LogMessage &msg) override{os << msg.message_;}};class LevelFormat : public Format{public:void format(std::ostream &os, const LogMessage &msg) override{os << LevelHelper::ToString(msg.level_);}};class TimeFormat : public Format{public:TimeFormat(const std::string &format = "%H:%M:%S") : formatTime(format){}void format(std::ostream &os, const LogMessage &msg) override{struct tm t;localtime_r(&msg.ctime_, &t);char buf[64] = {0};strftime(buf, sizeof(buf), formatTime.c_str(), &t);os << buf;}private:std::string formatTime;};class FileFormat : public Format{public:void format(std::ostream &os, const LogMessage &msg) override{os << msg.file_;}};class LineFormat : public Format{public:void format(std::ostream &os, const LogMessage &msg) override{os << msg.line_;}};class LoggerFormat : public Format{public:void format(std::ostream &os, const LogMessage &msg) override{os << msg.logger_;}};class ThreadIdFormat : public Format{public:void format(std::ostream &os, const LogMessage &msg) override{os << msg.tid_;}};class TableFormat : public Format{public:void format(std::ostream &os, const LogMessage &msg) override{os << "\t";}};class NewLineFormat : public Format{public:void format(std::ostream &os, const LogMessage &msg) override{os << "\n";}};class OtherFormat : public Format{public:OtherFormat(const std::string &other = "") : other(other){}void format(std::ostream &os, const LogMessage &msg) override{os << other;}private:std::string other;};/*%d:表示日期 ,包含子格式 {%H:%M:%S}%t:表示线程 ID%c:表示日志器名称%f:表示源码文件名%l:表示源码行号%p:表示日志级别%T:表示制表符缩进%m:表示主体消息%n:表示换行*/class FormatBuilder // 日志格式构造器{public:FormatBuilder(const std::string &pattern = "[%d{%H:%M:%S}][%t][%c][%f:%l][%p]%T%m%n") : pattern_(pattern){assert(parsePattern());}// 对msg进行格式化void format(std::ostream &os, const LogMessage &msg){for (auto &f : formats){f->format(os, msg);}}std::string format(const LogMessage &msg){std::ostringstream oss;format(oss, msg);return oss.str();}private:// 假设此函数在 FormatBuilder 类中bool parsePattern(){size_t pos = 0; // 表示处理到的位置std::string key; // 表示格式化关键字std::string value; // 表示格式化值std::vector<std::pair<std::string, std::string>> items; // 表示格式化项while (pos < pattern_.size()){// 1. 如果不是%,说明是普通字符while (pos < pattern_.size() && pattern_[pos] != '%'){value += pattern_[pos];pos++;}// 2. 如果是%,说明是格式化字符if (pos + 1 < pattern_.size() && pattern_[pos + 1] == '%'){// 2.1 如果是%%,说明是%value += pattern_[pos];pos += 2;}if (!value.empty()){items.emplace_back(std::make_pair("", value));value.clear();continue;}// 3. 如果是格式化字符if (pos < pattern_.size() && pattern_[pos] == '%'){pos++; // 跳过%// 合并检查:跳过 '%' 后,统一检查是否超出字符串范围if (pos >= pattern_.size()){std::cerr << "Error: Unexpected end of pattern after % at position " << pos << "." << std::endl;return false;}key = pattern_[pos];pos++;if (pos < pattern_.size() && pattern_[pos] == '{'){pos++; // 跳过{// 3.1 如果是{,说明是子格式化while (pos < pattern_.size() && pattern_[pos] != '}'){value += pattern_[pos];pos++;}if (pos >= pattern_.size() || pattern_[pos] != '}'){std::cerr << "Error: Missing closing brace for sub - pattern starting at position " << pos - value.length() - 1 << "." << std::endl;return false;}pos++; // 跳过}}// 检查是否为不认识的格式化关键字static const std::unordered_set<char> validKeys = {'d', 't', 'c', 'f', 'l', 'p', 'T', 'm', '%', 'n'};if (validKeys.find(key[0]) == validKeys.end()){std::cerr << "Error: Unknown format keyword '" << key << "' at position " << pos - key.length() - 1 << "." << std::endl;return false;}items.emplace_back(std::make_pair(key, value));key.clear();value.clear();}}// 4. 创建格式化对象for (auto &item : items){formats.push_back(createItem(item.first, item.second));}return true;}// 创建格式化对象Format::ptr createItem(const std::string &key, const std::string &value){if (key == "d"){return std::make_shared<TimeFormat>(value);}else if (key == "t"){return std::make_shared<ThreadIdFormat>();}else if (key == "c"){return std::make_shared<LoggerFormat>();}else if (key == "f"){return std::make_shared<FileFormat>();}else if (key == "l"){return std::make_shared<LineFormat>();}else if (key == "p"){return std::make_shared<LevelFormat>();}else if (key == "T"){return std::make_shared<TableFormat>();}else if (key == "m"){return std::make_shared<MsgFormat>();}else if (key == "n"){return std::make_shared<NewLineFormat>();}else if (key.empty()){return std::make_shared<OtherFormat>(value);}else{return std::make_shared<OtherFormat>(key);}}private:std::string pattern_; // 格式化字符串std::vector<Format::ptr> formats;};
}
// 解析格式化字符串:
日志落地(LogSink)类设计(简单工厂模式)
sink.hpp
#pragma once#include "util.hpp"
#include <memory>
#include <fstream>
#include <sstream>
#include<assert.h>
namespace mylog
{/*日志落地实现1.抽象落地基类2.根据不同的落地方式派生出不同的落地类3.简单工厂模式*/class LogSink{public:using ptr = std::shared_ptr<LogSink>;LogSink() {}virtual void log(const char *msg, size_t len) = 0;virtual ~LogSink() {}};// 向标准输出流输出日志class StdoutSink : public LogSink{public:void log(const char *msg, size_t len) override{std::cout.write(msg, len);}};// 向文件中输出日志class FileSink : public LogSink{public:FileSink(std::string pathname) : pathname_(pathname){// 1.创建文件所在目录if (!util::File::createDir(util::File::path(pathname_))){std::cerr << "create dir failed" << std::endl;}// 2.打开文件ofs_.open(pathname_.c_str(), std::ios::binary | std::ios::app); // 以二进制,追加方式打开文件assert(ofs_.is_open());}void log(const char *msg, size_t len) override{ofs_.write(msg, len);}private:std::string pathname_;std::ofstream ofs_; // ofstream对象,用于输出日志到文件};// 向滚动文件中输出日志(按文件大小)class RollSinkbySize : public LogSink{public:RollSinkbySize(std::string basename, size_t max_size) : basename_(basename), max_size_(max_size){std::string pathname = create_new_file();// 1.创建文件所在目录util::File::createDir(util::File::path(pathname));// 2.打开文件ofs_.open(pathname.c_str(), std::ios::binary | std::ios::app); // 以二进制,追加方式打开文件assert(ofs_.is_open());cur_size_ = ofs_.tellp(); // 获取当前文件大小}void log(const char *msg, size_t len) override{if(cur_size_ > max_size_) // 当前文件大小 + 日志大小 > 最大文件大小{ofs_.close(); // 关闭当前文件std::string pathname = create_new_file();// 1.创建文件所在目录util::File::createDir(util::File::path(pathname));// 2.打开文件ofs_.open(pathname.c_str(), std::ios::binary | std::ios::app); // 以二进制,追加方式打开文件assert(ofs_.is_open());cur_size_ = 0; // 重置当前文件大小}ofs_.write(msg, len);assert(ofs_.good());cur_size_ += len; // 更新当前文件大小}private:std::string create_new_file() // 创建新的日志文件{time_t t = util::Date::getTime();struct tm lt;localtime_r(&t, <);std::stringstream filename;filename << basename_ << "-" << lt.tm_year + 1900 << "-" << lt.tm_mon + 1 << "-" << lt.tm_mday << "-" << lt.tm_hour << lt.tm_min << lt.tm_sec <<"-"<<name_count_++ << ".log";return filename.str();}size_t name_count_ = 0; // 日志文件名计数器std::string basename_; // 通过基础文件名 + 时间戳生成新的日志文件名size_t max_size_;std::ofstream ofs_; // ofstream对象,用于输出日志到文件size_t cur_size_; // 当前日志文件大小};//工厂模式class SinkFactory{public:template<typename SinkType, typename... Args>static LogSink::ptr create(Args &&... args){return std::make_shared<SinkType>(std::forward<Args>(args)...);}};
}
后续还有代码,期待共同实现~
如果你对日志系统感到兴趣,欢迎关注我👉【A charmer】
相关文章:
C++ 日志系统实战第四步:设计与代码实现详解
全是通俗易懂的讲解,如果你本节之前的知识都掌握清楚,那就速速来看我的项目笔记吧~ 本文将加入项目代码编写! 目录 日志系统框架设计 模块划分 模块关系图 代码设计 实用类设计 日志等级设计 日志消息类 日志输出格式 日志落地(L…...
DeepSeek API接口调用示例(开发语言C#,替换其中key值为自己的key值即可)
示例: DeepSeek官方接口说明文档:对话补全 | DeepSeek API Docs 官网暂未提供C#代码实现:(以下为根据CURL接口C#代码调用) using System; using System.Collections.Generic; using System.Linq; using System.Text; …...
PyTorch常用命令(可快速上手PyTorch的核心功能,涵盖从数据预处理到模型训练的全流程)
以下是PyTorch常用命令的分类整理,涵盖张量操作、模型构建、数据加载、训练流程等核心内容: 1. 张量操作 创建张量 x torch.tensor([1, 2, 3]) # 从数据创建 x torch.zeros(3, 3) # 全零张量 x torch.ones(3, 3) …...
软开错题(二)
SNMP的传输层协议是UDP Linux操作系统中通常使用apache作为web服务器,其默认的web站点的目录是 /var/www/html 归并排序不是原地排序 邻接表:包含n个头节点和e个表节点,其广度和深度遍历的时间复杂度都是O(ne) grant使用方式 grant 权限 …...
拉削丝锥,螺纹类加工的选择之一
在我们的日常生活中,螺纹连接无处不在,从简单的螺丝钉到复杂的机械设备,都离不开螺纹的精密加工。今天,给大家介绍一种的螺纹刀具——拉削丝锥: 一、拉削丝锥的工作原理 拉削丝锥,听起来有点陌生吧&#…...
【Python Number(数字)】
Python 中的数字类型是编程的基础元素,用于表示数值数据并进行数学运算。以下是 Python 数字类型的核心知识点: 一、基础数字类型 整数(int) 表示整数值,例如 42, -7, 0支持任意精度(无大小限制)…...
大疆无人机SDR 链路
在大疆无人机或通信技术领域,SDR 是 Software-Defined Radio(软件定义无线电) 的缩写,而 SDR 链路 指的是一种通过软件编程实现无线通信功能的技术链路。其核心是通过软件动态调整通信参数&#…...
linux基础学习--linux磁盘与文件管理系统
linux磁盘与文件管理系统 1.认识linux系统 1.1 磁盘组成与分区的复习 首先了解磁盘的物理组成,主要有: 圆形的碟片(主要记录数据的部分)。机械手臂,与在机械手臂上的磁头(可擦写碟片上的内容)。主轴马达,可以转动碟片,让机械手臂的磁头在碟片上读写数据。 数据存储…...
【Qt】Qt 构建系统详解:qmake 入门到项目实战
Qt 构建系统详解:qmake 入门到项目实战 本文将系统介绍 Qt 构建工具 qmake 的用法,并通过一个完整的项目结构示例,帮助你掌握 .pro 文件编写、子项目管理、模块依赖等核心技能。 🧭 一、什么是 qmake? qmake 是 Qt 提…...
Laravel 12 实现验证码功能
Laravel 12 实现验证码功能 在 Laravel 12 中实现验证码功能可以通过多种方式,以下是几种常见的方法: 方法一:使用 Captcha 包(推荐) 首先安装 mews/captcha 包: composer require mews/captcha发布配置…...
深入解析Http11AprProtocol:Tomcat高性能通信的底层原理
HTTP/1.1 协议作为 Web 通信的基础标准,其实现效率直接影响服务器性能。Apache Tomcat 作为 Java 生态中最流行的 Servlet 容器,提供了多种 HTTP 协议实现方案,其中基于 Apache Portable Runtime(APR)的 Http11AprProt…...
HTTP请求与缓存、页面渲染全流程
文章目录 前言**1. HTTP请求与缓存处理****缓存机制**• 强缓存(Cache-Control / Expires)• 协商缓存(Last-Modified / ETag) **2. 服务器响应与数据解析****3. HTML DOM 构建****4. CSSOM 构建****5. 渲染树(Render …...
HTB - Eureka记录
HTB - Eurekahttps://mp.weixin.qq.com/s/r1WmZXNR6YkvnwP40liciA...
CentOS 7 基础环境安装脚本
🌟 CentOS 7 基础环境安装脚本使用文档 🧰 一键部署!助你在 CentOS 7 系统上快速构建高效开发环境。 开源地址:https://github.com/hahaha-zsq/Shortcut-Script CentOS 7 基础环境安装脚本使用 📦 项目结构一览 ./ ├…...
【Python 模块】
Python 中的模块(Module)是组织代码的核心方式,通过将相关函数、类和变量封装到独立文件中,实现代码复用和结构化管理。以下是模块的核心知识点: 一、基础概念 1. 模块定义 任何 .py 文件都是一个模块模块名即文件名…...
极狐Gitlab 如何创建并使用子群组?
极狐GitLab 是 GitLab 在中国的发行版,关于中文参考文档和资料有: 极狐GitLab 中文文档极狐GitLab 中文论坛极狐GitLab 官网 子群组 (BASIC ALL) 您可以将极狐GitLab 群组组织成子群组。您可以使用子群组: 内部和外部组织分开。因为每个子…...
【MCP】服务端搭建(python和uv环境搭建、nodejs安装、pycharma安装)
【MCP】服务端搭建(python和uv环境搭建、nodejs安装、pycharma安装) 服务端搭建(1)python和uv环境搭建(2)nodejs安装(3)pycharm安装 服务端搭建 (1)python和…...
【疑难杂症2025-003】Java-mvn项目在gitlab-ci构建镜像时遇到的问题和解决方案
本文由Markdown语法编辑器编辑完成. 1.背景: 之前从同事手里接手了一个java的项目,是用maven构建项目的.由于我们的服务都是基于docker来部署的,因此这个java项目也是要编译成docker image然后发布.但是之前一直都是…...
AI与Web3.0:去中心化智能合约的未来
AI与Web3.0:去中心化智能合约的未来 系统化学习人工智能网站(收藏):https://www.captainbed.cn/flu 文章目录 AI与Web3.0:去中心化智能合约的未来摘要引言1.1 技术演进背景1.2 行业格局分化 技术架构对比2.1 智能合约…...
记录学习的第三十五天
今天主攻单源最短路Dijkstra算法。不过,还是没有完全掌握。 首先是书本的例题我理解了一遍。 然后其实在力扣上做了三道题的,但是我看题解的情况就不太会。然后试着用上面的方法敲了一下↓的题,但是不对啊,我也不知道为什么呀。...
虚拟现实(VR)与增强现实(AR)在教育领域的应用:开启沉浸式学习新时代
前言 随着科技的飞速发展,虚拟现实(VR)和增强现实(AR)技术逐渐从游戏和娱乐领域走向教育领域。传统的教育模式主要依赖于书本、黑板和课堂讲解,这种模式虽然有效,但往往难以激发学生的学习兴趣和…...
线性代数之矩阵运算:驱动深度学习模型进化的数学引擎
目录 一、矩阵运算的基本概念与类型 二、矩阵运算在深度学习中的核心作用 三、典型深度学习模型中的矩阵运算实现 四、矩阵运算的优化与加速 五、未来发展趋势与挑战 矩阵运算是线性代数的核心组成部分,也是深度学习模型构建和优化的数学基础。从基本的前向传播到复杂的注…...
Spring AI(1)—— 基本使用
Spring AI 是一个用于 AI 工程的应用程序框架。 其目标是将 Spring 生态系统设计原则应用于 AI 领域。 Spring AI 提供以下功能: 支持所有主要的 AI 模型提供商,例如 Anthropic、OpenAI、Microsoft、Amazon、Google 和 Ollama等。支持跨 AI 提供商对同…...
深入浅出HTML:构建现代网页的基石
深入浅出HTML:构建现代网页的基石 引言 在数字世界的基石中,HTML(HyperText Markup Language)始终扮演着不可替代的角色。作为万维网的核心语言,HTML经历了30年的演变,从简单的文档标记发展到支持复杂Web…...
ai说什么是注解,并以angular ts为例
在编程中,注解(Annotation) 是一种特殊的语法结构,用于为代码添加元数据(metadata),从而在不修改代码逻辑的情况下,提供额外的信息或指示编译器、框架、工具如何处理这些代码。注解通…...
【存储管理—动态不等长存储资源分配算法】
文章目录 一、实验目的二、实验内容与设计思想实验内容设计思路 三、实验代码实现四、总结 一、实验目的 理解动态异长存储分区资源管理,掌握所需数据结构和管理程序,了解各种存储分配算法的优点和缺点。 二、实验内容与设计思想 实验内容 1.分析uni…...
【25软考网工】第五章(8)路由协议RIP、OSPF
目录 一、路由协议--RIP 1. RIP协议 1)RIPv1与RIPv2对比 2. 距离矢量路由协议 1)距离矢量路由协议特点 2)RIP路由的度量值 3)路由优先级 4)RIP防环机制 3. 应用案例 1)例题:RIPv1与RIPv2区别 2&…...
日常开发中,iOS 性能调优我们怎么做?
日常开发中,iOS 性能调优我们怎么做?聊聊我用过的几款工具 最近在给一个 iOS 视频类 App 做性能优化,过程中踩了不少坑,也用了一些不错的工具,今天就以一个开发者视角随便聊聊我在调试过程中的一些经验。 一、性能问…...
JNDI 注入原理解析
文章目录 JNDI基础概述JNDI SPI命名服务目录服务 JNDI演示查询 DNS 服务查询 LDAP 服务条目动态协议切换 JNDI 注入漏洞 JNDI基础 概述 JNDI(Java Naming and Directory Interface)是Java提供的标准命名和目录接口,通过统一的API使应用程序…...
Android开发-视图基础
在Android应用开发中,视图(View)是构建用户界面的基本元素。无论是按钮、文本框还是复杂的自定义控件,它们都是基于View类或其子类实现的。掌握视图的基础知识对于创建功能强大且美观的应用至关重要。本文将深入探讨Android中的视…...
Prometheus实战教程:k8s平台-Redis监控案例
以下是 Prometheus 自动发现 Redis 实例的完整 YAML 文件示例,适用于生产环境。该配置包括: Redis 部署:运行 Redis 实例。Redis Exporter:用于暴露 Redis 指标。Prometheus 自动发现:通过 Kubernetes 服务发现自动抓…...
Prompt Engineering 提示词工程学习
一、Prompt Engineering 简介 Prompt Engineering 是设计和优化输入提示(Prompt)以获得预期输出的过程。在与大型语言模型(如 GPT-4)交互时,如何构造提示会显著影响模型的回答质量。 二、Prompt 的重要性 提高生成准确性:通过正确的 Prompt 引导,模型能够更好地理解用…...
数造科技携 DataBuilder 亮相安徽科交会,展现“DataOps +AI”双引擎魅力
近日,数造科技受邀参加第三届中国(安徽)科技创新成果转化交易。 作为国内领先的数据开发与治理平台提供商,数造科技携带其核心产品 DataBuilder 精彩亮相 “新一代信息技术展区”,吸引了众多参会者的目光。 关于 DataB…...
Linux/AndroidOS中进程间的通信线程间的同步 - 共享内存
在之前的文章中介绍了允许无关进程共享内存区域以便执行 IPC 的技术:共享文件映射。但他存在一些不足。 使用一个共享文件映射来进行 IPC 要求创建一个磁盘文件,即使无需对共享区域进行持久存储也需要这样做。除此之外,这种技术还会带来一些…...
ES6入门---第三单元 模块七: Proxy的使用+Reflect的使用
proxy: 代理 扩展(增强)对象、方法(函数)一些功能 比如: Vue Vue.config.keyCodes.enter65 Proxy作用: 比如vue中拦截 预警、上报、扩展功能、统计、增强对象等等 proxy是设计模式一种, 代理模式 语法: new Proxy(target, handler); …...
【JAVA】BigDecimal判断是否为0, / by zero的问题修复
bug场景 我要处理一个任务完成率的计算,任务完成率pct 实际值 real / 任务值 task 进入计算前,我需要判断task是否为空,或者为0,防止除法出错。 之前使用了equal方法 if(!task.equals(BigDecimal.ZERO))//开始计算因为刚开始测…...
从 “机器人 +“ 到 “+ 机器人“:算力政策撬动的产业生态革命
在深圳光明科学城的云端算力平台上,数据以每秒千万次的速度流动,这里每年发放的 600 万元算力补贴,正如同催化剂般激活着人形机器人产业的深层变革。当广东将 "算力券" 政策精准嵌入珠三角制造体系,一场从 "单机智…...
如何在24G显存机器上搭建一个超过gpt效果的DeepSeek-R1?
DeepSeek-R1蒸馏模型概述与应用指南  引言 DeepSeek-R1作为一款先进的AI推理模型,在性能上已超越GPT-4o和Claude-3.5等主流开源模型。为满足更广泛应用…...
seamless_communication,facebook推出的开源语音翻译项目
Seamless Communication是由Facebook Research开发的一个开源项目,旨在提供先进的语音和文本翻译功能,支持多国语音。 今天试着来复现下。 1、首先下载代码。 git clone https://github.com/facebookresearch/seamless_communication 2、按照步骤执…...
C++从入门到实战(十二)详细讲解C++如何实现内存管理
C从入门到实战(十二)详细讲解C如何实现内存管理 前言一、C内存管理方式1. new/delete操作内置类型2. 异常与内存管理的联系(简单了解)3. new和delete操作自定义类型 二、 operator new与operator delete函数(重点&…...
console-chat-gpt开源程序是用于 AI Chat API 的 Python CLI
一、软件介绍 文末提供程序和源码下载 console-chat-gpt开源程序是用于 AI Chat API 的 Python CLI,与 AI 模型聊天的终极 CLI 伴侣,直接从命令行享受与 OpenAI、MistralAI、Anthropic、xAI、Google AI、DeepSeek、阿里巴巴、Inception 或 Ollama 托管…...
影刀RPA开发-程序备注说明的必要性
1. 备注指令的调用 1.1 指令搜索 搜索出备注指令后,添加到代码框中 1.2 快捷输入 在代码框中,输入指令关键字,可以快速展示出相关指令 2.备注指令内容设置 备注信息要依据代码执行的功能书写 尽量写明该语句或该段落代码的功能作用 单行…...
第十节:图像处理基础-图像算术运算 (加法、减法、混合)
引言 在计算机视觉领域,图像算术运算是最基础却至关重要的核心技术。无论是实现简单的图片合成、开发智能监控系统,还是构建复杂的医学影像分析工具,加减运算和混合操作都扮演着关键角色。OpenCV作为最流行的计算机视觉库,提供了…...
如何使用UGUI的EventTrigger
前言 在 Unity 的 UGUI 系统中,EventTrigger 是一个强大的组件,允许开发者监听和处理多种 UI 交互事件。以下是详细的使用方法、示例代码、优缺点分析以及注意事项。 一、EventTrigger 基本用法 1. 添加 EventTrigger 组件 在 Unity 编辑器中选中 UI 对象(如 But…...
5G赋能农业物联网:智能化种植的新纪元
5G赋能农业物联网:智能化种植的新纪元 在农业领域,精准化、智能化已成为现代农业发展的方向。而5G的出现,让农业物联网(Agri-IoT)突破了传统的瓶颈,真正实现了实时监测、高效数据传输、智能化决策…...
LeetCode 热题 100 64. 最小路径和
LeetCode 热题 100 | 64. 最小路径和 大家好,今天我们来解决一道经典的动态规划问题——最小路径和。这道题在 LeetCode 上被标记为中等难度,要求找到从网格的左上角到右下角的路径,使得路径上的数字总和为最小。 问题描述 给定一个包含非负…...
精益数据分析(45/126):媒体网站商业模式的深度剖析与挑战应对
精益数据分析(45/126):媒体网站商业模式的深度剖析与挑战应对 在创业和数据分析的领域中,探索不同商业模式的运作机制和关键要点是提升业务能力的重要途径。今天,我们依旧带着共同进步的目标,深入研读《精…...
学习Linux的第四天
今天我们来学习Linux的网络配置,以及链表的知识开个小头 三种网络配置模式 桥接模式(用的最多) 2.Nat模式 3. 仅主机模式(Nat模式的功能外,只能在局域网通信,不能访问外网) 桥接模式…...
AGV导航控制器技术方案——基于EFISH-SBC-RK3576/SAIL-RK3576的国产化革新(新一代工业级自主可控解决方案)
一、方案核心架构 1. 硬件拓扑设计 主控单元:SAIL-RK3576核心板(八核A72A53M0异构架构)传感器层: 双激光雷达(RS-LiDAR-16线 SICK TIM240)9轴IMU(BMI088) 轮式编码器&…...
ISCC 2025练武题 WP部分
总结 垃圾比赛,垃圾题目,纯脑洞题,技术好不好没得关系,就看你脑洞大不大。 web里塞misc,re里塞misc真是牛逼他妈给牛逼开门牛逼到家。 逆天平台,卡的一批,靶机还是公用的,把flag删…...