RedFlag/agent/internal/logging/process_logger.go
Fimeg 67e26be2d9 projection: begin the exact-path public epoch
The public tree and its history contain only the listed paths. Earlier projection history remains preserved internally.

Source-Sha: 913fde029b935671833254797f0f20f1eb9fabba

Policy-Sha: 913fde029b935671833254797f0f20f1eb9fabba

Tree-Digest: 180ae530c1058a2a5c89837bdce2d323ae83e669e38590ca72e75b8d92b7262f
2026-09-08 21:59:33 -04:00

63 lines
1.6 KiB
Go

package logging
import (
"fmt"
"io"
"log"
"os"
"path/filepath"
"runtime"
"sync"
"github.com/Fimeg/RedFlag/agent/internal/constants"
)
var (
processLoggerMu sync.Mutex
processLoggerFile *os.File
)
// ConfigureProcessLogger routes the standard library logger to the primary
// Windows agent log. Linux keeps its existing service/journal behavior.
func ConfigureProcessLogger() error {
if runtime.GOOS != "windows" {
return nil
}
return ConfigureProcessLoggerFile(constants.GetAgentLogPath(), os.Stderr)
}
// ConfigureProcessLoggerFile wires log.Printf/log.Fatal output to both the
// console stream and a durable file. This preserves CLI feedback while making
// Windows service diagnostics available without Event Viewer.
func ConfigureProcessLoggerFile(logPath string, console io.Writer) error {
if logPath == "" {
return fmt.Errorf("agent log path is empty")
}
if console == nil {
console = io.Discard
}
logDir := filepath.Dir(logPath)
if err := os.MkdirAll(logDir, 0750); err != nil {
return fmt.Errorf("failed to create agent log directory %q: %w", logDir, err)
}
file, err := os.OpenFile(logPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0640)
if err != nil {
return fmt.Errorf("failed to open agent log file %q: %w", logPath, err)
}
processLoggerMu.Lock()
defer processLoggerMu.Unlock()
oldFile := processLoggerFile
processLoggerFile = file
log.SetFlags(log.LstdFlags | log.LUTC)
log.SetOutput(io.MultiWriter(file, console))
if oldFile != nil {
_ = oldFile.Close()
}
log.Printf("[INFO] [agent] [logging] process_logger_initialized path=%s", logPath)
return nil
}