Files
MyGoNavi/internal/logger/logger.go
Syngnat e6af5f966b 🔧 fix(driver/kingbase,mongodb): 修复外置驱动事务引用与连接测试链路问题
- 金仓外置驱动链路增加表名与变更字段归一化,修复 ApplyChanges 场景下双引号转义异常导致的 SQL 语法错误
- 新增金仓公共标识符工具并复用到 kingbase_impl 与 optional_driver_agent_impl,统一处理多重转义、schema.table 拆分与引用规范
- 金仓代理连接后自动探测并设置 search_path,降低查询时必须手写 schema 前缀的概率
- MongoDB 连接参数改为显式 host/hosts 优先,避免被 URI 中 localhost 覆盖;代理链路保留目标地址不再改写为本地地址
- 连接测试增加前后端超时收敛与日志增强,避免长时间转圈;连接错误文案在未启用 TLS 时移除误导性的“SSL”前缀
- 统一日志级别为 INFO/WARN/ERROR,默认日志目录收敛到 ~/.GoNavi/Logs,并补充驱动构建脚本 build-driver-agents.sh
2026-03-12 16:45:46 +08:00

220 lines
4.1 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 logger
import (
"errors"
"fmt"
"io"
"log"
"os"
"path/filepath"
"sort"
"strings"
"sync"
"time"
)
const (
envLogDir = "GONAVI_LOG_DIR"
appHiddenDir = ".GoNavi"
appLogDirName = "Logs"
logFileName = "gonavi.log"
logRotateMaxBytes = 10 * 1024 * 1024 // 10MB
logRotateMaxBackups = 10
)
var (
once sync.Once
logMu sync.Mutex
logInst *log.Logger
logFile *os.File
logPath string
)
func Init() {
once.Do(func() {
path, out := initOutput()
logMu.Lock()
defer logMu.Unlock()
logPath = path
logInst = log.New(out, "", log.Ldate|log.Ltime|log.Lmicroseconds)
logInst.Printf("[INFO] 日志初始化完成,日志文件:%s", logPath)
})
}
func Path() string {
Init()
logMu.Lock()
defer logMu.Unlock()
return logPath
}
func Close() {
Init()
logMu.Lock()
defer logMu.Unlock()
if logInst != nil {
logInst.SetOutput(os.Stderr)
}
if logFile != nil {
_ = logFile.Close()
logFile = nil
}
}
func Infof(format string, args ...any) {
printf("INFO", format, args...)
}
func Warnf(format string, args ...any) {
printf("WARN", format, args...)
}
func Errorf(format string, args ...any) {
printf("ERROR", format, args...)
}
func Error(err error, format string, args ...any) {
msg := fmt.Sprintf(format, args...)
if err == nil {
Errorf("%s", msg)
return
}
Errorf("%s错误链%s", msg, ErrorChain(err))
}
func ErrorChain(err error) string {
if err == nil {
return ""
}
var parts []string
seen := map[string]struct{}{}
cur := err
truncated := false
for i := 0; cur != nil && i < 20; i++ {
s := cur.Error()
if _, ok := seen[s]; !ok {
seen[s] = struct{}{}
parts = append(parts, s)
}
cur = errors.Unwrap(cur)
}
if cur != nil {
truncated = true
}
if len(parts) == 0 {
return err.Error()
}
if truncated {
parts = append(parts, "(错误链过长,已截断)")
}
return strings.Join(parts, " -> ")
}
func printf(level string, format string, args ...any) {
Init()
logMu.Lock()
defer logMu.Unlock()
inst := logInst
if inst == nil {
return
}
inst.Printf("[%s] %s", level, fmt.Sprintf(format, args...))
if logFile != nil {
_ = logFile.Sync()
}
}
func initOutput() (string, io.Writer) {
dir := strings.TrimSpace(os.Getenv(envLogDir))
if dir == "" {
dir = defaultLogDir()
}
if path, writer, ok := openLogFile(dir); ok {
return path, writer
}
fallbackDir := filepath.Join(os.TempDir(), appHiddenDir, appLogDirName)
if path, writer, ok := openLogFile(fallbackDir); ok {
return path, writer
}
return "", os.Stderr
}
func defaultLogDir() string {
home, err := os.UserHomeDir()
if err != nil || strings.TrimSpace(home) == "" {
return filepath.Join(os.TempDir(), appHiddenDir, appLogDirName)
}
return filepath.Join(home, appHiddenDir, appLogDirName)
}
func openLogFile(dir string) (string, io.Writer, bool) {
if strings.TrimSpace(dir) == "" {
return "", nil, false
}
if err := os.MkdirAll(dir, 0o755); err != nil {
return "", nil, false
}
path := filepath.Join(dir, logFileName)
rotateIfNeeded(path, dir)
f, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0o644)
if err != nil {
return "", nil, false
}
logFile = f
return path, f, true
}
func rotateIfNeeded(path, dir string) {
fi, err := os.Stat(path)
if err != nil || fi.IsDir() {
return
}
if fi.Size() < logRotateMaxBytes {
return
}
ts := time.Now().Format("20060102-150405")
rotated := filepath.Join(dir, fmt.Sprintf("gonavi-%s.log", ts))
if err := os.Rename(path, rotated); err != nil {
return
}
cleanupOldLogs(dir)
}
func cleanupOldLogs(dir string) {
entries, err := os.ReadDir(dir)
if err != nil {
return
}
type item struct {
name string
path string
}
var logs []item
for _, e := range entries {
if e.IsDir() {
continue
}
name := e.Name()
if !strings.HasPrefix(name, "gonavi-") || !strings.HasSuffix(name, ".log") {
continue
}
logs = append(logs, item{name: name, path: filepath.Join(dir, name)})
}
sort.Slice(logs, func(i, j int) bool { return logs[i].name > logs[j].name })
if len(logs) <= logRotateMaxBackups {
return
}
for _, it := range logs[logRotateMaxBackups:] {
_ = os.Remove(it.path)
}
}