daShangDao_kfzgw-info/logger/logger.go
2026-02-27 11:46:40 +08:00

1054 lines
25 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package main
/*
#include <stdlib.h>
*/
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() {
}