191 lines
4.6 KiB
Go
191 lines
4.6 KiB
Go
// Package applog 监管中转运行日志(与云端 xk_hy_transit_* 表互补,供外网机排错)。
|
||
//
|
||
// - Pullf:步骤 ①② 拉取/建批次(pull-YYYY-MM-DD.log)
|
||
// - Pushf:步骤 ③ 转发政务云、PDF 上传(push-YYYY-MM-DD.log)
|
||
// - Appf:进程级事件、定时任务、配置告警(app-YYYY-MM-DD.log)
|
||
// - Consolef:仅控制台(开始/结束/错误摘要)
|
||
//
|
||
// 详情默认只写文件;SetQuietConsole(false) 时 Pullf/Pushf/Appf 同时 echo 到控制台(调试)。
|
||
package applog
|
||
|
||
import (
|
||
"fmt"
|
||
"log"
|
||
"os"
|
||
"path/filepath"
|
||
"strings"
|
||
"sync"
|
||
"time"
|
||
)
|
||
|
||
var (
|
||
mu sync.Mutex
|
||
logRoot string
|
||
logService string
|
||
currentDay string
|
||
pullF *os.File
|
||
pushF *os.File
|
||
appF *os.File
|
||
quietConsole = true
|
||
stdFlags = log.LstdFlags
|
||
)
|
||
|
||
// Init 初始化日志目录。service 如 transit、forward。
|
||
// LOG_DIR 可覆盖根目录(默认 {程序目录}/log/{service})。
|
||
func Init(service string) error {
|
||
mu.Lock()
|
||
defer mu.Unlock()
|
||
|
||
logService = service
|
||
logRoot = resolveLogRoot(service)
|
||
if err := os.MkdirAll(logRoot, 0o755); err != nil {
|
||
return fmt.Errorf("mkdir log root: %w", err)
|
||
}
|
||
if err := rotateDailyFilesLocked(); err != nil {
|
||
return err
|
||
}
|
||
log.Printf("applog: service=%s dir=%s", service, logRoot)
|
||
return nil
|
||
}
|
||
|
||
// Dir 返回当前日志根目录({root}/{service})。
|
||
func Dir() string {
|
||
mu.Lock()
|
||
defer mu.Unlock()
|
||
if logRoot != "" && logService != "" {
|
||
return logRoot
|
||
}
|
||
return resolveLogRoot("transit")
|
||
}
|
||
|
||
// SetQuietConsole 为 true 时 Pullf/Pushf/Appf 只写文件;false 时同时输出到控制台。
|
||
func SetQuietConsole(quiet bool) {
|
||
mu.Lock()
|
||
quietConsole = quiet
|
||
mu.Unlock()
|
||
}
|
||
|
||
// Consolef 仅输出到控制台(定时/单次任务的开始、结束、错误摘要)。
|
||
func Consolef(format string, args ...any) {
|
||
line := fmt.Sprintf(format, args...)
|
||
log.New(os.Stdout, "", stdFlags).Print(line)
|
||
}
|
||
|
||
// RunSeparator 在 app/pull/push 三个日文件中写入执行分隔行。
|
||
func RunSeparator(title string) {
|
||
mu.Lock()
|
||
defer mu.Unlock()
|
||
_ = rotateDailyFilesLocked()
|
||
line := strings.Repeat("=", 8) + " " + title + " " + strings.Repeat("=", 8)
|
||
writeLineLocked(appF, line)
|
||
writeLineLocked(pullF, line)
|
||
writeLineLocked(pushF, line)
|
||
}
|
||
|
||
func resolveLogRoot(service string) string {
|
||
if v := strings.TrimSpace(os.Getenv("LOG_DIR")); v != "" {
|
||
return filepath.Join(v, service)
|
||
}
|
||
base := programDir()
|
||
return filepath.Join(base, "log", service)
|
||
}
|
||
|
||
func programDir() string {
|
||
if exe, err := os.Executable(); err == nil {
|
||
dir := filepath.Dir(exe)
|
||
if strings.Contains(dir, "go-build") {
|
||
if wd, err := os.Getwd(); err == nil && wd != "" {
|
||
return wd
|
||
}
|
||
}
|
||
return dir
|
||
}
|
||
if wd, err := os.Getwd(); err == nil && wd != "" {
|
||
return wd
|
||
}
|
||
return "."
|
||
}
|
||
|
||
func rotateDailyFilesLocked() error {
|
||
day := time.Now().Format("2006-01-02")
|
||
if day == currentDay && pullF != nil && pushF != nil && appF != nil {
|
||
return nil
|
||
}
|
||
closeFile(&pullF)
|
||
closeFile(&pushF)
|
||
closeFile(&appF)
|
||
currentDay = day
|
||
var err error
|
||
pullF, err = openDailyFile(logRoot, "pull-"+day+".log")
|
||
if err != nil {
|
||
return err
|
||
}
|
||
pushF, err = openDailyFile(logRoot, "push-"+day+".log")
|
||
if err != nil {
|
||
return err
|
||
}
|
||
appF, err = openDailyFile(logRoot, "app-"+day+".log")
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return nil
|
||
}
|
||
|
||
func openDailyFile(dir, name string) (*os.File, error) {
|
||
f, err := os.OpenFile(filepath.Join(dir, name), os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0o644)
|
||
if err != nil {
|
||
log.Printf("applog: open %s: %v", name, err)
|
||
return nil, err
|
||
}
|
||
return f, nil
|
||
}
|
||
|
||
func closeFile(f **os.File) {
|
||
if f == nil || *f == nil {
|
||
return
|
||
}
|
||
_ = (*f).Close()
|
||
*f = nil
|
||
}
|
||
|
||
func writeLineLocked(f *os.File, line string) {
|
||
if f == nil {
|
||
return
|
||
}
|
||
_, _ = fmt.Fprintf(f, "%s\n", line)
|
||
}
|
||
|
||
func writeLocked(tag string, f *os.File, format string, args ...any) {
|
||
line := fmt.Sprintf(format, args...)
|
||
if f != nil {
|
||
log.New(f, "["+tag+"] ", stdFlags).Print(line)
|
||
}
|
||
if !quietConsole {
|
||
log.New(os.Stdout, "["+tag+"] ", stdFlags).Print(line)
|
||
}
|
||
}
|
||
|
||
// Pullf 拉取阶段日志(写 pull 日文件)。
|
||
func Pullf(format string, args ...any) {
|
||
mu.Lock()
|
||
defer mu.Unlock()
|
||
_ = rotateDailyFilesLocked()
|
||
writeLocked("pull", pullF, format, args...)
|
||
}
|
||
|
||
// Pushf 推送阶段日志(写 push 日文件)。
|
||
func Pushf(format string, args ...any) {
|
||
mu.Lock()
|
||
defer mu.Unlock()
|
||
_ = rotateDailyFilesLocked()
|
||
writeLocked("push", pushF, format, args...)
|
||
}
|
||
|
||
// Appf 应用级日志(写 app 日文件)。
|
||
func Appf(format string, args ...any) {
|
||
mu.Lock()
|
||
defer mu.Unlock()
|
||
_ = rotateDailyFilesLocked()
|
||
writeLocked("app", appF, format, args...)
|
||
}
|