go:loghelper
/* # 版权所有 2026 ©涂聚文有限公司™ ® # 许可信息查看言語成了邀功盡責的功臣還需要行爲每日來值班嗎 # 描述 # Author : geovindu,Geovin Du 涂聚文. # IDE : goLang 2024.3.6 go 26.2 # os : windows 10 # database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j # Datetime : 2026/7/31 21:01 # User : geovindu # Product : GoLand # Project : goalgorithms # File : loghelper.go */ package loghelper import ( archive/zip context encoding/json fmt io math/rand os path/filepath regexp runtime sort strings sync sync/atomic time ) // LogLevel 日志级别 大写导出 type LogLevel int const ( LogLevelOff LogLevel iota LogLevelError LogLevelInfo LogLevelDebug ) // 告警常量 const ( AlertEventDiskOverLimit DISK_OVER_LIMIT AlertEventQueueBlocked QUEUE_BLOCKED ) // DesensitizeFunc 日志脱敏回调定义 type DesensitizeFunc func(level string, module string, msg string) string // AlertCallbackFunc 系统告警回调定义 type AlertCallbackFunc func(eventType string, message string, ext map[string]any) // LogItem 日志队列单元 type LogItem struct { level string module string textData string jsonData map[string]any stackTrace string } // LogHelper 日志核心结构体 type LogHelper struct { logRoot string defaultModule string keepDays int maxFileSizeMB int64 queueMaxSize int queueAlertThreshold int batchWriteCount int enableConsole bool enableJSONFormat bool compressBackupLog bool maxDiskTotalMB float64 levelDiskLimit map[string]float64 globalSamplingRate float64 levelSamplingRate map[string]float64 diskCheckIntervalSec int timeFormatTemplate string minLogLevel LogLevel desensitizeCallback DesensitizeFunc // 脱敏回调 alertCallback AlertCallbackFunc // 告警回调 logQueue chan *LogItem stopCtx context.Context stopCancel context.CancelFunc wg sync.WaitGroup fileLock sync.Mutex lastCleanDay time.Time lastDiskCheckTs int64 pid int colorMap map[string]string } var ( singleton *LogHelper once sync.Once // 随机数全局 rngOnce sync.Once rng *rand.Rand ) // 随机数初始化 func initRng() { seed : time.Now().UnixNano() src : rand.NewSource(seed) rng rand.New(src) } func randFloat() float64 { rngOnce.Do(initRng) return rng.Float64() } // getGoroutineName 获取当前协程名称 func getGoroutineName() string { buf : make([]byte, 64) n : runtime.Stack(buf, false) stackStr : string(buf[:n]) lines : strings.Split(stackStr, \n) if len(lines) 0 { firstLine : lines[0] idx : strings.Index(firstLine, [) if idx 0 { return strings.TrimSpace(firstLine[:idx]) } return strings.TrimSpace(firstLine) } return main } // NewLogHelper 获取日志单例 func NewLogHelper() *LogHelper { once.Do(func() { ctx, cancel : context.WithCancel(context.Background()) singleton LogHelper{ logRoot: filepath.Join(getWorkDir(), Logs), defaultModule: default, keepDays: 7, maxFileSizeMB: 10, queueMaxSize: 10000, queueAlertThreshold: 7000, batchWriteCount: 10, enableConsole: true, enableJSONFormat: false, compressBackupLog: true, maxDiskTotalMB: 500, levelDiskLimit: map[string]float64{ debug: 150, info: 250, error: 100, }, globalSamplingRate: 1.0, levelSamplingRate: map[string]float64{ debug: 1.0, info: 1.0, error: 1.0, }, diskCheckIntervalSec: 60, timeFormatTemplate: 2006-01-02 15:04:05, minLogLevel: LogLevelDebug, logQueue: make(chan *LogItem, 10000), stopCtx: ctx, stopCancel: cancel, pid: os.Getpid(), } singleton.colorMap map[string]string{ DEBUG: \033[34m, INFO: \033[32m, ERROR: \033[31m, RESET: \033[0m, } // Windows关闭ANSI颜色防止控制台空白 if runtime.GOOS windows { singleton.colorMap[DEBUG] singleton.colorMap[INFO] singleton.colorMap[ERROR] singleton.colorMap[RESET] } _ os.MkdirAll(singleton.logRoot, 0755) singleton.startWriterLoop() }) return singleton } // Logger 全局导出单例外部调用入口 var Logger NewLogHelper() func getWorkDir() string { wd, err : os.Getwd() if err ! nil { return . } return wd } var safeModuleReg regexp.MustCompile([^\w\-]) // SafeModuleName 模块名清洗 func (lh *LogHelper) SafeModuleName(module string) string { module strings.TrimSpace(module) if module { return lh.defaultModule } return safeModuleReg.ReplaceAllString(module, _) } // GetModuleLogDir 获取模块日志目录 func (lh *LogHelper) GetModuleLogDir(module string) string { mod : lh.SafeModuleName(module) return filepath.Join(lh.logRoot, mod) } // 两个关键注册方法完整保留 // RegisterDesensitize 注册日志脱敏回调 func (lh *LogHelper) RegisterDesensitize(fn DesensitizeFunc) { lh.desensitizeCallback fn } // RegisterAlertCallback 注册告警回调 func (lh *LogHelper) RegisterAlertCallback(fn AlertCallbackFunc) { lh.alertCallback fn } // // TriggerAlert 内部触发告警 func (lh *LogHelper) TriggerAlert(eventType string, msg string, ext map[string]any) { if lh.alertCallback nil { return } defer func() { recover() }() lh.alertCallback(eventType, msg, ext) } // SetLogLevel 设置日志级别 func (lh *LogHelper) SetLogLevel(level LogLevel) { lh.minLogLevel level } // SwitchJSONFormat 开启/关闭JSON格式化 func (lh *LogHelper) SwitchJSONFormat(enable bool) { lh.enableJSONFormat enable } // SwitchConsole 开启/关闭控制台打印 func (lh *LogHelper) SwitchConsole(enable bool) { lh.enableConsole enable } // SetSamplingRate 设置级别采样率 func (lh *LogHelper) SetSamplingRate(level string, rate float64) { if rate 0 { rate 0 } if rate 1 { rate 1 } lh.levelSamplingRate[strings.ToLower(level)] rate } // NeedSample 判断是否需要打印本条日志 func (lh *LogHelper) NeedSample(level string) bool { lv : strings.ToLower(level) lvRate : lh.levelSamplingRate[lv] finalRate : lh.globalSamplingRate * lvRate if finalRate 1.0 { return true } return randFloat() finalRate } // getDirTotalSizeMB 统计文件夹大小 func (lh *LogHelper) getDirTotalSizeMB(folder string, filterPrefix string) float64 { var total int64 _ filepath.Walk(folder, func(path string, info os.FileInfo, err error) error { if err ! nil || info.IsDir() { return nil } base : filepath.Base(path) if !strings.HasSuffix(base, .log) !strings.HasSuffix(base, .zip) { return nil } if filterPrefix ! !strings.HasPrefix(base, filterPrefix_) { return nil } total info.Size() return nil }) return float64(total) / 1024 / 1024 } // getSortedLogFiles 获取按修改时间升序日志文件 func (lh *LogHelper) getSortedLogFiles(folder string, filterPrefix string) []string { var list []struct { mtime int64 path string } _ filepath.Walk(folder, func(path string, info os.FileInfo, err error) error { if err ! nil || info.IsDir() { return nil } base : filepath.Base(path) if !strings.HasSuffix(base, .log) !strings.HasSuffix(base, .zip) { return nil } if filterPrefix ! !strings.HasPrefix(base, filterPrefix_) { return nil } list append(list, struct { mtime int64 path string }{info.ModTime().Unix(), path}) return nil }) sort.Slice(list, func(i, j int) bool { return list[i].mtime list[j].mtime }) res : make([]string, 0, len(list)) for _, v : range list { res append(res, v.path) } return res } // tryFreeDiskSpace 磁盘清理 func (lh *LogHelper) tryFreeDiskSpace(level string) bool { for { var usage float64 var limit float64 if level ! { usage lh.getDirTotalSizeMB(lh.logRoot, level) limit lh.levelDiskLimit[level] if usage limit { return true } } else { usage lh.getDirTotalSizeMB(lh.logRoot, ) limit lh.maxDiskTotalMB if usage limit { return true } } files : lh.getSortedLogFiles(lh.logRoot, level) if len(files) 0 { return false } deleted : false for _, fp : range files { if strings.HasSuffix(fp, .zip) { if err : os.Remove(fp); err nil { fmt.Fprintf(os.Stderr, [LogHelper CLEAN] 删除老旧压缩包%s\n, filepath.Base(fp)) deleted true break } } } if deleted { continue } for _, fp : range files { if strings.HasSuffix(fp, .log) { if err : os.Remove(fp); err nil { fmt.Fprintf(os.Stderr, [LogHelper CLEAN] 删除老旧日志%s\n, filepath.Base(fp)) deleted true break } } } if !deleted { return false } } } // CheckDiskLimit 磁盘超限检测 func (lh *LogHelper) CheckDiskLimit(level string) bool { nowTs : time.Now().Unix() last : atomic.LoadInt64(lh.lastDiskCheckTs) if nowTs-last int64(lh.diskCheckIntervalSec) { return false } atomic.StoreInt64(lh.lastDiskCheckTs, nowTs) lv : strings.ToLower(level) lvUsage : lh.getDirTotalSizeMB(lh.logRoot, lv) lvLimit : lh.levelDiskLimit[lv] totalUsage : lh.getDirTotalSizeMB(lh.logRoot, ) over : false if lvUsage lvLimit { lh.TriggerAlert(AlertEventDiskOverLimit, fmt.Sprintf(日志【%s】分级磁盘占用超限(%.2fMB/%.2fMB)开始清理, level, lvUsage, lvLimit), map[string]any{ level: level, usage_mb: lvUsage, limit_mb: lvLimit, total_usage: totalUsage, }) if !lh.tryFreeDiskSpace(lv) { over true } } else if totalUsage lh.maxDiskTotalMB { lh.TriggerAlert(AlertEventDiskOverLimit, fmt.Sprintf(日志总磁盘占用超限(%.2fMB/%.2fMB)开始清理, totalUsage, lh.maxDiskTotalMB), map[string]any{ usage_mb: totalUsage, limit_mb: lh.maxDiskTotalMB, }) if !lh.tryFreeDiskSpace() { over true } } if over { fmt.Fprintf(os.Stderr, [LogHelper FATAL] 磁盘清理后依旧超限暂停持久化【%s】日志\n, level) } return over } // cleanOldLogs 清理过期日志 func (lh *LogHelper) cleanOldLogs() { expire : time.Now().AddDate(0, 0, -lh.keepDays) _ filepath.Walk(lh.logRoot, func(path string, info os.FileInfo, err error) error { if err ! nil || info.IsDir() { return nil } base : filepath.Base(path) if !strings.HasSuffix(base, .log) !strings.HasSuffix(base, .zip) { return nil } if info.ModTime().Before(expire) { _ os.Remove(path) } return nil }) } // getLogPath 获取日志文件路径 func (lh *LogHelper) getLogPath(level, module string) string { modDir : lh.GetModuleLogDir(module) _ os.MkdirAll(modDir, 0755) dateStr : time.Now().Format(20060102) return filepath.Join(modDir, strings.ToLower(level)_dateStr.log) } // splitLogFile 日志切割 func (lh *LogHelper) splitLogFile(logPath string) { lh.fileLock.Lock() defer lh.fileLock.Unlock() stat, err : os.Stat(logPath) if err ! nil { return } if stat.Size() lh.maxFileSizeMB*1024*1024 { return } base : strings.TrimSuffix(logPath, .log) bakPath : fmt.Sprintf(%s_%s.log, base, time.Now().Format(150405)) _ os.Rename(logPath, bakPath) if lh.compressBackupLog { go lh.compressFile(bakPath) } } // compressFile 压缩备份日志 func (lh *LogHelper) compressFile(src string) { if _, err : os.Stat(src); err ! nil { return } zipPath : strings.TrimSuffix(src, .log) .zip fw, err : os.Create(zipPath) if err ! nil { return } defer fw.Close() zw : zip.NewWriter(fw) defer zw.Close() fIn, err : os.Open(src) if err ! nil { return } defer fIn.Close() w, err : zw.Create(filepath.Base(src)) if err ! nil { return } _, _ io.Copy(w, fIn) _ os.Remove(src) } // getCallerInfo 获取调用位置 func getCallerInfo(skip int) (fileName, funcName string, line int) { pc, file, lineNo, ok : runtime.Caller(skip) if !ok { return unknown, unknown, 0 } fileName filepath.Base(file) fn : runtime.FuncForPC(pc) if fn ! nil { funcName fn.Name() } return fileName, funcName, lineNo } // GetStackTrace 安全获取异常堆栈 func GetStackTrace(err error, skip int) string { if err nil { return } buf : make([]byte, 4096) n : runtime.Stack(buf, false) return fmt.Sprintf(错误信息%v\n调用堆栈\n%s, err, string(buf[:n])) } // buildLogData 组装日志文本/JSON func (lh *LogHelper) buildLogData(level, module, msg, stack string) (string, map[string]any) { modSafe : lh.SafeModuleName(module) // 脱敏回调自动执行 if lh.desensitizeCallback ! nil { defer func() { recover() }() msg lh.desensitizeCallback(level, modSafe, msg) } now : time.Now() timeStr : now.Format(lh.timeFormatTemplate) timestamp : float64(now.UnixNano()) / 1e9 goroutineName : getGoroutineName() fileName, funcName, lineNo : getCallerInfo(4) if lh.enableJSONFormat { jsonMap : map[string]any{ time: timeStr, timestamp: timestamp, level: level, module: modSafe, pid: lh.pid, thread: goroutineName, file: fileName, function: funcName, line: lineNo, message: msg, stack_trace: stack, } return , jsonMap } text : fmt.Sprintf([%s] [%s] [MODULE:%s] [PID:%d] [G:%s] [%s·%d行·%s] %s, timeStr, level, modSafe, lh.pid, goroutineName, funcName, lineNo, fileName, msg) if stack ! { text \n stack } text \n return text, nil } // EnqueueLog 日志入队 func (lh *LogHelper) EnqueueLog(level, module, msg, stack string) { if !lh.NeedSample(level) { return } textData, jsonData : lh.buildLogData(level, module, msg, stack) // 控制台输出独立执行不受队列阻塞 if lh.enableConsole { var consoleStr string if jsonData ! nil { buf, err : json.MarshalIndent(jsonData, , ) if err ! nil { consoleStr strings.TrimSuffix(textData, \n) } else { consoleStr string(buf) } } else { consoleStr strings.TrimSuffix(textData, \n) } color : lh.colorMap[level] reset : lh.colorMap[RESET] fmt.Printf(%s%s%s\n, color, consoleStr, reset) } item : LogItem{ level: strings.ToLower(level), module: module, textData: textData, jsonData: jsonData, stackTrace: stack, } // 非阻塞入队队列满丢弃 select { case lh.logQueue - item: default: fmt.Fprintln(os.Stderr, [LogHelper WARN] 队列已满丢弃持久化日志) } } // startWriterLoop 后台持久化协程 func (lh *LogHelper) startWriterLoop() { lh.wg.Add(1) go func() { defer lh.wg.Done() buffer : make(map[string][]*LogItem) ticker : time.NewTicker(800 * time.Millisecond) defer ticker.Stop() for { select { case -lh.stopCtx.Done(): lh.FlushAllBuffer(buffer) return case item : -lh.logQueue: key : fmt.Sprintf(%s|%s, item.level, item.module) buffer[key] append(buffer[key], item) if len(buffer[key]) lh.batchWriteCount { lh.FlushBuffer(buffer, key) } case -ticker.C: if len(buffer) 0 { lh.FlushAllBuffer(buffer) } } } }() } // FlushBuffer 刷新单组缓存写入文件 func (lh *LogHelper) FlushBuffer(buffer map[string][]*LogItem, key string) { items, ok : buffer[key] if !ok || len(items) 0 { return } parts : strings.Split(key, |) level, module : parts[0], parts[1] if lh.CheckDiskLimit(level) { delete(buffer, key) return } logPath : lh.getLogPath(level, module) lh.splitLogFile(logPath) var writeBuilder strings.Builder for _, it : range items { if it.jsonData ! nil { buf, err : json.Marshal(it.jsonData) if err nil { writeBuilder.Write(buf) writeBuilder.WriteByte(\n) } } else { writeBuilder.WriteString(it.textData) } } f, err : os.OpenFile(logPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) if err ! nil { fmt.Fprintf(os.Stderr, [LogHelper] 写入失败 level:%s module:%s err:%v\n, level, module, err) delete(buffer, key) return } _, _ f.WriteString(writeBuilder.String()) _ f.Close() delete(buffer, key) nowDay : time.Now().Truncate(24 * time.Hour) if !nowDay.Equal(lh.lastCleanDay) { lh.cleanOldLogs() lh.lastCleanDay nowDay } } // FlushAllBuffer 刷新全部缓存 func (lh *LogHelper) FlushAllBuffer(buffer map[string][]*LogItem) { for k : range buffer { lh.FlushBuffer(buffer, k) } } // Debug 普通调试日志 func (lh *LogHelper) Debug(msg string) { if lh.minLogLevel LogLevelDebug { return } lh.EnqueueLog(DEBUG, lh.defaultModule, msg, ) } // Info 普通信息日志 func (lh *LogHelper) Info(msg string) { if lh.minLogLevel LogLevelInfo { return } lh.EnqueueLog(INFO, lh.defaultModule, msg, ) } // Error 普通错误日志 func (lh *LogHelper) Error(msg string, err error) { if lh.minLogLevel LogLevelError { return } stack : if err ! nil { stack GetStackTrace(err, 4) } lh.EnqueueLog(ERROR, lh.defaultModule, msg, stack) } // DebugMod 模块调试日志 func (lh *LogHelper) DebugMod(module string, msg string) { if lh.minLogLevel LogLevelDebug { return } lh.EnqueueLog(DEBUG, module, msg, ) } // InfoMod 模块信息日志 func (lh *LogHelper) InfoMod(module string, msg string) { if lh.minLogLevel LogLevelInfo { return } lh.EnqueueLog(INFO, module, msg, ) } // ErrorMod 模块错误日志 func (lh *LogHelper) ErrorMod(module string, msg string, err error) { if lh.minLogLevel LogLevelError { return } stack : if err ! nil { stack GetStackTrace(err, 4) } lh.EnqueueLog(ERROR, module, msg, stack) } // Shutdown 优雅关闭日志 func (lh *LogHelper) Shutdown() { lh.stopCancel() lh.wg.Wait() } // 全局快捷导出函数 func Debug(msg string) { Logger.Debug(msg) } func Info(msg string) { Logger.Info(msg) } func Error(msg string, err error) { Logger.Error(msg, err) } func DebugMod(module string, msg string) { Logger.DebugMod(module, msg) } func InfoMod(module string, msg string) { Logger.InfoMod(module, msg) } func ErrorMod(module string, msg string, err error) { Logger.ErrorMod(module, msg, err) } func Shutdown() { Logger.Shutdown() }调用// 1. 注册脱敏回调 mobileReg : regexp.MustCompile(1[3-9]\d{9}) loghelper.Logger.RegisterDesensitize(func(level, module, msg string) string { return mobileReg.ReplaceAllStringFunc(msg, func(s string) string { return s[:3] **** s[7:] }) }) // 2. 注册告警回调 loghelper.Logger.RegisterAlertCallback(func(eventType, msg string, ext map[string]any) { fmt.Printf(\n【ALERT】%s | %s | %v\n, eventType, msg, ext) }) // 3. 设置采样率debug 20%采样 loghelper.Logger.SetSamplingRate(debug, 0.2) // 旧接口默认模块 default loghelper.Info(默认模块启动手机号13800138000) // 新接口按业务模块隔离日志 loghelper.InfoMod(user_service, 用户注册手机号13900139000) loghelper.DebugMod(order_service, 订单创建请求) errTest : fmt.Errorf(div zero) loghelper.ErrorMod(pay_service, 支付异常, errTest) // 开启JSON格式 loghelper.Logger.SwitchJSONFormat(true) //SwitchJSONFormat(true) loghelper.InfoMod(goods_service, 商品库存查询) fmt.Println(按回车退出...) var in string fmt.Scanln(in) loghelper.Shutdown()Go语言实现的日志工具包loghelper主要功能包括多级日志记录DEBUG/INFO/ERROR模块化日志隔离日志脱敏处理支持手机号等敏感信息掩码磁盘空间监控与自动清理日志采样率控制支持JSON格式输出异步队列写入告警回调机制核心特性线程安全的单例模式支持按日期、大小自动分割日志文件可配置日志保留天数支持压缩归档旧日志包含调用堆栈信息协程安全的写入队列使用示例展示了如何注册脱敏回调、告警回调以及通过不同模块记录日志。该工具适用于需要结构化日志、敏感信息保护和资源监控的中大型项目。

