mirror of
https://github.com/lordmathis/llamactl.git
synced 2025-12-22 09:04:22 +00:00
Simplify logging config
This commit is contained in:
@@ -63,8 +63,8 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create logs directory
|
// Create logs directory
|
||||||
if err := os.MkdirAll(cfg.Instances.Logging.LogsDir, 0755); err != nil {
|
if err := os.MkdirAll(cfg.Instances.LogsDir, 0755); err != nil {
|
||||||
log.Printf("Error creating log directory %s: %v\nInstance logs will not be available.", cfg.Instances.Logging.LogsDir, err)
|
log.Printf("Error creating log directory %s: %v\nInstance logs will not be available.", cfg.Instances.LogsDir, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -88,21 +88,11 @@ type DatabaseConfig struct {
|
|||||||
ConnMaxLifetime time.Duration `yaml:"connection_max_lifetime" json:"connection_max_lifetime" swaggertype:"string" example:"1h"`
|
ConnMaxLifetime time.Duration `yaml:"connection_max_lifetime" json:"connection_max_lifetime" swaggertype:"string" example:"1h"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoggingConfig contains all logging-related configuration for instances
|
|
||||||
type LoggingConfig struct {
|
|
||||||
// Logs directory override (relative to data_dir if not absolute)
|
|
||||||
LogsDir string `yaml:"logs_dir" json:"logs_dir"`
|
|
||||||
|
|
||||||
// Log rotation configuration
|
|
||||||
LogRotation LogRotationConfig `yaml:"log_rotation" json:"log_rotation"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// LogRotationConfig contains log rotation settings for instances
|
// LogRotationConfig contains log rotation settings for instances
|
||||||
type LogRotationConfig struct {
|
type LogRotationConfig struct {
|
||||||
Enabled bool `yaml:"enabled" default:"true"`
|
Enabled bool `yaml:"enabled" default:"true"`
|
||||||
MaxSizeMB int `yaml:"max_size_mb" default:"100"` // MB
|
MaxSizeMB int `yaml:"max_size_mb" default:"100"` // MB
|
||||||
MaxBackups int `yaml:"max_backups" default:"3"`
|
Compress bool `yaml:"compress" default:"false"`
|
||||||
Compress bool `yaml:"compress" default:"false"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// InstancesConfig contains instance management configuration
|
// InstancesConfig contains instance management configuration
|
||||||
@@ -143,8 +133,17 @@ type InstancesConfig struct {
|
|||||||
// Interval for checking instance timeouts (in minutes)
|
// Interval for checking instance timeouts (in minutes)
|
||||||
TimeoutCheckInterval int `yaml:"timeout_check_interval" json:"timeout_check_interval"`
|
TimeoutCheckInterval int `yaml:"timeout_check_interval" json:"timeout_check_interval"`
|
||||||
|
|
||||||
// Logging configuration
|
// Logs directory override (relative to data_dir if not absolute)
|
||||||
Logging LoggingConfig `yaml:"logging" json:"logging"`
|
LogsDir string `yaml:"logs_dir" json:"logs_dir"`
|
||||||
|
|
||||||
|
// Log rotation enabled
|
||||||
|
LogRotationEnabled bool `yaml:"log_rotation_enabled" default:"true"`
|
||||||
|
|
||||||
|
// Maximum log file size in MB before rotation
|
||||||
|
LogRotationMaxSizeMB int `yaml:"log_rotation_max_size_mb" default:"100"`
|
||||||
|
|
||||||
|
// Whether to compress rotated log files
|
||||||
|
LogRotationCompress bool `yaml:"log_rotation_compress" default:"false"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// AuthConfig contains authentication settings
|
// AuthConfig contains authentication settings
|
||||||
@@ -235,15 +234,10 @@ func LoadConfig(configPath string) (AppConfig, error) {
|
|||||||
DefaultOnDemandStart: true,
|
DefaultOnDemandStart: true,
|
||||||
OnDemandStartTimeout: 120, // 2 minutes
|
OnDemandStartTimeout: 120, // 2 minutes
|
||||||
TimeoutCheckInterval: 5, // Check timeouts every 5 minutes
|
TimeoutCheckInterval: 5, // Check timeouts every 5 minutes
|
||||||
Logging: LoggingConfig{
|
LogsDir: "", // Will be set to data_dir/logs if empty
|
||||||
LogsDir: "", // Will be set to data_dir/logs if empty
|
LogRotationEnabled: true,
|
||||||
LogRotation: LogRotationConfig{
|
LogRotationMaxSizeMB: 100,
|
||||||
Enabled: true,
|
LogRotationCompress: false,
|
||||||
MaxSizeMB: 100,
|
|
||||||
MaxBackups: 3,
|
|
||||||
Compress: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
Database: DatabaseConfig{
|
Database: DatabaseConfig{
|
||||||
Path: "", // Will be set to data_dir/llamactl.db if empty
|
Path: "", // Will be set to data_dir/llamactl.db if empty
|
||||||
@@ -285,8 +279,8 @@ func LoadConfig(configPath string) (AppConfig, error) {
|
|||||||
// Log deprecation warning if using custom instances dir
|
// Log deprecation warning if using custom instances dir
|
||||||
log.Println("⚠️ Instances directory is deprecated and will be removed in future versions. Instances are persisted in the database.")
|
log.Println("⚠️ Instances directory is deprecated and will be removed in future versions. Instances are persisted in the database.")
|
||||||
}
|
}
|
||||||
if cfg.Instances.Logging.LogsDir == "" {
|
if cfg.Instances.LogsDir == "" {
|
||||||
cfg.Instances.Logging.LogsDir = filepath.Join(cfg.DataDir, "logs")
|
cfg.Instances.LogsDir = filepath.Join(cfg.DataDir, "logs")
|
||||||
}
|
}
|
||||||
if cfg.Database.Path == "" {
|
if cfg.Database.Path == "" {
|
||||||
cfg.Database.Path = filepath.Join(cfg.DataDir, "llamactl.db")
|
cfg.Database.Path = filepath.Join(cfg.DataDir, "llamactl.db")
|
||||||
@@ -353,7 +347,7 @@ func loadEnvVars(cfg *AppConfig) {
|
|||||||
cfg.Instances.InstancesDir = instancesDir
|
cfg.Instances.InstancesDir = instancesDir
|
||||||
}
|
}
|
||||||
if logsDir := os.Getenv("LLAMACTL_LOGS_DIR"); logsDir != "" {
|
if logsDir := os.Getenv("LLAMACTL_LOGS_DIR"); logsDir != "" {
|
||||||
cfg.Instances.Logging.LogsDir = logsDir
|
cfg.Instances.LogsDir = logsDir
|
||||||
}
|
}
|
||||||
if autoCreate := os.Getenv("LLAMACTL_AUTO_CREATE_DATA_DIR"); autoCreate != "" {
|
if autoCreate := os.Getenv("LLAMACTL_AUTO_CREATE_DATA_DIR"); autoCreate != "" {
|
||||||
if b, err := strconv.ParseBool(autoCreate); err == nil {
|
if b, err := strconv.ParseBool(autoCreate); err == nil {
|
||||||
@@ -574,6 +568,23 @@ func loadEnvVars(cfg *AppConfig) {
|
|||||||
cfg.Database.ConnMaxLifetime = d
|
cfg.Database.ConnMaxLifetime = d
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Log rotation config
|
||||||
|
if logRotationEnabled := os.Getenv("LLAMACTL_LOG_ROTATION_ENABLED"); logRotationEnabled != "" {
|
||||||
|
if b, err := strconv.ParseBool(logRotationEnabled); err == nil {
|
||||||
|
cfg.Instances.LogRotationEnabled = b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if logRotationMaxSizeMB := os.Getenv("LLAMACTL_LOG_ROTATION_MAX_SIZE_MB"); logRotationMaxSizeMB != "" {
|
||||||
|
if m, err := strconv.Atoi(logRotationMaxSizeMB); err == nil {
|
||||||
|
cfg.Instances.LogRotationMaxSizeMB = m
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if logRotationCompress := os.Getenv("LLAMACTL_LOG_ROTATION_COMPRESS"); logRotationCompress != "" {
|
||||||
|
if b, err := strconv.ParseBool(logRotationCompress); err == nil {
|
||||||
|
cfg.Instances.LogRotationCompress = b
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParsePortRange parses port range from string formats like "8000-9000" or "8000,9000"
|
// ParsePortRange parses port range from string formats like "8000-9000" or "8000,9000"
|
||||||
|
|||||||
@@ -44,8 +44,8 @@ func TestLoadConfig_Defaults(t *testing.T) {
|
|||||||
if cfg.Instances.InstancesDir != filepath.Join(homedir, ".local", "share", "llamactl", "instances") {
|
if cfg.Instances.InstancesDir != filepath.Join(homedir, ".local", "share", "llamactl", "instances") {
|
||||||
t.Errorf("Expected default instances directory '%s', got %q", filepath.Join(homedir, ".local", "share", "llamactl", "instances"), cfg.Instances.InstancesDir)
|
t.Errorf("Expected default instances directory '%s', got %q", filepath.Join(homedir, ".local", "share", "llamactl", "instances"), cfg.Instances.InstancesDir)
|
||||||
}
|
}
|
||||||
if cfg.Instances.Logging.LogsDir != filepath.Join(homedir, ".local", "share", "llamactl", "logs") {
|
if cfg.Instances.LogsDir != filepath.Join(homedir, ".local", "share", "llamactl", "logs") {
|
||||||
t.Errorf("Expected default logs directory '%s', got %q", filepath.Join(homedir, ".local", "share", "llamactl", "logs"), cfg.Instances.Logging.LogsDir)
|
t.Errorf("Expected default logs directory '%s', got %q", filepath.Join(homedir, ".local", "share", "llamactl", "logs"), cfg.Instances.LogsDir)
|
||||||
}
|
}
|
||||||
if !cfg.Instances.AutoCreateDirs {
|
if !cfg.Instances.AutoCreateDirs {
|
||||||
t.Error("Expected default instances auto-create to be true")
|
t.Error("Expected default instances auto-create to be true")
|
||||||
@@ -79,8 +79,7 @@ server:
|
|||||||
instances:
|
instances:
|
||||||
port_range: [7000, 8000]
|
port_range: [7000, 8000]
|
||||||
max_instances: 5
|
max_instances: 5
|
||||||
logging:
|
logs_dir: "/custom/logs"
|
||||||
logs_dir: "/custom/logs"
|
|
||||||
llama_executable: "/usr/bin/llama-server"
|
llama_executable: "/usr/bin/llama-server"
|
||||||
default_auto_restart: false
|
default_auto_restart: false
|
||||||
default_max_restarts: 10
|
default_max_restarts: 10
|
||||||
@@ -107,8 +106,8 @@ instances:
|
|||||||
if cfg.Instances.PortRange != [2]int{7000, 8000} {
|
if cfg.Instances.PortRange != [2]int{7000, 8000} {
|
||||||
t.Errorf("Expected port range [7000, 8000], got %v", cfg.Instances.PortRange)
|
t.Errorf("Expected port range [7000, 8000], got %v", cfg.Instances.PortRange)
|
||||||
}
|
}
|
||||||
if cfg.Instances.Logging.LogsDir != "/custom/logs" {
|
if cfg.Instances.LogsDir != "/custom/logs" {
|
||||||
t.Errorf("Expected logs directory '/custom/logs', got %q", cfg.Instances.Logging.LogsDir)
|
t.Errorf("Expected logs directory '/custom/logs', got %q", cfg.Instances.LogsDir)
|
||||||
}
|
}
|
||||||
if cfg.Instances.MaxInstances != 5 {
|
if cfg.Instances.MaxInstances != 5 {
|
||||||
t.Errorf("Expected max instances 5, got %d", cfg.Instances.MaxInstances)
|
t.Errorf("Expected max instances 5, got %d", cfg.Instances.MaxInstances)
|
||||||
@@ -158,8 +157,8 @@ func TestLoadConfig_EnvironmentOverrides(t *testing.T) {
|
|||||||
if cfg.Instances.PortRange != [2]int{5000, 6000} {
|
if cfg.Instances.PortRange != [2]int{5000, 6000} {
|
||||||
t.Errorf("Expected port range [5000, 6000], got %v", cfg.Instances.PortRange)
|
t.Errorf("Expected port range [5000, 6000], got %v", cfg.Instances.PortRange)
|
||||||
}
|
}
|
||||||
if cfg.Instances.Logging.LogsDir != "/env/logs" {
|
if cfg.Instances.LogsDir != "/env/logs" {
|
||||||
t.Errorf("Expected logs directory '/env/logs', got %q", cfg.Instances.Logging.LogsDir)
|
t.Errorf("Expected logs directory '/env/logs', got %q", cfg.Instances.LogsDir)
|
||||||
}
|
}
|
||||||
if cfg.Instances.MaxInstances != 20 {
|
if cfg.Instances.MaxInstances != 20 {
|
||||||
t.Errorf("Expected max instances 20, got %d", cfg.Instances.MaxInstances)
|
t.Errorf("Expected max instances 20, got %d", cfg.Instances.MaxInstances)
|
||||||
|
|||||||
@@ -68,10 +68,15 @@ func New(name string, globalConfig *config.AppConfig, opts *Options, onStatusCha
|
|||||||
|
|
||||||
// Only create logger, proxy, and process for local instances
|
// Only create logger, proxy, and process for local instances
|
||||||
if !instance.IsRemote() {
|
if !instance.IsRemote() {
|
||||||
|
logRotationConfig := &config.LogRotationConfig{
|
||||||
|
Enabled: globalInstanceSettings.LogRotationEnabled,
|
||||||
|
MaxSizeMB: globalInstanceSettings.LogRotationMaxSizeMB,
|
||||||
|
Compress: globalInstanceSettings.LogRotationCompress,
|
||||||
|
}
|
||||||
instance.logger = newLogger(
|
instance.logger = newLogger(
|
||||||
name,
|
name,
|
||||||
globalInstanceSettings.Logging.LogsDir,
|
globalInstanceSettings.LogsDir,
|
||||||
&globalInstanceSettings.Logging.LogRotation,
|
logRotationConfig,
|
||||||
)
|
)
|
||||||
instance.process = newProcess(instance)
|
instance.process = newProcess(instance)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,10 +27,8 @@ func TestNewInstance(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
Instances: config.InstancesConfig{
|
Instances: config.InstancesConfig{
|
||||||
DefaultAutoRestart: true,
|
DefaultAutoRestart: true,
|
||||||
Logging: config.LoggingConfig{
|
LogsDir: "/tmp/test",
|
||||||
LogsDir: "/tmp/test",
|
|
||||||
},
|
|
||||||
DefaultMaxRestarts: 3,
|
DefaultMaxRestarts: 3,
|
||||||
DefaultRestartDelay: 5,
|
DefaultRestartDelay: 5,
|
||||||
},
|
},
|
||||||
@@ -122,10 +120,8 @@ func TestSetOptions(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
Instances: config.InstancesConfig{
|
Instances: config.InstancesConfig{
|
||||||
DefaultAutoRestart: true,
|
DefaultAutoRestart: true,
|
||||||
Logging: config.LoggingConfig{
|
LogsDir: "/tmp/test",
|
||||||
LogsDir: "/tmp/test",
|
|
||||||
},
|
|
||||||
DefaultMaxRestarts: 3,
|
DefaultMaxRestarts: 3,
|
||||||
DefaultRestartDelay: 5,
|
DefaultRestartDelay: 5,
|
||||||
},
|
},
|
||||||
@@ -180,7 +176,7 @@ func TestMarshalJSON(t *testing.T) {
|
|||||||
Backends: config.BackendConfig{
|
Backends: config.BackendConfig{
|
||||||
LlamaCpp: config.BackendSettings{Command: "llama-server"},
|
LlamaCpp: config.BackendSettings{Command: "llama-server"},
|
||||||
},
|
},
|
||||||
Instances: config.InstancesConfig{Logging: config.LoggingConfig{LogsDir: "/tmp/test"}},
|
Instances: config.InstancesConfig{LogsDir: "/tmp/test"},
|
||||||
Nodes: map[string]config.NodeConfig{},
|
Nodes: map[string]config.NodeConfig{},
|
||||||
LocalNode: "main",
|
LocalNode: "main",
|
||||||
}
|
}
|
||||||
@@ -317,9 +313,7 @@ func TestCreateOptionsValidation(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
Instances: config.InstancesConfig{
|
Instances: config.InstancesConfig{
|
||||||
Logging: config.LoggingConfig{
|
LogsDir: "/tmp/test",
|
||||||
LogsDir: "/tmp/test",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
Nodes: map[string]config.NodeConfig{},
|
Nodes: map[string]config.NodeConfig{},
|
||||||
LocalNode: "main",
|
LocalNode: "main",
|
||||||
@@ -364,7 +358,7 @@ func TestStatusChangeCallback(t *testing.T) {
|
|||||||
Backends: config.BackendConfig{
|
Backends: config.BackendConfig{
|
||||||
LlamaCpp: config.BackendSettings{Command: "llama-server"},
|
LlamaCpp: config.BackendSettings{Command: "llama-server"},
|
||||||
},
|
},
|
||||||
Instances: config.InstancesConfig{Logging: config.LoggingConfig{LogsDir: "/tmp/test"}},
|
Instances: config.InstancesConfig{LogsDir: "/tmp/test"},
|
||||||
Nodes: map[string]config.NodeConfig{},
|
Nodes: map[string]config.NodeConfig{},
|
||||||
LocalNode: "main",
|
LocalNode: "main",
|
||||||
}
|
}
|
||||||
@@ -406,7 +400,7 @@ func TestSetOptions_NodesPreserved(t *testing.T) {
|
|||||||
Backends: config.BackendConfig{
|
Backends: config.BackendConfig{
|
||||||
LlamaCpp: config.BackendSettings{Command: "llama-server"},
|
LlamaCpp: config.BackendSettings{Command: "llama-server"},
|
||||||
},
|
},
|
||||||
Instances: config.InstancesConfig{Logging: config.LoggingConfig{LogsDir: "/tmp/test"}},
|
Instances: config.InstancesConfig{LogsDir: "/tmp/test"},
|
||||||
Nodes: map[string]config.NodeConfig{},
|
Nodes: map[string]config.NodeConfig{},
|
||||||
LocalNode: "main",
|
LocalNode: "main",
|
||||||
}
|
}
|
||||||
@@ -488,7 +482,7 @@ func TestProcessErrorCases(t *testing.T) {
|
|||||||
Backends: config.BackendConfig{
|
Backends: config.BackendConfig{
|
||||||
LlamaCpp: config.BackendSettings{Command: "llama-server"},
|
LlamaCpp: config.BackendSettings{Command: "llama-server"},
|
||||||
},
|
},
|
||||||
Instances: config.InstancesConfig{Logging: config.LoggingConfig{LogsDir: "/tmp/test"}},
|
Instances: config.InstancesConfig{LogsDir: "/tmp/test"},
|
||||||
Nodes: map[string]config.NodeConfig{},
|
Nodes: map[string]config.NodeConfig{},
|
||||||
LocalNode: "main",
|
LocalNode: "main",
|
||||||
}
|
}
|
||||||
@@ -524,7 +518,7 @@ func TestRemoteInstanceOperations(t *testing.T) {
|
|||||||
Backends: config.BackendConfig{
|
Backends: config.BackendConfig{
|
||||||
LlamaCpp: config.BackendSettings{Command: "llama-server"},
|
LlamaCpp: config.BackendSettings{Command: "llama-server"},
|
||||||
},
|
},
|
||||||
Instances: config.InstancesConfig{Logging: config.LoggingConfig{LogsDir: "/tmp/test"}},
|
Instances: config.InstancesConfig{LogsDir: "/tmp/test"},
|
||||||
Nodes: map[string]config.NodeConfig{
|
Nodes: map[string]config.NodeConfig{
|
||||||
"remote-node": {Address: "http://remote-node:8080"},
|
"remote-node": {Address: "http://remote-node:8080"},
|
||||||
},
|
},
|
||||||
@@ -572,7 +566,7 @@ func TestIdleTimeout(t *testing.T) {
|
|||||||
Backends: config.BackendConfig{
|
Backends: config.BackendConfig{
|
||||||
LlamaCpp: config.BackendSettings{Command: "llama-server"},
|
LlamaCpp: config.BackendSettings{Command: "llama-server"},
|
||||||
},
|
},
|
||||||
Instances: config.InstancesConfig{Logging: config.LoggingConfig{LogsDir: "/tmp/test"}},
|
Instances: config.InstancesConfig{LogsDir: "/tmp/test"},
|
||||||
Nodes: map[string]config.NodeConfig{},
|
Nodes: map[string]config.NodeConfig{},
|
||||||
LocalNode: "main",
|
LocalNode: "main",
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ func (l *logger) create() error {
|
|||||||
t := &timber.Logger{
|
t := &timber.Logger{
|
||||||
Filename: logPath,
|
Filename: logPath,
|
||||||
MaxSize: l.cfg.MaxSizeMB,
|
MaxSize: l.cfg.MaxSizeMB,
|
||||||
MaxBackups: l.cfg.MaxBackups,
|
MaxBackups: 0, // No limit on backups - use index-based naming
|
||||||
// Compression: "gzip" if Compress is true, else "none"
|
// Compression: "gzip" if Compress is true, else "none"
|
||||||
Compression: func() string {
|
Compression: func() string {
|
||||||
if l.cfg.Compress {
|
if l.cfg.Compress {
|
||||||
@@ -57,8 +57,8 @@ func (l *logger) create() error {
|
|||||||
}
|
}
|
||||||
return "none"
|
return "none"
|
||||||
}(),
|
}(),
|
||||||
FileMode: 0644, // default; timberjack uses 640 if 0
|
FileMode: 0644, // default; timberjack uses 640 if 0
|
||||||
LocalTime: true, // use local time for consistency with lumberjack
|
LocalTime: false, // Use index-based naming instead of timestamps
|
||||||
}
|
}
|
||||||
|
|
||||||
// If rotation is disabled, set MaxSize to 0 so no rotation occurs
|
// If rotation is disabled, set MaxSize to 0 so no rotation occurs
|
||||||
|
|||||||
@@ -201,15 +201,13 @@ func createTestAppConfig(instancesDir string) *config.AppConfig {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
Instances: config.InstancesConfig{
|
Instances: config.InstancesConfig{
|
||||||
PortRange: [2]int{8000, 9000},
|
PortRange: [2]int{8000, 9000},
|
||||||
InstancesDir: instancesDir,
|
InstancesDir: instancesDir,
|
||||||
MaxInstances: 10,
|
MaxInstances: 10,
|
||||||
MaxRunningInstances: 10,
|
MaxRunningInstances: 10,
|
||||||
DefaultAutoRestart: true,
|
DefaultAutoRestart: true,
|
||||||
DefaultMaxRestarts: 3,
|
DefaultMaxRestarts: 3,
|
||||||
Logging: config.LoggingConfig{
|
LogsDir: instancesDir,
|
||||||
LogsDir: instancesDir,
|
|
||||||
},
|
|
||||||
DefaultRestartDelay: 5,
|
DefaultRestartDelay: 5,
|
||||||
TimeoutCheckInterval: 5,
|
TimeoutCheckInterval: 5,
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user