Add optional "log_file" configuration option.
This commit is contained in:
@@ -17,6 +17,9 @@ type Config struct {
|
|||||||
// DataDir is the base directory for all database files.
|
// DataDir is the base directory for all database files.
|
||||||
DataDir string `json:"data_dir"`
|
DataDir string `json:"data_dir"`
|
||||||
|
|
||||||
|
// LogFile is the optional path to a file where logs will be written.
|
||||||
|
LogFile string `json:"log_file"`
|
||||||
|
|
||||||
// HotRetentionHours is how many hours of raw ticks to keep in the hot DB.
|
// HotRetentionHours is how many hours of raw ticks to keep in the hot DB.
|
||||||
HotRetentionHours int `json:"hot_retention_hours"`
|
HotRetentionHours int `json:"hot_retention_hours"`
|
||||||
|
|
||||||
@@ -42,6 +45,7 @@ func DefaultConfig() Config {
|
|||||||
WebSocketURL: "wss://stream.bybit.com/v5/public/linear",
|
WebSocketURL: "wss://stream.bybit.com/v5/public/linear",
|
||||||
Symbol: "BTCUSDT",
|
Symbol: "BTCUSDT",
|
||||||
DataDir: "data",
|
DataDir: "data",
|
||||||
|
LogFile: "engine.log",
|
||||||
HotRetentionHours: 12,
|
HotRetentionHours: 12,
|
||||||
FeatureRetentionDays: 30,
|
FeatureRetentionDays: 30,
|
||||||
WriterFlushIntervalMs: 500,
|
WriterFlushIntervalMs: 500,
|
||||||
@@ -52,14 +56,12 @@ func DefaultConfig() Config {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// LoadConfig reads a JSON config file and merges with defaults.
|
// LoadConfig reads a JSON config file and merges with defaults.
|
||||||
// If the file does not exist, defaults are returned and a config file is written.
|
|
||||||
func LoadConfig(path string) (Config, error) {
|
func LoadConfig(path string) (Config, error) {
|
||||||
cfg := DefaultConfig()
|
cfg := DefaultConfig()
|
||||||
|
|
||||||
data, err := os.ReadFile(path)
|
data, err := os.ReadFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
// Write default config for user reference
|
|
||||||
if writeErr := writeDefaultConfig(path, cfg); writeErr != nil {
|
if writeErr := writeDefaultConfig(path, cfg); writeErr != nil {
|
||||||
return cfg, fmt.Errorf("failed to write default config: %w", writeErr)
|
return cfg, fmt.Errorf("failed to write default config: %w", writeErr)
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -2,10 +2,11 @@
|
|||||||
"websocket_url": "wss://stream.bybit.com/v5/public/linear",
|
"websocket_url": "wss://stream.bybit.com/v5/public/linear",
|
||||||
"symbol": "BTCUSDT",
|
"symbol": "BTCUSDT",
|
||||||
"data_dir": "data",
|
"data_dir": "data",
|
||||||
|
"log_file": "engine.log",
|
||||||
"hot_retention_hours": 12,
|
"hot_retention_hours": 12,
|
||||||
"feature_retention_days": 30,
|
"feature_retention_days": 30,
|
||||||
"writer_flush_interval_ms": 500,
|
"writer_flush_interval_ms": 500,
|
||||||
"writer_batch_size": 100,
|
"writer_batch_size": 100,
|
||||||
"tick_channel_buffer": 10000,
|
"tick_channel_buffer": 10000,
|
||||||
"maintenance_interval_minutes": 60
|
"maintenance_interval_minutes": 60
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if isDaemon {
|
if isDaemon {
|
||||||
startDaemon()
|
startDaemon(cfg.LogFile)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
runEngine(cfg)
|
runEngine(cfg)
|
||||||
@@ -78,13 +78,17 @@ func printUsage() {
|
|||||||
fmt.Println("Usage: engine [command] [--daemon]")
|
fmt.Println("Usage: engine [command] [--daemon]")
|
||||||
fmt.Println("\nCommands:")
|
fmt.Println("\nCommands:")
|
||||||
fmt.Println(" run Start the WebSocket ingestor and processing engine")
|
fmt.Println(" run Start the WebSocket ingestor and processing engine")
|
||||||
fmt.Println(" Use --daemon to run in background and log to engine.log")
|
fmt.Println(" Use --daemon to run in background and log to the file defined in config")
|
||||||
fmt.Println(" recover Run startup recovery to migrate stale ticks")
|
fmt.Println(" recover Run startup recovery to migrate stale ticks")
|
||||||
fmt.Println(" maintain Run a single maintenance cycle (cleanup/retention)")
|
fmt.Println(" maintain Run a single maintenance cycle (cleanup/retention)")
|
||||||
fmt.Println(" help Show this help message")
|
fmt.Println(" help Show this help message")
|
||||||
}
|
}
|
||||||
|
|
||||||
func startDaemon() {
|
func startDaemon(logPath string) {
|
||||||
|
if logPath == "" {
|
||||||
|
logPath = "engine.log"
|
||||||
|
}
|
||||||
|
|
||||||
// Prepare arguments for the child process (remove --daemon)
|
// Prepare arguments for the child process (remove --daemon)
|
||||||
args := []string{}
|
args := []string{}
|
||||||
for _, arg := range os.Args[1:] {
|
for _, arg := range os.Args[1:] {
|
||||||
@@ -93,24 +97,24 @@ func startDaemon() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare the command
|
// Prepare the command to re-run the current binary
|
||||||
cmd := exec.Command(os.Args[0], args...)
|
cmd := exec.Command(os.Args[0], args...)
|
||||||
|
|
||||||
// Open log file for output redirection
|
// Open the configured log file for output redirection
|
||||||
logFile, err := os.OpenFile("engine.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
logFile, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to open log file: %v", err)
|
log.Fatalf("Failed to open log file %s: %v", logPath, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.Stdout = logFile
|
cmd.Stdout = logFile
|
||||||
cmd.Stderr = logFile
|
cmd.Stderr = logFile
|
||||||
|
|
||||||
// Start the process
|
// Start the process in the background
|
||||||
if err := cmd.Start(); err != nil {
|
if err := cmd.Start(); err != nil {
|
||||||
log.Fatalf("Failed to start daemon: %v", err)
|
log.Fatalf("Failed to start daemon: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("Engine started in background (PID: %d). Logs: engine.log\n", cmd.Process.Pid)
|
fmt.Printf("Engine started in background (PID: %d). Logs: %s\n", cmd.Process.Pid, logPath)
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user