相关新闻

STM32与W5500以太网通信:从硬件设计到Modbus TCP实战

STM32与W5500以太网通信:从硬件设计到Modbus TCP实战

1. 项目概述:当STM32遇见W5500如果你正在做一个需要联网的嵌入式设备,比如智能家居的控制器、工业现场的数据采集器,或者一个远程监控终端,那么“STM32 W5500”这个组合大概率会出现在你的备选方案里。这不是什么新鲜玩意儿&…

2026/8/1 1:47:27 阅读更多 →
第一篇:为什么需要消息队列?

第一篇:为什么需要消息队列?

为什么需要消息队列? 上一篇我们聊完了 Redis 系列的最后一篇《Redis 为什么不能当数据库?》。 Redis 解决的是一个问题:如何让系统访问数据更快。但是,当系统继续发展,流量继续增加,只靠 Redis 还不够。因…

2026/8/1 1:47:27 阅读更多 →
【转载】亚马逊蝮蛇毒液多样性研究:生态和毒理学意义 | MDPI Toxins

【转载】亚马逊蝮蛇毒液多样性研究:生态和毒理学意义 | MDPI Toxins

蛇毒作为蝮蛇等毒蛇的关键生化武器,其组成与变异常被视为影响物种生态适应与生存成功的重要因素。然而,关于毒液成分是否直接决定蛇类在不同生态环境中的分布与生存策略,目前仍缺乏明确结论。来自巴西布坦坦研究所等多家科研机构的的Ana M. M…

