粘贴图根修(Ctrl+V直读系统剪贴板,不再依赖不可靠的paste事件)/SFTP串台双刀(拨号期禁回退本机+盘符路径自动回根)/PDF导出目录设置与文案带落盘路径/性能批(连接保存下载防重/md渲染防抖+预热/hljs按需加载/审计锁外置/快捷方式解析超时/webview-watch可停)/前端62类型错清零(含keyPath蛇形字段炸弹)/目录图标字段错位修复
90 lines
2.4 KiB
Go
90 lines
2.4 KiB
Go
package common
|
||
|
||
import (
|
||
"fmt"
|
||
"io"
|
||
"log/slog"
|
||
"os"
|
||
"path/filepath"
|
||
"sync"
|
||
"time"
|
||
)
|
||
|
||
// GetLogsDir 日志目录:~/.u-desk/logs(audit 日志同目录)
|
||
func GetLogsDir() string {
|
||
return filepath.Join(GetUserDataDir(), "logs")
|
||
}
|
||
|
||
// dailyFile 按天滚动的追加写文件:每次写时发现日期变化即切换新文件。
|
||
// 只做按天滚动,不搞大小上限/压缩/清理,崩溃取证够用。
|
||
type dailyFile struct {
|
||
mu sync.Mutex
|
||
dir string
|
||
naming func(t time.Time) string
|
||
f *os.File
|
||
day string
|
||
}
|
||
|
||
func newDailyFile(dir string, naming func(time.Time) string) *dailyFile {
|
||
return &dailyFile{dir: dir, naming: naming}
|
||
}
|
||
|
||
func (w *dailyFile) Write(p []byte) (int, error) {
|
||
w.mu.Lock()
|
||
defer w.mu.Unlock()
|
||
|
||
now := time.Now()
|
||
day := now.Format("2006-01-02")
|
||
if w.f != nil && day != w.day {
|
||
w.f.Close()
|
||
w.f = nil
|
||
}
|
||
if w.f == nil {
|
||
if err := os.MkdirAll(w.dir, 0755); err != nil {
|
||
return 0, err
|
||
}
|
||
f, err := os.OpenFile(filepath.Join(w.dir, w.naming(now)), os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
|
||
if err != nil {
|
||
return 0, err
|
||
}
|
||
w.f, w.day = f, day
|
||
}
|
||
return w.f.Write(p)
|
||
}
|
||
|
||
func (w *dailyFile) Close() error {
|
||
w.mu.Lock()
|
||
defer w.mu.Unlock()
|
||
if w.f == nil {
|
||
return nil
|
||
}
|
||
err := w.f.Close()
|
||
w.f = nil
|
||
return err
|
||
}
|
||
|
||
// FileLogOut 应用文件日志的底层写出器(stderr + 按天文件 MultiWriter)。
|
||
// SetupFileLogging 之后有效;供需要独立过滤级别的 logger 复用同一落盘目的地
|
||
// (如 wails 系统日志降到 Warn,业务 slog.Default() 保持 INFO 全量)。
|
||
var FileLogOut io.Writer = os.Stderr
|
||
|
||
// SetupFileLogging slog 双路落盘:stderr + ~/.u-desk/logs/app-YYYY-MM-DD.log。
|
||
// windowsgui 下 stderr 被丢弃,文件路是日志唯一可见载体;
|
||
// 文件侧打不开时静默降级为仅 stderr,不因日志失败阻断启动。
|
||
func SetupFileLogging() {
|
||
out := io.Writer(os.Stderr)
|
||
w := newDailyFile(GetLogsDir(), func(t time.Time) string {
|
||
return fmt.Sprintf("app-%s.log", t.Format("2006-01-02"))
|
||
})
|
||
// 预开当天文件探测文件侧可用性(Write 内部含懒开/换天逻辑)
|
||
if _, err := w.Write(nil); err == nil {
|
||
// 文件必须放 MultiWriter 首位:windowsgui 下 stderr 句柄无效,
|
||
// MultiWriter 按序短路,stderr 在前会吞掉后续文件路(真机零日志的根因)
|
||
out = io.MultiWriter(w, os.Stderr)
|
||
} else {
|
||
w.Close()
|
||
}
|
||
FileLogOut = out
|
||
slog.SetDefault(slog.New(slog.NewTextHandler(out, nil)))
|
||
}
|