Initial commit: frpc-client-win v1.0
Single-file Windows frpc service wrapper. Features: - Embeds frpc v0.70.1 (16.7MB) into one 18.9MB EXE - Registers as Windows Service (auto-start, no login required) - Auto-detects frpc config by three-tier search: 1. frpc.ini 2. frpc.toml 3. any *.ini/*.toml in exe dir - Self-healing supervisor: frpc crashes are restarted with exponential backoff (2s..60s), capped to avoid log spam - Console subsystem so install/start show feedback - FreeConsole on SCM start to suppress console window - Logs to <exe-dir>/logs/service.log Subcommands: install start stop remove import -d Smoke test (smoke_linux.go) covers all config detection paths including the custom-name (e.g. frpc-mike.ini) use case.
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
// Package logger 提供一个轻量日志器,把消息写入指定目录下的 service.log。
|
||||
//
|
||||
// 设计要点:
|
||||
//
|
||||
// - 文件日志始终启用,便于事后取证
|
||||
// - 所有写入加锁,frpc 输出 + 本服务输出可能并发
|
||||
// - 服务模式下,日志必须写到 EXE 同目录(SCM 默认工作目录是 System32)
|
||||
// - 不再同时写 stdout - 服务模式已经 FreeConsole,CLI 模式 stdout 由 main 自己处理
|
||||
package logger
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Log 是简易同步日志器。
|
||||
type Log struct {
|
||||
mu sync.Mutex
|
||||
f *os.File
|
||||
}
|
||||
|
||||
const timeLayout = "2006-01-02 15:04:05"
|
||||
|
||||
// New 在 logDir 下创建日志文件并返回 Log。
|
||||
//
|
||||
// logDir 必须传绝对路径;不存在会自动创建。
|
||||
func New(logDir string) (*Log, error) {
|
||||
if err := os.MkdirAll(logDir, 0o755); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f, err := os.OpenFile(
|
||||
filepath.Join(logDir, "service.log"),
|
||||
os.O_CREATE|os.O_APPEND|os.O_WRONLY,
|
||||
0o644,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Log{f: f}, nil
|
||||
}
|
||||
|
||||
// Close 关闭底层文件句柄。
|
||||
func (l *Log) Close() error {
|
||||
if l == nil || l.f == nil {
|
||||
return nil
|
||||
}
|
||||
return l.f.Close()
|
||||
}
|
||||
|
||||
// Info 写一条普通日志。
|
||||
func (l *Log) Info(format string, args ...any) { l.write("INFO", format, args...) }
|
||||
func (l *Log) Warn(format string, args ...any) { l.write("WARN", format, args...) }
|
||||
func (l *Log) Error(format string, args ...any) { l.write("ERROR", format, args...) }
|
||||
|
||||
// FrpcRedirect 返回一个 io.Writer,frpc 的 stdout/stderr 重定向到这里。
|
||||
func (l *Log) FrpcRedirect() io.Writer {
|
||||
return &frpcWriter{l: l}
|
||||
}
|
||||
|
||||
type frpcWriter struct{ l *Log }
|
||||
|
||||
func (w *frpcWriter) Write(p []byte) (int, error) {
|
||||
w.l.write("FRPC", "%s", string(p))
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func (l *Log) write(level, format string, args ...any) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
|
||||
msg := fmt.Sprintf(format, args...)
|
||||
line := fmt.Sprintf("[%s] [%s] %s\n", time.Now().Format(timeLayout), level, msg)
|
||||
|
||||
if l.f != nil {
|
||||
_, _ = l.f.WriteString(line)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user