2026/8/1 1:47:27 阅读更多 →

最新新闻

告别“中看不中用”的立体模型:三维重构迈向“可操作、可仿真”的新纪元研发课题方案

告别“中看不中用”的立体模型:三维重构迈向“可操作、可仿真”的新纪元研发课题方案

告别“中看不中用”的立体模型:三维重构迈向“可操作、可仿真”的新纪元研发课题方案研发单位:镜像视界(浙江)科技有限公司核心研发负责人:耿文海(创始人、首席技术官)学术支撑单位:…

2026/8/1 2:30:43 阅读更多 →
Pandoc架构揭秘:统一文档转换引擎的深度解析

Pandoc架构揭秘:统一文档转换引擎的深度解析

Pandoc架构揭秘:统一文档转换引擎的深度解析 【免费下载链接】pandoc Universal markup converter 项目地址: https://gitcode.com/gh_mirrors/pa/pandoc 在技术演进的长河中,文档格式的多样性一直是信息交换的痛点。当传统方案遇到瓶颈时&#x…

2026/8/1 2:30:43 阅读更多 →
MES与ERP集成:工单/物料/成本的数据打通

MES与ERP集成:工单/物料/成本的数据打通

一、背景故事:一次月结对账引发的集成项目故事发生在一家年产能约二十亿颗芯片的封测厂。这家工厂的ERP用的是SAP ECC,MES是某国产商用系统,两套系统各自运行了五年,中间的数据交换方式非常原始:计划员每天早上从ERP导…

