package main /* #include */ import "C" import ( "bufio" "context" "encoding/json" "fmt" "io" "os" "path/filepath" "regexp" "runtime" "sort" "strings" "sync" "time" "unsafe" ) // LogLevel 日志级别类型 type LogLevel int const ( LevelSuccess LogLevel = iota LevelInfo LevelWarning LevelError ) // SplitType 日志分片方式 type SplitType int const ( SplitByMonth SplitType = iota SplitByDay SplitByHour SplitByMinute SplitBySecond ) // RotateType 日志分割方式 type RotateType int const ( RotateBySize RotateType = iota RotateByCount ) // 上下文键类型 type contextKey string const ( loggerKey contextKey = "logger" taskTypeKey contextKey = "task_type" ) // LevelFileLogger 每个级别的文件日志器 type LevelFileLogger struct { mu sync.RWMutex // 读写锁,保证并发安全 level LogLevel // 日志级别 logDir string // 日志目录 splitType SplitType // 分片方式 rotateType RotateType // 轮转方式 maxSize int64 // 最大文件大小 maxCount int // 最大文件数量 currentFile *os.File // 当前日志文件 currentDate string // 当前日期 writer io.Writer // 写入器 taskType string // 任务类型 } // Logger 日志结构体 type Logger struct { mu sync.RWMutex baseLogDir string // 基础日志目录 splitType SplitType // 分片方式 rotateType RotateType // 轮转方式 maxSize int64 // 最大文件大小 maxCount int // 最大文件数量 level LogLevel // 日志级别 enableCaller bool // 是否启用调用者信息 defaultTaskType string // 默认任务类型 levelLoggers map[string]*LevelFileLogger // key: "level-taskType" 级别日志器映射 lastUsed time.Time // 最后使用时间 } // Config 日志配置 type Config struct { LogDir string SplitType SplitType RotateType RotateType MaxSize int64 MaxCount int Level LogLevel EnableCaller bool DefaultTaskType string // 默认任务类型 } // DLLConfig 用于JSON解析的配置结构体 type DLLConfig struct { LogDir string `json:"log_dir"` SplitType SplitType `json:"split_type"` RotateType RotateType `json:"rotate_type"` MaxSize int64 `json:"max_size"` MaxCount int `json:"max_count"` Level LogLevel `json:"level"` EnableCaller bool `json:"enable_caller"` DefaultTaskType string `json:"default_task_type"` } // 日志级别字符串映射 var levelStrings = map[LogLevel]string{ LevelSuccess: "SUCCESS", LevelInfo: "INFO", LevelWarning: "WARNING", LevelError: "ERROR", } // 级别目录名映射 var levelDirs = map[LogLevel]string{ LevelSuccess: "success", LevelInfo: "info", LevelWarning: "warning", LevelError: "error", } // 分片格式映射 var splitFormats = map[SplitType]string{ SplitByMonth: "2006-01", SplitByDay: "2006-01-02", SplitByHour: "2006-01-02-15", SplitByMinute: "2006-01-02-15-04", SplitBySecond: "2006-01-02-15-04-05", } // 全局变量 var ( loggers = make(map[string]*Logger) contexts = make(map[string]context.Context) globalMu sync.Mutex lastCleanup time.Time ) // 初始化自动清理 func init() { go autoCleanupRoutine() } func autoCleanupRoutine() { ticker := time.NewTicker(30 * time.Minute) // 每30分钟清理一次 defer ticker.Stop() for range ticker.C { cleanupExpiredResources() // 清理2小时未使用的资源 } } func cleanupExpiredResources() { globalMu.Lock() defer globalMu.Unlock() now := time.Now() cutoff := now.Add(-2 * time.Hour) // 2小时未使用的资源 for handle, logger := range loggers { if logger == nil { delete(loggers, handle) delete(contexts, handle) continue } // 使用cutoff:清理2小时未使用的logger logger.mu.RLock() lastUsed := logger.lastUsed logger.mu.RUnlock() if lastUsed.Before(cutoff) { logger.Close() // 关闭文件资源 delete(loggers, handle) delete(contexts, handle) } } lastCleanup = now } // NewLogger 创建新的日志实例 func NewLogger(config Config) (*Logger, error) { // 确保基础日志目录存在 if err := os.MkdirAll(config.LogDir, 0755); err != nil { return nil, fmt.Errorf("创建日志目录失败: %v", err) } // 设置默认任务类型 if config.DefaultTaskType == "" { config.DefaultTaskType = "main" } logger := &Logger{ baseLogDir: config.LogDir, splitType: config.SplitType, rotateType: config.RotateType, maxSize: config.MaxSize, maxCount: config.MaxCount, level: config.Level, enableCaller: config.EnableCaller, defaultTaskType: config.DefaultTaskType, levelLoggers: make(map[string]*LevelFileLogger), } return logger, nil } // WithContext 将logger存入context func (l *Logger) WithContext(ctx context.Context) context.Context { return context.WithValue(ctx, loggerKey, l) } // WithTaskType 将任务类型存入context func (l *Logger) WithTaskType(ctx context.Context, taskType string) context.Context { return context.WithValue(ctx, taskTypeKey, taskType) } // FromContext 从context获取logger func FromContext(ctx context.Context) *Logger { if logger, ok := ctx.Value(loggerKey).(*Logger); ok { return logger } return nil } // getTaskTypeFromContext 从context获取任务类型,如果没有则返回默认值 func (l *Logger) getTaskTypeFromContext(ctx context.Context) string { if taskType, ok := ctx.Value(taskTypeKey).(string); ok && taskType != "" { return taskType } return l.defaultTaskType } // getCurrentDate 获取当前日期字符串 func (lfl *LevelFileLogger) getCurrentDate() string { format := splitFormats[lfl.splitType] return time.Now().Format(format) } // getLogFileName 获取日志文件名 func (lfl *LevelFileLogger) getLogFileName(date string) string { fileName := fmt.Sprintf("%s-%s-%s.log", levelStrings[lfl.level], lfl.taskType, date) return filepath.Join(lfl.logDir, fileName) } // getLevelLoggerKey 生成级别日志器的key func getLevelLoggerKey(level LogLevel, taskType string) string { return fmt.Sprintf("%d-%s", level, taskType) } // getLevelLogger 获取或创建指定任务类型的级别日志器 func (l *Logger) getLevelLogger(level LogLevel, taskType string) (*LevelFileLogger, error) { key := getLevelLoggerKey(level, taskType) // 第一次检查(读锁) l.mu.RLock() if levelLogger, exists := l.levelLoggers[key]; exists { l.mu.RUnlock() return levelLogger, nil } l.mu.RUnlock() // 获取写锁创建新的日志器 l.mu.Lock() defer l.mu.Unlock() // 第二次检查(写锁内),防止并发创建 if levelLogger, exists := l.levelLoggers[key]; exists { return levelLogger, nil } // 创建级别目录 levelDir := filepath.Join(l.baseLogDir, levelDirs[level]) if err := os.MkdirAll(levelDir, 0755); err != nil { return nil, fmt.Errorf("创建级别目录失败: %v", err) } levelLogger := &LevelFileLogger{ level: level, logDir: levelDir, splitType: l.splitType, rotateType: l.rotateType, maxSize: l.maxSize, maxCount: l.maxCount, taskType: taskType, } if err := levelLogger.rotateFile(); err != nil { return nil, err } // 使用复合key存储 l.levelLoggers[key] = levelLogger return levelLogger, nil } // rotateFile 轮转日志文件 func (lfl *LevelFileLogger) rotateFile() error { lfl.mu.Lock() defer lfl.mu.Unlock() currentDate := lfl.getCurrentDate() // 如果日期没变且不需要按大小分割,则不需要轮转 if lfl.currentDate == currentDate && lfl.currentFile != nil { if lfl.rotateType == RotateBySize { info, err := lfl.currentFile.Stat() if err == nil && info.Size() < lfl.maxSize { return nil } } else { return nil } } // 关闭当前文件 if lfl.currentFile != nil { err := lfl.currentFile.Close() if err != nil { return err } } // 创建新文件 fileName := lfl.getLogFileName(currentDate) file, err := os.OpenFile(fileName, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644) if err != nil { return fmt.Errorf("创建日志文件失败: %v", err) } lfl.currentFile = file lfl.currentDate = currentDate lfl.writer = file // 如果启用了控制台输出,同时输出到控制台 if os.Getenv("LOG_CONSOLE") == "true" { lfl.writer = io.MultiWriter(file, os.Stdout) } // 处理文件数量限制 if lfl.rotateType == RotateByCount { lfl.cleanupOldFiles() } return nil } // cleanupOldFiles 清理旧日志文件 func (lfl *LevelFileLogger) cleanupOldFiles() { pattern := filepath.Join(lfl.logDir, fmt.Sprintf("%s-%s-*.log", levelStrings[lfl.level], lfl.taskType)) files, err := filepath.Glob(pattern) if err != nil { return } if len(files) <= lfl.maxCount { return } // 按修改时间排序,删除最旧的文件 for i := 0; i < len(files)-lfl.maxCount; i++ { err := os.Remove(files[i]) if err != nil { return } } } // writeLog 写入日志 func (lfl *LevelFileLogger) writeLog(message string) error { // 检查是否需要轮转文件 if err := lfl.rotateFile(); err != nil { return err } lfl.mu.RLock() defer lfl.mu.RUnlock() if lfl.writer != nil { _, err := lfl.writer.Write([]byte(message)) return err } return nil } // getCallerInfo 获取调用者信息 func (l *Logger) getCallerInfo() string { if !l.enableCaller { return "" } _, file, line, ok := runtime.Caller(3) // 跳过3层调用栈 if !ok { return "" } return fmt.Sprintf("%s:%d", filepath.Base(file), line) } // log 基础日志方法 func (l *Logger) log(ctx context.Context, level LogLevel, format string, args ...interface{}) { l.mu.Lock() l.lastUsed = time.Now() l.mu.Unlock() // 级别过滤 if level < l.level { return } // 获取任务类型 taskType := l.getTaskTypeFromContext(ctx) // 获取或创建级别日志器 levelLogger, err := l.getLevelLogger(level, taskType) if err != nil { return } // 格式化日志条目 timestamp := time.Now().Format("2006-01-02 15:04:05.000") levelStr := levelStrings[level] message := fmt.Sprintf(format, args...) callerInfo := l.getCallerInfo() var logEntry string if callerInfo != "" { logEntry = fmt.Sprintf("[%s] [%s] [%s] [%s] %s\n", timestamp, levelStr, taskType, callerInfo, message) } else { logEntry = fmt.Sprintf("[%s] [%s] [%s] %s\n", timestamp, levelStr, taskType, message) } // 写入对应级别的日志文件 err = levelLogger.writeLog(logEntry) } // Success 记录成功日志 func (l *Logger) Success(ctx context.Context, format string, args ...interface{}) { l.log(ctx, LevelSuccess, format, args...) } // Info 记录信息日志 func (l *Logger) Info(ctx context.Context, format string, args ...interface{}) { l.log(ctx, LevelInfo, format, args...) } // Warning 记录警告日志 func (l *Logger) Warning(ctx context.Context, format string, args ...interface{}) { l.log(ctx, LevelWarning, format, args...) } // Error 记录错误日志 func (l *Logger) Error(ctx context.Context, format string, args ...interface{}) { l.log(ctx, LevelError, format, args...) } // TimeCost 耗时记录方法 func (l *Logger) TimeCost(ctx context.Context, operation string) func() { start := time.Now() return func() { cost := time.Since(start) l.Info(ctx, "%s 耗时: %s", operation, cost.String()) } } // TimeCostMs 返回毫秒级耗时(直接返回耗时值) func (l *Logger) TimeCostMs(ctx context.Context, operation string) func() float64 { start := time.Now() return func() float64 { cost := time.Since(start) ms := float64(cost.Nanoseconds()) / 1e6 l.Info(ctx, "%s 耗时: %.3fms", operation, ms) return ms } } // Close 关闭所有日志文件 func (l *Logger) Close() error { l.mu.Lock() defer l.mu.Unlock() var lastErr error for _, levelLogger := range l.levelLoggers { levelLogger.mu.Lock() if levelLogger.currentFile != nil { if err := levelLogger.currentFile.Close(); err != nil { lastErr = err } } levelLogger.mu.Unlock() } // 清空映射 l.levelLoggers = make(map[string]*LevelFileLogger) return lastErr } // ============================ DLL 导出函数 ============================ //export CreateLogger func CreateLogger(configJSON *C.char) *C.char { jsonStr := C.GoString(configJSON) var config DLLConfig if err := json.Unmarshal([]byte(jsonStr), &config); err != nil { return C.CString("错误: " + err.Error()) } loggerConfig := Config{ LogDir: config.LogDir, SplitType: config.SplitType, RotateType: config.RotateType, MaxSize: config.MaxSize, MaxCount: config.MaxCount, Level: config.Level, EnableCaller: config.EnableCaller, DefaultTaskType: config.DefaultTaskType, } logger, err := NewLogger(loggerConfig) if err != nil { return C.CString("错误: " + err.Error()) } globalMu.Lock() defer globalMu.Unlock() handle := fmt.Sprintf("logger_%d", time.Now().UnixNano()) loggers[handle] = logger // 创建初始上下文 ctx := context.Background() ctx = logger.WithContext(ctx) contexts[handle] = ctx return C.CString(handle) } //export CreateContextWithTaskType func CreateContextWithTaskType(loggerHandle *C.char, taskType *C.char) *C.char { handle := C.GoString(loggerHandle) taskTypeStr := C.GoString(taskType) globalMu.Lock() defer globalMu.Unlock() logger, exists := loggers[handle] if !exists { return C.CString("错误: 无效的logger句柄") } baseCtx, exists := contexts[handle] if !exists { return C.CString("错误: 无效的上下文") } // 创建带任务类型的新上下文 ctx := logger.WithTaskType(baseCtx, taskTypeStr) ctxHandle := fmt.Sprintf("ctx_%d", time.Now().UnixNano()) contexts[ctxHandle] = ctx return C.CString(ctxHandle) } //export LogInfo func LogInfo(ctxHandle *C.char, message *C.char) { handle := C.GoString(ctxHandle) msg := C.GoString(message) globalMu.Lock() ctx, exists := contexts[handle] globalMu.Unlock() if !exists { return } if logger := FromContext(ctx); logger != nil { logger.Info(ctx, "%s", msg) } } //export LogError func LogError(ctxHandle *C.char, message *C.char) { handle := C.GoString(ctxHandle) msg := C.GoString(message) globalMu.Lock() ctx, exists := contexts[handle] globalMu.Unlock() if !exists { return } if logger := FromContext(ctx); logger != nil { logger.Error(ctx, "%s", msg) } } //export LogWarning func LogWarning(ctxHandle *C.char, message *C.char) { handle := C.GoString(ctxHandle) msg := C.GoString(message) globalMu.Lock() ctx, exists := contexts[handle] globalMu.Unlock() if !exists { return } if logger := FromContext(ctx); logger != nil { logger.Warning(ctx, "%s", msg) } } //export LogSuccess func LogSuccess(ctxHandle *C.char, message *C.char) { handle := C.GoString(ctxHandle) msg := C.GoString(message) globalMu.Lock() ctx, exists := contexts[handle] globalMu.Unlock() if !exists { return } if logger := FromContext(ctx); logger != nil { logger.Success(ctx, "%s", msg) } } //export FreeString func FreeString(str *C.char) { // 这是专门用于释放C.CString返回的内存 C.free(unsafe.Pointer(str)) } //export CloseAllLoggers func CloseAllLoggers() *C.char { globalMu.Lock() defer globalMu.Unlock() var errorCount int var lastError string // 关闭所有日志器 for handle, logger := range loggers { if err := logger.Close(); err != nil { errorCount++ lastError = err.Error() } delete(loggers, handle) delete(contexts, handle) } if errorCount > 0 { return C.CString(fmt.Sprintf("关闭了%d个logger,其中%d个出错,最后错误: %s", len(loggers), errorCount, lastError)) } return C.CString("成功关闭所有logger") } /* // 便捷函数 - 通过上下文使用 func Success(ctx context.Context, format string, args ...interface{}) { if logger := FromContext(ctx); logger != nil { logger.Success(ctx, format, args...) } } func Info(ctx context.Context, format string, args ...interface{}) { if logger := FromContext(ctx); logger != nil { logger.Info(ctx, format, args...) } } func Warning(ctx context.Context, format string, args ...interface{}) { if logger := FromContext(ctx); logger != nil { logger.Warning(ctx, format, args...) } } func Error(ctx context.Context, format string, args ...interface{}) { if logger := FromContext(ctx); logger != nil { logger.Error(ctx, format, args...) } } func TimeCost(ctx context.Context, operation string) func() { if logger := FromContext(ctx); logger != nil { return logger.TimeCost(ctx, operation) } return func() {} } func TimeCostMs(ctx context.Context, operation string) func() float64 { if logger := FromContext(ctx); logger != nil { return logger.TimeCostMs(ctx, operation) } return func() float64 { return 0 } } // WithTaskType 便捷函数 - 将任务类型存入context func WithTaskType(ctx context.Context, taskType string) context.Context { if logger := FromContext(ctx); logger != nil { return logger.WithTaskType(ctx, taskType) } return ctx } */ // ============================ 日志读取功能 ============================ // LogEntry 表示一个日志条目 type LogEntry struct { Timestamp string `json:"timestamp"` Level string `json:"level"` TaskType string `json:"task_type"` Caller string `json:"caller,omitempty"` Message string `json:"message"` Time time.Time `json:"-"` } // ReadLogsConfig 读取日志的配置 type ReadLogsConfig struct { Level LogLevel `json:"level"` TaskType string `json:"task_type"` StartTime string `json:"start_time"` // 格式: 2006-01-02 15:04:05 EndTime string `json:"end_time"` // 格式: 2006-01-02 15:04:05 MaxEntries int `json:"max_entries"` // 最大返回条目数 } // parseLogLine 解析单行日志 func parseLogLine(line string) (*LogEntry, error) { // 正则表达式:匹配四个 [...] 块,后面跟任意消息 re := regexp.MustCompile(`^\[(.*?)\]\s+\[(.*?)\]\s+\[(.*?)\]\s+\[(.*?)\]\s+(.*)$`) matches := re.FindStringSubmatch(line) if len(matches) != 6 { return nil, fmt.Errorf("log line does not match expected format") } timestampStr := matches[1] levelStr := matches[2] taskType := matches[3] var caller, message string if len(matches) == 6 { // 有调用者信息 [caller] caller = matches[4] message = matches[5] } else { // 无调用者信息(只有三个 []) caller = "" message = matches[4] } // 解析时间戳(支持带毫秒格式:2006-01-02 15:04:05.000) var logTime time.Time var err error if strings.Contains(timestampStr, ".") { logTime, err = time.Parse("2006-01-02 15:04:05.000", timestampStr) } else { logTime, err = time.Parse("2006-01-02 15:04:05", timestampStr) } if err != nil { // 如果解析失败,保留字符串但 Time 为零值(或可跳过) logTime = time.Time{} } return &LogEntry{ Timestamp: timestampStr, Level: levelStr, TaskType: taskType, Caller: caller, Message: message, Time: logTime, }, nil } // readLogFile 读取单个日志文件 func (l *Logger) readLogFile(filePath string, config ReadLogsConfig) ([]LogEntry, error) { file, err := os.Open(filePath) if err != nil { return nil, err } defer file.Close() // 预解析时间范围 var startTime, endTime time.Time var hasStartTime, hasEndTime bool if config.StartTime != "" { if t, err := time.Parse("2006-01-02 15:04:05", config.StartTime); err == nil { startTime = t hasStartTime = true } } if config.EndTime != "" { if t, err := time.Parse("2006-01-02 15:04:05", config.EndTime); err == nil { endTime = t hasEndTime = true } } var entries []LogEntry scanner := bufio.NewScanner(file) for scanner.Scan() { line := scanner.Text() entry, err := parseLogLine(line) if err != nil { continue // 跳过无法解析的行 } // 过滤条件 if config.Level != -1 && levelStrings[config.Level] != entry.Level { continue } if config.TaskType != "" && config.TaskType != entry.TaskType { continue } // 时间过滤(仅当 Time 有效时才比较) if !entry.Time.IsZero() { if hasStartTime && entry.Time.Before(startTime) { continue } if hasEndTime && entry.Time.After(endTime) { continue } } entries = append(entries, *entry) // 检查最大条目数 if config.MaxEntries > 0 && len(entries) >= config.MaxEntries { break } } if err := scanner.Err(); err != nil { return nil, err } return entries, nil } // GetLogs 获取日志条目 func (l *Logger) GetLogs(config ReadLogsConfig) ([]LogEntry, error) { var allEntries []LogEntry // 确定要搜索的级别目录 levelsToSearch := []LogLevel{} if config.Level == -1 { // 搜索所有级别 for level := range levelDirs { levelsToSearch = append(levelsToSearch, level) } } else { levelsToSearch = append(levelsToSearch, config.Level) } for _, level := range levelsToSearch { levelDir := filepath.Join(l.baseLogDir, levelDirs[level]) // 构建文件匹配模式 pattern := filepath.Join(levelDir, "*.log") if config.TaskType != "" { pattern = filepath.Join(levelDir, fmt.Sprintf("%s-%s-*.log", levelStrings[level], config.TaskType)) } files, err := filepath.Glob(pattern) if err != nil { continue } // 按文件名排序(通常是时间顺序) sort.Strings(files) for _, file := range files { entries, err := l.readLogFile(file, config) if err != nil { continue } allEntries = append(allEntries, entries...) // 检查最大条目数 if config.MaxEntries > 0 && len(allEntries) >= config.MaxEntries { break } } if config.MaxEntries > 0 && len(allEntries) >= config.MaxEntries { break } } // 按时间排序 sort.Slice(allEntries, func(i, j int) bool { return allEntries[i].Time.Before(allEntries[j].Time) }) // 限制返回数量 if config.MaxEntries > 0 && len(allEntries) > config.MaxEntries { allEntries = allEntries[:config.MaxEntries] } return allEntries, nil } // ============================ DLL 导出函数 ============================ //export GetLogs func GetLogs(loggerHandle *C.char, configJSON *C.char) *C.char { handle := C.GoString(loggerHandle) jsonStr := C.GoString(configJSON) globalMu.Lock() logger, exists := loggers[handle] globalMu.Unlock() if !exists { return C.CString(`{"error": "无效的logger句柄"}`) } var config ReadLogsConfig if err := json.Unmarshal([]byte(jsonStr), &config); err != nil { return C.CString(`{"error": "配置解析错误: ` + err.Error() + `"}`) } // 设置默认值 if config.MaxEntries == 0 { config.MaxEntries = 1000 // 默认最大1000条 } entries, err := logger.GetLogs(config) if err != nil { return C.CString(`{"error": "读取日志失败: ` + err.Error() + `"}`) } result := map[string]interface{}{ "count": len(entries), "entries": entries, } jsonData, err := json.Marshal(result) if err != nil { return C.CString(`{"error": "JSON序列化失败: ` + err.Error() + `"}`) } return C.CString(string(jsonData)) } //export GetLogFiles func GetLogFiles(loggerHandle *C.char) *C.char { handle := C.GoString(loggerHandle) globalMu.Lock() logger, exists := loggers[handle] globalMu.Unlock() if !exists { return C.CString(`{"error": "无效的logger句柄"}`) } type LogFileInfo struct { Level string `json:"level"` TaskType string `json:"task_type"` FileName string `json:"file_name"` FileSize int64 `json:"file_size"` ModTime string `json:"mod_time"` } var logFiles []LogFileInfo for _, levelDirName := range levelDirs { levelDir := filepath.Join(logger.baseLogDir, levelDirName) pattern := filepath.Join(levelDir, "*.log") files, err := filepath.Glob(pattern) if err != nil { continue } for _, file := range files { info, err := os.Stat(file) if err != nil { continue } // 从文件名解析级别和任务类型 baseName := filepath.Base(file) parts := strings.Split(strings.TrimSuffix(baseName, ".log"), "-") if len(parts) >= 3 { logFile := LogFileInfo{ Level: parts[0], TaskType: parts[1], FileName: baseName, FileSize: info.Size(), ModTime: info.ModTime().Format("2006-01-02 15:04:05"), } logFiles = append(logFiles, logFile) } } } result := map[string]interface{}{ "count": len(logFiles), "files": logFiles, } jsonData, err := json.Marshal(result) if err != nil { return C.CString(`{"error": "JSON序列化失败: ` + err.Error() + `"}`) } return C.CString(string(jsonData)) } // LOGGER_VERSION 版本号 const ( LOGGER_VERSION = "v1" ) // 获取版本信息 // //export GetVersion func GetVersion() *C.char { return C.CString(LOGGER_VERSION) } // main 函数 - DLL必须的入口 func main() { }