diff --git a/pkg/database/database.go b/pkg/database/database.go index a095bf5..4b0a2ed 100644 --- a/pkg/database/database.go +++ b/pkg/database/database.go @@ -40,17 +40,14 @@ var ( databaseMutex sync.RWMutex ) -// Config holds database configuration options +// database.Config{Enabled: true, Path: "/data/mining.db", RetentionDays: 30} type Config struct { - // Enabled determines if database persistence is active - Enabled bool `json:"enabled"` - // Path is the database file path (optional, uses default if empty) - Path string `json:"path,omitempty"` - // RetentionDays is how long to keep historical data (default 30) - RetentionDays int `json:"retentionDays,omitempty"` + Enabled bool `json:"enabled"` + Path string `json:"path,omitempty"` + RetentionDays int `json:"retentionDays,omitempty"` } -// defaultConfig returns the default database configuration +// configuration := defaultConfig() // Config{Enabled: true, RetentionDays: 30} func defaultConfig() Config { return Config{ Enabled: true, @@ -59,7 +56,7 @@ func defaultConfig() Config { } } -// defaultDBPath returns the default database file path +// path, err := defaultDBPath() // "~/.local/share/lethean-desktop/mining.db" func defaultDBPath() (string, error) { dataDir := filepath.Join(xdg.DataHome, "lethean-desktop") if err := os.MkdirAll(dataDir, 0755); err != nil { @@ -110,7 +107,7 @@ func Initialize(config Config) error { return nil } -// Close closes the database connection +// if err := database.Close(); err != nil { logging.Warn("close failed", ...) } func Close() error { databaseMutex.Lock() defer databaseMutex.Unlock() @@ -132,7 +129,7 @@ func isInitialized() bool { return globalDatabase != nil } -// createTables creates all required database tables +// if err := createTables(); err != nil { return databaseError("create tables", err) } func createTables() error { schema := ` -- Hashrate history table for storing miner performance data @@ -175,7 +172,7 @@ func createTables() error { return err } -// Cleanup removes old data based on retention settings +// database.Cleanup(30) // remove hashrate rows older than 30 days func Cleanup(retentionDays int) error { databaseMutex.RLock() defer databaseMutex.RUnlock() @@ -194,7 +191,7 @@ func Cleanup(retentionDays int) error { return err } -// vacuumDB optimizes the database file size +// if err := vacuumDB(); err != nil { logging.Warn("vacuum failed", ...) } func vacuumDB() error { databaseMutex.RLock() defer databaseMutex.RUnlock() diff --git a/pkg/database/hashrate.go b/pkg/database/hashrate.go index 6ae6058..a12ec8d 100644 --- a/pkg/database/hashrate.go +++ b/pkg/database/hashrate.go @@ -7,8 +7,8 @@ import ( "forge.lthn.ai/Snider/Mining/pkg/logging" ) -// parseSQLiteTimestamp parses timestamp strings from SQLite which may use various formats. -// Logs a warning if parsing fails and returns zero time. +// t := parseSQLiteTimestamp("2006-01-02 15:04:05") // time.Time{...} +// t := parseSQLiteTimestamp("") // time.Time{} (zero) func parseSQLiteTimestamp(s string) time.Time { if s == "" { return time.Time{} @@ -33,7 +33,8 @@ func parseSQLiteTimestamp(s string) time.Time { return time.Time{} } -// Resolution indicates the data resolution type +// database.ResolutionHigh // "high" — 10-second intervals +// database.ResolutionLow // "low" — 1-minute averages type Resolution string const ( @@ -41,13 +42,13 @@ const ( ResolutionLow Resolution = "low" // 1-minute averages ) -// HashratePoint represents a single hashrate measurement +// point := database.HashratePoint{Timestamp: time.Now(), Hashrate: 1234} type HashratePoint struct { Timestamp time.Time `json:"timestamp"` Hashrate int `json:"hashrate"` } -// dbInsertTimeout is the maximum time to wait for a database insert operation +// ctx, cancel := context.WithTimeout(ctx, dbInsertTimeout) // 5s ceiling for INSERT const dbInsertTimeout = 5 * time.Second // InsertHashratePoint stores a hashrate measurement in the database. diff --git a/pkg/database/interface.go b/pkg/database/interface.go index c709b3d..2606d3e 100644 --- a/pkg/database/interface.go +++ b/pkg/database/interface.go @@ -5,8 +5,9 @@ import ( "time" ) -// HashrateStore defines the interface for hashrate data persistence. -// This interface allows for dependency injection and easier testing. +// var store database.HashrateStore = database.DefaultStore() +// store.InsertHashratePoint(ctx, "xmrig", "xmrig", point, database.ResolutionHigh) +// store.Cleanup(30) type HashrateStore interface { // store.InsertHashratePoint(ctx, "xmrig", "xmrig", HashratePoint{Timestamp: time.Now(), Hashrate: 1234}, ResolutionHigh) InsertHashratePoint(ctx context.Context, minerName, minerType string, point HashratePoint, resolution Resolution) error @@ -29,12 +30,11 @@ type HashrateStore interface { Close() error } -// defaultStore implements HashrateStore using the global database connection. -// This provides backward compatibility while allowing interface-based usage. +// store := database.DefaultStore() // wraps the global database connection type defaultStore struct{} -// DefaultStore returns a HashrateStore that uses the global database connection. -// This is useful for gradual migration from package-level functions to interface-based usage. +// store := database.DefaultStore() +// store.GetHashrateStats("xmrig") func DefaultStore() HashrateStore { return &defaultStore{} } @@ -63,8 +63,7 @@ func (s *defaultStore) Close() error { return Close() } -// NopStore returns a HashrateStore that does nothing. -// Useful for testing or when database is disabled. +// store := database.NopStore() // all methods are no-ops; use when database is disabled func NopStore() HashrateStore { return &nopStore{} } diff --git a/pkg/logging/logger.go b/pkg/logging/logger.go index 894e6a2..57eec7d 100644 --- a/pkg/logging/logger.go +++ b/pkg/logging/logger.go @@ -10,21 +10,18 @@ import ( "time" ) -// Level represents the severity of a log message. +// if level >= logging.LevelWarn { alertOps() } type Level int const ( - // LevelDebug is the most verbose log level. - LevelDebug Level = iota - // LevelInfo is for general informational messages. - LevelInfo - // LevelWarn is for warning messages. - LevelWarn - // LevelError is for error messages. - LevelError + LevelDebug Level = iota // logger.SetLevel(logging.LevelDebug) + LevelInfo // logger.SetLevel(logging.LevelInfo) + LevelWarn // logger.SetLevel(logging.LevelWarn) + LevelError // logger.SetLevel(logging.LevelError) ) -// String returns the string representation of the log level. +// logging.LevelDebug.String() // "DEBUG" +// logging.LevelError.String() // "ERROR" func (l Level) String() string { switch l { case LevelDebug: @@ -40,7 +37,8 @@ func (l Level) String() string { } } -// Logger provides structured logging with configurable output and level. +// logger := logging.New(logging.Config{Level: logging.LevelDebug, Component: "mining"}) +// logger.Info("started", logging.Fields{"miner": "xmrig"}) type Logger struct { mutex sync.Mutex output io.Writer @@ -48,14 +46,14 @@ type Logger struct { component string } -// Config holds configuration for creating a new Logger. +// logging.Config{Output: os.Stderr, Level: logging.LevelInfo, Component: "mining"} type Config struct { Output io.Writer Level Level Component string } -// DefaultConfig returns the default logger configuration. +// config := logging.DefaultConfig() // Output: os.Stderr, Level: LevelInfo func DefaultConfig() Config { return Config{ Output: os.Stderr, @@ -66,14 +64,14 @@ func DefaultConfig() Config { // New creates a Logger with the given configuration. // logger := logging.New(logging.Config{Output: os.Stderr, Level: logging.LevelInfo, Component: "mining"}) -func New(cfg Config) *Logger { - if cfg.Output == nil { - cfg.Output = os.Stderr +func New(config Config) *Logger { + if config.Output == nil { + config.Output = os.Stderr } return &Logger{ - output: cfg.Output, - level: cfg.Level, - component: cfg.Component, + output: config.Output, + level: config.Level, + component: config.Component, } } @@ -105,7 +103,7 @@ func (l *Logger) GetLevel() Level { return l.level } -// Fields represents key-value pairs for structured logging. +// logger.Info("started", logging.Fields{"miner": "xmrig", "pool": "pool.lthn.io"}) type Fields map[string]interface{} // log writes a log message at the specified level. @@ -149,47 +147,47 @@ func (l *Logger) log(level Level, msg string, fields Fields) { fmt.Fprint(l.output, builder.String()) } -// Debug logs a debug message. +// logger.Debug("hashrate collected", logging.Fields{"rate": 1234}) func (l *Logger) Debug(msg string, fields ...Fields) { l.log(LevelDebug, msg, mergeFields(fields)) } -// Info logs an informational message. +// logger.Info("miner started", logging.Fields{"miner": "xmrig"}) func (l *Logger) Info(msg string, fields ...Fields) { l.log(LevelInfo, msg, mergeFields(fields)) } -// Warn logs a warning message. +// logger.Warn("hashrate drop", logging.Fields{"current": 500, "min": 1000}) func (l *Logger) Warn(msg string, fields ...Fields) { l.log(LevelWarn, msg, mergeFields(fields)) } -// Error logs an error message. +// logger.Error("miner crashed", logging.Fields{"code": -1, "miner": "xmrig"}) func (l *Logger) Error(msg string, fields ...Fields) { l.log(LevelError, msg, mergeFields(fields)) } -// Debugf logs a formatted debug message. +// logger.Debugf("collected %d hashrate points for %s", len(points), minerName) func (l *Logger) Debugf(format string, args ...interface{}) { l.log(LevelDebug, fmt.Sprintf(format, args...), nil) } -// Infof logs a formatted informational message. +// logger.Infof("miner %s started on pool %s", minerName, poolURL) func (l *Logger) Infof(format string, args ...interface{}) { l.log(LevelInfo, fmt.Sprintf(format, args...), nil) } -// Warnf logs a formatted warning message. +// logger.Warnf("hashrate %d H/s below minimum %d H/s", current, minimum) func (l *Logger) Warnf(format string, args ...interface{}) { l.log(LevelWarn, fmt.Sprintf(format, args...), nil) } -// Errorf logs a formatted error message. +// logger.Errorf("failed to connect to pool %s: %v", poolURL, err) func (l *Logger) Errorf(format string, args ...interface{}) { l.log(LevelError, fmt.Sprintf(format, args...), nil) } -// mergeFields combines multiple Fields maps into one. +// combined := mergeFields([]Fields{{"a": 1}, {"b": 2}}) // Fields{"a": 1, "b": 2} func mergeFields(fields []Fields) Fields { if len(fields) == 0 { return nil @@ -210,21 +208,22 @@ var ( globalMutex sync.RWMutex ) -// SetGlobal sets the global logger instance. +// logging.SetGlobal(logging.New(logging.Config{Level: logging.LevelDebug})) func SetGlobal(l *Logger) { globalMutex.Lock() defer globalMutex.Unlock() globalLogger = l } -// GetGlobal returns the global logger instance. +// logger := logging.GetGlobal() +// logger.Info("using global logger") func GetGlobal() *Logger { globalMutex.RLock() defer globalMutex.RUnlock() return globalLogger } -// SetGlobalLevel sets the log level of the global logger. +// logging.SetGlobalLevel(logging.LevelDebug) // enable debug logging globally func SetGlobalLevel(level Level) { globalMutex.RLock() defer globalMutex.RUnlock() @@ -273,7 +272,8 @@ func Errorf(format string, args ...interface{}) { GetGlobal().Errorf(format, args...) } -// ParseLevel parses a string into a log level. +// level, err := logging.ParseLevel("DEBUG") // LevelDebug, nil +// level, err := logging.ParseLevel("nope") // LevelInfo, error func ParseLevel(s string) (Level, error) { switch strings.ToUpper(s) { case "DEBUG": diff --git a/pkg/mining/syslog_windows.go b/pkg/mining/syslog_windows.go index d335b37..3fe59b9 100644 --- a/pkg/mining/syslog_windows.go +++ b/pkg/mining/syslog_windows.go @@ -6,10 +6,7 @@ import ( "forge.lthn.ai/Snider/Mining/pkg/logging" ) -// On Windows, syslog is not available. We'll use a dummy implementation -// that logs to the standard logger. - -// logToSyslog logs a message to the standard logger, mimicking the syslog function's signature. +// logToSyslog("miner started: xmrig") // falls back to logging.Info on Windows func logToSyslog(message string) { logging.Info(message) }