2026/8/1 2:30:43 阅读更多 →
嵌入式开发避坑指南:低成本“CCS小金人”板卡硬件排查与软件适配实战

嵌入式开发避坑指南:低成本“CCS小金人”板卡硬件排查与软件适配实战

最近在嵌入式开发圈里,不少朋友都在讨论一个让人哭笑不得的现象:从某知名电商平台购买的“CCS小金人”开发板,到手后问题频发。所谓“CCS小金人”,通常指的是那些价格低廉、仿制德州仪器(TI)官方Code Compo…

2026/8/1 2:30:43 阅读更多 →
x86描述符详解:从内存管理到特权级切换的核心机制

x86描述符详解:从内存管理到特权级切换的核心机制

1. 项目概述:从“段”到“门”的x86内存与特权级基石如果你写过x86汇编,或者稍微深入一点研究过操作系统内核,那么“描述符”这个词对你来说一定不陌生。它就像隐藏在繁华都市地下的庞大管线系统,应用程序在“地面”上自由奔跑&am…

2026/8/1 2:30:43 阅读更多 →
5步掌握百度文库免费下载:终极免费文档获取指南

5步掌握百度文库免费下载:终极免费文档获取指南

5步掌握百度文库免费下载:终极免费文档获取指南 【免费下载链接】baidu-wenku fetch the document for free 项目地址: https://gitcode.com/gh_mirrors/ba/baidu-wenku 还在为百度文库的下载限制而烦恼吗?想要轻松保存那些宝贵的学习资料和工作文…

