package main import "C" import ( "encoding/json" "fmt" "log" "os" "path/filepath" "runtime" "syscall" "time" "unsafe" "golang.org/x/sys/windows" ) // LoggerWrapper 封装DLL调用 type LoggerWrapper struct { dllHandle windows.Handle createLogger uintptr createContext uintptr logInfo uintptr logError uintptr logWarning uintptr logSuccess uintptr freeString uintptr closeAllLoggers uintptr // cui getLogs uintptr getLogFiles uintptr } // 全局变量来跟踪创建的logger数量 var ( createdLoggers []string loggerCount int ) func NewLoggerWrapper(dllPath string) (*LoggerWrapper, error) { if _, err := os.Stat(dllPath); os.IsNotExist(err) { return nil, fmt.Errorf("DLL文件不存在: %s", dllPath) } dllHandle, err := windows.LoadLibrary(dllPath) if err != nil { return nil, fmt.Errorf("加载DLL失败: %v", err) } wrapper := &LoggerWrapper{ dllHandle: dllHandle, } if err := wrapper.loadFunctions(); err != nil { windows.FreeLibrary(dllHandle) return nil, err } return wrapper, nil } func (lw *LoggerWrapper) loadFunctions() error { var err error lw.createLogger, err = windows.GetProcAddress(lw.dllHandle, "CreateLogger") if err != nil { return err } lw.createContext, err = windows.GetProcAddress(lw.dllHandle, "CreateContextWithTaskType") if err != nil { return err } lw.logInfo, err = windows.GetProcAddress(lw.dllHandle, "LogInfo") if err != nil { return err } lw.logError, err = windows.GetProcAddress(lw.dllHandle, "LogError") if err != nil { return err } lw.logWarning, err = windows.GetProcAddress(lw.dllHandle, "LogWarning") if err != nil { return err } lw.logSuccess, err = windows.GetProcAddress(lw.dllHandle, "LogSuccess") if err != nil { return err } lw.freeString, err = windows.GetProcAddress(lw.dllHandle, "FreeString") if err != nil { return err } lw.closeAllLoggers, err = windows.GetProcAddress(lw.dllHandle, "CloseAllLoggers") if err != nil { return err } // cui lw.getLogs, err = windows.GetProcAddress(lw.dllHandle, "GetLogs") if err != nil { return err } // cui lw.getLogFiles, err = windows.GetProcAddress(lw.dllHandle, "GetLogFiles") if err != nil { return err } return nil } func (lw *LoggerWrapper) CreateLogger(configJSON string) (string, error) { configCStr, err := syscall.BytePtrFromString(configJSON) if err != nil { return "", err } resultPtr, _, err := syscall.SyscallN(lw.createLogger, uintptr(unsafe.Pointer(configCStr))) if err != syscall.Errno(0) { return "", fmt.Errorf("CreateLogger调用失败: %v", err) } if resultPtr == 0 { return "", fmt.Errorf("CreateLogger返回空指针") } result := readNullTerminatedString(resultPtr) lw.FreeStringByPtr(resultPtr) // 记录创建的logger createdLoggers = append(createdLoggers, result) loggerCount++ return result, nil } func (lw *LoggerWrapper) CreateContextWithTaskType(loggerHandle, taskType string) (string, error) { loggerHandleCStr, err := syscall.BytePtrFromString(loggerHandle) if err != nil { return "", err } taskTypeCStr, err := syscall.BytePtrFromString(taskType) if err != nil { return "", err } resultPtr, _, err := syscall.SyscallN( lw.createContext, uintptr(unsafe.Pointer(loggerHandleCStr)), uintptr(unsafe.Pointer(taskTypeCStr)), ) if err != syscall.Errno(0) { return "", fmt.Errorf("CreateContextWithTaskType调用失败: %v", err) } if resultPtr == 0 { return "", fmt.Errorf("CreateContextWithTaskType返回空指针") } result := readNullTerminatedString(resultPtr) lw.FreeStringByPtr(resultPtr) return result, nil } func (lw *LoggerWrapper) LogInfo(ctxHandle, message string) error { return lw.logMessage(ctxHandle, message, lw.logInfo) } func (lw *LoggerWrapper) LogError(ctxHandle, message string) error { return lw.logMessage(ctxHandle, message, lw.logError) } func (lw *LoggerWrapper) LogWarning(ctxHandle, message string) error { return lw.logMessage(ctxHandle, message, lw.logWarning) } func (lw *LoggerWrapper) LogSuccess(ctxHandle, message string) error { return lw.logMessage(ctxHandle, message, lw.logSuccess) } func (lw *LoggerWrapper) logMessage(ctxHandle, message string, logFunc uintptr) error { ctxHandleCStr, err := syscall.BytePtrFromString(ctxHandle) if err != nil { return err } messageCStr, err := syscall.BytePtrFromString(message) if err != nil { return err } _, _, err = syscall.SyscallN( logFunc, uintptr(unsafe.Pointer(ctxHandleCStr)), uintptr(unsafe.Pointer(messageCStr)), ) if err != syscall.Errno(0) { return fmt.Errorf("日志记录失败: %v", err) } return nil } // cui func (lw *LoggerWrapper) GetLogs(loggerHandle, configJSON string) (string, error) { loggerHandleCStr, err := syscall.BytePtrFromString(loggerHandle) if err != nil { return "", err } configCStr, err := syscall.BytePtrFromString(configJSON) if err != nil { return "", err } resultPtr, _, err := syscall.SyscallN( lw.getLogs, uintptr(unsafe.Pointer(loggerHandleCStr)), uintptr(unsafe.Pointer(configCStr)), ) if err != syscall.Errno(0) { return "", fmt.Errorf("GetLogs调用失败: %v", err) } if resultPtr == 0 { return "", fmt.Errorf("GetLogs返回空指针") } result := ptrToString(resultPtr) lw.FreeStringByPtr(resultPtr) return result, nil } // cui func (lw *LoggerWrapper) GetLogFiles(loggerHandle string) (string, error) { loggerHandleCStr, err := syscall.BytePtrFromString(loggerHandle) if err != nil { return "", err } resultPtr, _, err := syscall.SyscallN( lw.getLogFiles, uintptr(unsafe.Pointer(loggerHandleCStr)), ) if err != syscall.Errno(0) { return "", fmt.Errorf("GetLogFiles调用失败: %v", err) } if resultPtr == 0 { return "", fmt.Errorf("GetLogFiles返回空指针") } result := ptrToString(resultPtr) lw.FreeStringByPtr(resultPtr) return result, nil } func ptrToString(ptr uintptr) string { if ptr == 0 { return "" } // 先找到字符串长度 var length int for { b := *(*byte)(unsafe.Pointer(ptr + uintptr(length))) if b == 0 { break } length++ if length > 64*1024 { // 64KB限制 break } } if length == 0 { return "" } // 创建适当大小的切片 data := make([]byte, length) for i := 0; i < length; i++ { data[i] = *(*byte)(unsafe.Pointer(ptr + uintptr(i))) } return string(data) } func (lw *LoggerWrapper) FreeStringByPtr(ptr uintptr) { if ptr != 0 { syscall.SyscallN(lw.freeString, ptr) } } func (lw *LoggerWrapper) CloseAllLoggers() (string, error) { resultPtr, _, err := syscall.SyscallN(lw.closeAllLoggers) if err != syscall.Errno(0) { return "", fmt.Errorf("CloseAllLoggers调用失败: %v", err) } if resultPtr == 0 { return "", fmt.Errorf("CloseAllLoggers返回空指针") } result := readNullTerminatedString(resultPtr) lw.FreeStringByPtr(resultPtr) return result, nil } func readNullTerminatedString(ptr uintptr) string { if ptr == 0 { return "" } var bytes []byte for i := 0; i < 256; i++ { b := *(*byte)(unsafe.Pointer(ptr + uintptr(i))) if b == 0 { break } bytes = append(bytes, b) } return string(bytes) } func (lw *LoggerWrapper) Close() error { return windows.FreeLibrary(lw.dllHandle) } func checkError(result string) error { if len(result) >= 6 && result[:6] == "错误:" { return fmt.Errorf(result) } return nil } // 监控内存 func monitorResources() { var m runtime.MemStats runtime.ReadMemStats(&m) fmt.Printf("📊 内存使用: Alloc=%.2fMB, TotalAlloc=%.2fMB, Sys=%.2fMB, NumGC=%d, Goroutines=%d\n", float64(m.Alloc)/1024/1024, float64(m.TotalAlloc)/1024/1024, float64(m.Sys)/1024/1024, m.NumGC, runtime.NumGoroutine()) } // 测试日志读取功能 cui func testLogReadingFunctions(wrapper *LoggerWrapper) { fmt.Println("\n📖 ========== 开始日志读取功能测试 ==========") // 创建测试logger config := `{ "log_dir": "./test_logs_reading", "level": 1, "split_type": 1, "max_size": 1048576, "max_count": 5, "enable_caller": true, "default_task_type": "reading_test" }` loggerHandle, err := wrapper.CreateLogger(config) if err != nil { log.Printf("创建测试logger失败: %v", err) return } fmt.Printf("✅ 创建测试logger: %s\n", loggerHandle) // 创建不同任务类型的上下文 taskTypes := []string{"task_a", "task_b", "task_c"} ctxHandles := make(map[string]string) for _, taskType := range taskTypes { ctxHandle, err := wrapper.CreateContextWithTaskType(loggerHandle, taskType) if err != nil { log.Printf("创建上下文失败: %v", err) continue } ctxHandles[taskType] = ctxHandle fmt.Printf("✅ 创建上下文: %s -> %s\n", taskType, ctxHandle) } // 记录测试日志 fmt.Println("\n📝 记录测试日志数据...") messages := []struct { level string message string task string }{ {"INFO", "这是INFO级别的测试消息", "task_a"}, {"ERROR", "这是ERROR级别的错误消息", "task_b"}, {"WARNING", "这是WARNING级别的警告消息", "task_c"}, {"SUCCESS", "这是SUCCESS级别的成功消息", "task_a"}, {"INFO", "另一条INFO消息", "task_b"}, } for i, msg := range messages { var err error switch msg.level { case "INFO": err = wrapper.LogInfo(ctxHandles[msg.task], fmt.Sprintf("%s - 序号: %d", msg.message, i)) case "ERROR": err = wrapper.LogError(ctxHandles[msg.task], fmt.Sprintf("%s - 序号: %d", msg.message, i)) case "WARNING": err = wrapper.LogWarning(ctxHandles[msg.task], fmt.Sprintf("%s - 序号: %d", msg.message, i)) case "SUCCESS": err = wrapper.LogSuccess(ctxHandles[msg.task], fmt.Sprintf("%s - 序号: %d", msg.message, i)) } if err != nil { log.Printf("记录日志失败: %v", err) } else { fmt.Printf("✅ 记录%s日志: %s\n", msg.level, msg.message) } } // 等待一下确保日志写入完成 time.Sleep(100 * time.Millisecond) // 测试1: 获取日志文件列表 fmt.Println("\n📁 测试1: 获取日志文件列表") filesResult, err := wrapper.GetLogFiles(loggerHandle) if err != nil { log.Printf("获取日志文件列表失败: %v", err) } else { fmt.Printf("✅ 获取日志文件列表成功:\n%s\n", formatJSON(filesResult)) } // 测试2: 获取所有日志 fmt.Println("\n📄 测试2: 获取所有日志") allLogsConfig := `{ "level": -1, "max_entries": 50 }` allLogsResult, err := wrapper.GetLogs(loggerHandle, allLogsConfig) if err != nil { log.Printf("获取所有日志失败: %v", err) } else { fmt.Printf("✅ 获取所有日志成功:\n%s\n", formatJSON(allLogsResult)) } // 测试3: 按级别过滤日志 fmt.Println("\n🔍 测试3: 按级别过滤日志(INFO)") infoLogsConfig := `{ "level": 1, "max_entries": 10 }` infoLogsResult, err := wrapper.GetLogs(loggerHandle, infoLogsConfig) if err != nil { log.Printf("获取INFO日志失败: %v", err) } else { fmt.Printf("✅ 获取INFO日志成功:\n%s\n", formatJSON(infoLogsResult)) } // 测试4: 按任务类型过滤日志 fmt.Println("\n🔍 测试4: 按任务类型过滤日志(task_a)") taskALogsConfig := `{ "level": -1, "task_type": "task_a", "max_entries": 10 }` taskALogsResult, err := wrapper.GetLogs(loggerHandle, taskALogsConfig) if err != nil { log.Printf("获取task_a日志失败: %v", err) } else { fmt.Printf("✅ 获取task_a日志成功:\n%s\n", formatJSON(taskALogsResult)) } // 测试5: 限制返回条目数 fmt.Println("\n🔍 测试5: 限制返回条目数(2条)") limitedLogsConfig := `{ "level": -1, "max_entries": 2 }` limitedLogsResult, err := wrapper.GetLogs(loggerHandle, limitedLogsConfig) if err != nil { log.Printf("获取限制条目日志失败: %v", err) } else { fmt.Printf("✅ 获取限制条目日志成功:\n%s\n", formatJSON(limitedLogsResult)) } // 测试6: 组合条件查询 fmt.Println("\n🔍 测试6: 组合条件查询(ERROR级别 + task_b)") combinedConfig := `{ "level": 3, "task_type": "task_b", "max_entries": 10 }` combinedResult, err := wrapper.GetLogs(loggerHandle, combinedConfig) if err != nil { log.Printf("组合条件查询失败: %v", err) } else { fmt.Printf("✅ 组合条件查询成功:\n%s\n", formatJSON(combinedResult)) } // 测试7: 组合条件查询 fmt.Println("\n🔍 测试7: 组合条件查询(task_b)") combinedTimeConfig := `{ "level": -1, "task_type": "task_b", "start_time": "2025-11-25 15:03:31", "end_time": "2025-11-25 15:04:31", "max_entries": 10 }` combinedTimeResult, err := wrapper.GetLogs(loggerHandle, combinedTimeConfig) if err != nil { log.Printf("组合条件查询失败: %v", err) } else { fmt.Printf("✅ 组合条件查询成功:\n%s\n", formatJSON(combinedTimeResult)) } // 解析并分析结果 fmt.Println("\n📊 日志读取功能测试分析:") analyzeLogReadingResults(allLogsResult) fmt.Println("✅ 日志读取功能测试完成") } // 分析日志读取结果 cui func analyzeLogReadingResults(logsResult string) { var result map[string]interface{} if err := json.Unmarshal([]byte(logsResult), &result); err != nil { log.Printf("解析日志结果失败: %v", err) return } if errorMsg, exists := result["error"]; exists { fmt.Printf("❌ 错误: %v\n", errorMsg) return } count, _ := result["count"].(float64) entries, _ := result["entries"].([]interface{}) fmt.Printf(" 总日志条目数: %.0f\n", count) // 统计各级别日志数量 levelCount := make(map[string]int) taskCount := make(map[string]int) for _, entry := range entries { if entryMap, ok := entry.(map[string]interface{}); ok { level, _ := entryMap["level"].(string) taskType, _ := entryMap["task_type"].(string) levelCount[level]++ taskCount[taskType]++ } } fmt.Println(" 日志级别分布:") for level, count := range levelCount { fmt.Printf(" - %s: %d条\n", level, count) } fmt.Println(" 任务类型分布:") for taskType, count := range taskCount { fmt.Printf(" - %s: %d条\n", taskType, count) } } // 格式化JSON输出 cui func formatJSON(jsonStr string) string { var result map[string]interface{} if err := json.Unmarshal([]byte(jsonStr), &result); err != nil { return jsonStr // 返回原始字符串如果解析失败 } formatted, err := json.MarshalIndent(result, " ", " ") if err != nil { return jsonStr } return string(formatted) } // 测试资源回收的核心函数 func testResourceCleanup(wrapper *LoggerWrapper) { fmt.Println("🧪 开始资源回收验证测试") // 测试配置 config := `{ "log_dir": "./resource_cleanup_test", "level": 1, "split_type": 1, "max_size": 1048576, "max_count": 3, "enable_caller": false, "default_task_type": "cleanup_test" }` // 阶段1: 创建多个logger并记录初始状态 fmt.Println("\n📝 阶段1: 创建测试logger") activeLoggers := make([]string, 0) idleLoggers := make([]string, 0) // 创建6个logger,3个活跃,3个闲置 for i := 0; i < 6; i++ { loggerHandle, err := wrapper.CreateLogger(config) if err != nil { log.Printf("创建logger %d 失败: %v", i, err) continue } if i < 3 { activeLoggers = append(activeLoggers, loggerHandle) fmt.Printf("✅ 创建活跃logger %d: %s\n", i, loggerHandle) } else { idleLoggers = append(idleLoggers, loggerHandle) fmt.Printf("✅ 创建闲置logger %d: %s\n", i, loggerHandle) } } fmt.Printf("\n📊 创建统计: 活跃logger=%d, 闲置logger=%d, 总计=%d\n", len(activeLoggers), len(idleLoggers), len(activeLoggers)+len(idleLoggers)) // 阶段2: 使用活跃logger,让闲置logger保持不使用状态 fmt.Println("\n📝 阶段2: 使用活跃logger") for i, loggerHandle := range activeLoggers { ctxHandle, err := wrapper.CreateContextWithTaskType(loggerHandle, fmt.Sprintf("active_task_%d", i)) if err != nil { log.Printf("创建上下文失败: %v", err) continue } // 记录多条日志确保活跃状态 for j := 0; j < 5; j++ { message := fmt.Sprintf("活跃logger %d - 消息 %d - 时间: %s", i, j, time.Now().Format("15:04:05")) err = wrapper.LogInfo(ctxHandle, message) if err != nil { log.Printf("记录日志失败: %v", err) } } fmt.Printf("✅ 活跃logger %d 已使用: %s\n", i, loggerHandle) } // 记录当前资源状态 fmt.Println("\n📊 资源使用前状态:") monitorResources() // 阶段3: 模拟时间流逝并验证资源回收 fmt.Println("\n⏰ 阶段3: 模拟时间流逝等待资源回收") fmt.Println(" 自动清理机制: 每30分钟运行一次,清理2小时未使用的资源") fmt.Println(" 由于测试时间限制,我们将观察资源使用模式...") // 多次采样资源使用情况来观察趋势 fmt.Println("\n📈 资源使用趋势观察:") initialMemory := getMemoryUsage() for i := 0; i < 3; i++ { fmt.Printf("\n 采样 %d:\n", i+1) monitorResources() // 继续使用活跃logger以保持其活跃状态 for _, loggerHandle := range activeLoggers { ctxHandle, err := wrapper.CreateContextWithTaskType(loggerHandle, "keep_alive") if err == nil { wrapper.LogInfo(ctxHandle, fmt.Sprintf("保持活跃 - 采样 %d", i+1)) } } time.Sleep(2 * time.Second) } finalMemory := getMemoryUsage() memoryChange := finalMemory - initialMemory fmt.Printf("\n📊 内存变化: %.2fMB\n", memoryChange) // 阶段4: 验证闲置logger的状态 fmt.Println("\n🔍 阶段4: 验证闲置logger状态") stillWorkingCount := 0 for i, loggerHandle := range idleLoggers { ctxHandle, err := wrapper.CreateContextWithTaskType(loggerHandle, "test_after_idle") if err != nil { fmt.Printf("❌ 闲置logger %d 可能已被回收: %s -> %v\n", i, loggerHandle, err) } else { // 尝试记录日志 err = wrapper.LogInfo(ctxHandle, "测试闲置logger是否仍然工作") if err != nil { fmt.Printf("❌ 闲置logger %d 记录日志失败: %s -> %v\n", i, loggerHandle, err) } else { fmt.Printf("✅ 闲置logger %d 仍然工作: %s\n", i, loggerHandle) stillWorkingCount++ } } } // 阶段5: 验证活跃logger的状态 fmt.Println("\n🔍 阶段5: 验证活跃logger状态") activeWorkingCount := 0 for i, loggerHandle := range activeLoggers { ctxHandle, err := wrapper.CreateContextWithTaskType(loggerHandle, "test_active") if err != nil { fmt.Printf("❌ 活跃logger %d 异常: %s -> %v\n", i, loggerHandle, err) } else { err = wrapper.LogInfo(ctxHandle, "测试活跃logger是否正常工作") if err != nil { fmt.Printf("❌ 活跃logger %d 记录日志失败: %s -> %v\n", i, loggerHandle, err) } else { fmt.Printf("✅ 活跃logger %d 正常工作: %s\n", i, loggerHandle) activeWorkingCount++ } } } // 阶段6: 分析测试结果 fmt.Println("\n📋 阶段6: 资源回收测试结果分析") fmt.Printf(" 初始创建: %d 个logger\n", len(activeLoggers)+len(idleLoggers)) fmt.Printf(" 活跃logger正常: %d/%d\n", activeWorkingCount, len(activeLoggers)) fmt.Printf(" 闲置logger仍然工作: %d/%d\n", stillWorkingCount, len(idleLoggers)) fmt.Printf(" 内存变化: %.2fMB\n", memoryChange) // 资源回收效果评估 if stillWorkingCount < len(idleLoggers) { fmt.Println("🎯 资源回收效果: ✅ 检测到可能的资源回收") fmt.Println(" 说明: 部分闲置logger无法使用,可能已被自动清理机制回收") } else { fmt.Println("🎯 资源回收效果: ⚠️ 未检测到明显的资源回收") fmt.Println(" 说明: 所有logger仍然可用,可能因为:") fmt.Println(" - 测试时间不足以触发自动清理") fmt.Println(" - 自动清理机制配置时间较长") fmt.Println(" - 所有logger仍被认定为活跃状态") } // 阶段7: 显式清理 fmt.Println("\n🧹 阶段7: 显式清理所有资源") result, err := wrapper.CloseAllLoggers() if err != nil { log.Printf("关闭所有logger失败: %v", err) } else { fmt.Printf("✅ %s\n", result) } // 最终资源状态 fmt.Println("\n📊 最终资源状态:") monitorResources() fmt.Println("🧪 资源回收验证测试完成") } func getMemoryUsage() float64 { var m runtime.MemStats runtime.ReadMemStats(&m) return float64(m.Alloc) / 1024 / 1024 } // 文件系统资源检查 func checkFileSystemResources() { fmt.Println("\n📁 文件系统资源检查:") // 检查日志目录 dirs := []string{"./resource_cleanup_test", "./test_logs"} for _, dir := range dirs { if _, err := os.Stat(dir); !os.IsNotExist(err) { files, _ := filepath.Glob(filepath.Join(dir, "*", "*.log")) fmt.Printf(" 目录 %s: %d 个日志文件\n", dir, len(files)) // 显示文件详情 for _, file := range files { info, err := os.Stat(file) if err == nil { fmt.Printf(" - %s (大小: %.2fKB, 修改时间: %s)\n", filepath.Base(file), float64(info.Size())/1024, info.ModTime().Format("15:04:05")) } } } else { fmt.Printf(" 目录 %s: 不存在\n", dir) } } } func main() { config := `{ "log_dir": "./test_logs_reading", "level": 1, "split_type": 1, "max_size": 1048576, "max_count": 5, "enable_caller": true, "default_task_type": "reading_test" }` var configs Config err2 := json.Unmarshal([]byte(config), &configs) if err2 != nil { fmt.Println(err2) } logger, err := NewLogger(configs) if err != nil { fmt.Println(err) } fmt.Println(logger) fmt.Println("🚀 开始资源回收验证测试") // 确定DLL路径 dllPath := "logger.dll" if _, err := os.Stat(dllPath); os.IsNotExist(err) { log.Fatalf("DLL文件不存在: %s", dllPath) } // 创建包装器 wrapper, err := NewLoggerWrapper(dllPath) if err != nil { log.Fatalf("创建日志包装器失败: %v", err) } defer wrapper.Close() fmt.Println("✅ DLL加载成功") // 初始资源状态 fmt.Println("\n📈 初始资源状态:") monitorResources() // 运行日志读取功能测试 testLogReadingFunctions(wrapper) // 运行资源回收测试 testResourceCleanup(wrapper) // 检查文件系统资源 checkFileSystemResources() fmt.Println("\n🎉 资源回收验证完成!") fmt.Println("\n💡 测试说明:") fmt.Println(" - 本测试创建了活跃和闲置的logger来模拟真实使用场景") fmt.Println(" - 通过观察闲置logger的可用性来验证自动回收机制") fmt.Println(" - 内存使用趋势可以帮助识别资源泄漏") fmt.Println(" - 实际自动清理需要较长时间(2小时未使用)") }