Files
2026-06-24 03:16:40 +08:00

105 lines
2.9 KiB
Go

// Copyright (c) 2026 S.A. (SNOWARE). Written for SCU Team.
// Licensed under MulanPubL-2.0.
// You may obtain a copy at: http://license.coscl.org.cn/MulanPubL-2.0
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND.
// Package logger 基于 zerolog 的日志初始化,支持控制台输出(可读格式)+ 文件输出(JSON 结构化)。
// 日志目录优先级:exe 同目录 log/ → %APPDATA%/SRemoteConnectClient/log/ → 当前工作目录 log/ → 仅控制台输出。
package logger
import (
"io"
"os"
"path/filepath"
"time"
"github.com/rs/zerolog"
)
// Log 包级全局日志实例,供外部包使用。
var Log zerolog.Logger
// Init 初始化全局日志。
// 尝试在 exe 目录下创建 log/,若无写权限(多用户安装场景)则回退到 %APPDATA%。
// 日志按日期命名,控制台使用可读格式(带颜色),文件使用 JSON 结构化格式。
func Init() {
consoleWriter := zerolog.ConsoleWriter{
Out: os.Stdout,
TimeFormat: "2006-01-02 15:04:05",
NoColor: false,
}
logFile := openLogFile()
if logFile == nil {
// 连 %APPDATA% 都写不了,仅控制台输出
Log = zerolog.New(consoleWriter).
With().Timestamp().Caller().Logger().
Level(zerolog.DebugLevel)
return
}
multi := io.MultiWriter(consoleWriter, logFile)
Log = zerolog.New(multi).
With().Timestamp().Caller().Logger().
Level(zerolog.DebugLevel)
}
// openLogFile 按优先级尝试打开日志文件。
// 返回 nil 表示无法创建日志文件,此时程序仅输出到控制台。
func openLogFile() *os.File {
logDir, err := resolveLogDir()
if err != nil {
return nil
}
today := time.Now().Format("2006-01-02")
logPath := filepath.Join(logDir, today+".log")
f, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err != nil {
return nil
}
return f
}
// resolveLogDir 解析日志目录。
// 优先使用 exe 同目录下的 log/;若不可写则使用 %APPDATA%/SRemoteConnectClient/log/。
// [PORTBARR]
func resolveLogDir() (string, error) {
// 1. 尝试 exe 同目录
exePath, _ := os.Executable()
if exePath != "" {
dir := filepath.Join(filepath.Dir(exePath), "log")
if err := os.MkdirAll(dir, 0755); err == nil {
// 验证确实可写
testFile := filepath.Join(dir, ".write_test")
if f, err := os.Create(testFile); err == nil {
f.Close()
os.Remove(testFile)
return dir, nil
}
}
}
// 2. 回退到 %APPDATA%/SRemoteConnectClient/log
appData := os.Getenv("APPDATA")
if appData == "" {
appData = os.Getenv("LOCALAPPDATA")
}
if appData != "" {
dir := filepath.Join(appData, "SRemoteConnectClient", "log")
if err := os.MkdirAll(dir, 0755); err == nil {
return dir, nil
}
}
// 3. 最后尝试当前工作目录
cwd, _ := os.Getwd()
dir := filepath.Join(cwd, "log")
if err := os.MkdirAll(dir, 0755); err == nil {
return dir, nil
}
return "", os.ErrPermission
}