2026/8/1 2:29:43 阅读更多 →

日新闻

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南 【免费下载链接】BaiduNetdiskPlugin-macOS For macOS.百度网盘 破解SVIP、下载速度限制~ 项目地址: https://gitcode.com/gh_mirrors/ba/BaiduNetdiskPlugin-macOS 还在为百度网盘macOS版的龟速下…

2026/8/1 0:00:48 阅读更多 →
终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 还在为网易云音乐下载的NCM格式文件无法在其他播放器播放而烦恼吗?ncmdump解密工具帮你轻松解决这个困…

2026/8/1 0:00:48 阅读更多 →
HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

AgentCard 智能体卡片:为英语学习 App 打造桌面级学习助手适用平台:HarmonyOS 7.0 (API 26 Beta)一、引言 HarmonyOS 7.0(API 26 Beta)新增了 AgentCard 智能体卡片能力,这是继 HMAF(鸿蒙智能体框架&#x…

2026/8/1 0:00:48 阅读更多 →

周新闻

深度学习道路桥梁裂缝检测系统 道路桥梁裂缝检测数据集 道路桥梁病害识别检测数据集

深度学习道路桥梁裂缝检测系统 道路桥梁裂缝检测数据集 道路桥梁病害识别检测数据集

深度学习道路桥梁裂缝检测系统 数据集6000张 完整源码已标注数据集训练好的模型环境配置教程程序运行说明文档,可以直接使用!系统支持图片、视频、摄像头等多种方式检测裂缝,功能强大实用。 1数据集6000张 8各类别

2026/7/31 1:03:03 阅读更多 →
深度学习YOLO模型如何训练 PUBG 绝地求生目标检测数据集

深度学习YOLO模型如何训练 PUBG 绝地求生目标检测数据集

pubg数据集 精选原图1.42万数据 1.49万标签 无任何重复、算法增强或冗余图像! pubg绝地求生目标检测数据集 1分类:e_body,14905个标签,txt格式 共计14244张图,99%为640*640尺寸图像 适合yolo目标检测、AI训练关键词&am…

2026/7/29 14:34:28 阅读更多 →
Apex英雄目标检测数据集 深度学习框架YOLO如何训练APEX数据集

Apex英雄目标检测数据集 深度学习框架YOLO如何训练APEX数据集

Apex检测数据集数据集详情检测类别: allies enemy tag图片总量:7247张训练集:5139张验证集:1425张测试集:683张标注状态:全部已标注,即拿即用数据格式:支持YOLO格式及其他格式&#…

2026/7/31 4:19:39 阅读更多 →

月新闻

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南 【免费下载链接】BaiduNetdiskPlugin-macOS For macOS.百度网盘 破解SVIP、下载速度限制~ 项目地址: https://gitcode.com/gh_mirrors/ba/BaiduNetdiskPlugin-macOS 还在为百度网盘macOS版的龟速下…

2026/8/1 0:00:48 阅读更多 →
终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 还在为网易云音乐下载的NCM格式文件无法在其他播放器播放而烦恼吗?ncmdump解密工具帮你轻松解决这个困…

2026/8/1 0:00:48 阅读更多 →
HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

AgentCard 智能体卡片:为英语学习 App 打造桌面级学习助手适用平台:HarmonyOS 7.0 (API 26 Beta)一、引言 HarmonyOS 7.0(API 26 Beta)新增了 AgentCard 智能体卡片能力,这是继 HMAF(鸿蒙智能体框架&#x…

2026/8/1 0:00:48 阅读更多 →