package log import ( "fmt" "os" "sync" "time" "dappco.re/go/core/proxy" ) // ShareLog writes append-only share result lines. // // sl := log.NewShareLog("/var/log/proxy-shares.log") // bus.Subscribe(proxy.EventAccept, sl.OnAccept) // bus.Subscribe(proxy.EventReject, sl.OnReject) type ShareLog struct { path string mu sync.Mutex file *os.File closed bool } // NewShareLog opens the file lazily on first write. // // sl := log.NewShareLog("/var/log/proxy-shares.log") func NewShareLog(path string) *ShareLog { return &ShareLog{path: path} } // OnAccept writes `2026-04-04T12:00:00Z ACCEPT WALLET diff=100000 latency=82ms`. // // sl.OnAccept(proxy.Event{Miner: miner, Diff: 100000}) func (l *ShareLog) OnAccept(event proxy.Event) { if event.Miner == nil { return } line := fmt.Sprintf("%s ACCEPT %s diff=%d latency=%dms\n", utcTimestamp(), event.Miner.User(), event.Diff, event.Latency, ) l.writeLine(line) } // OnReject writes `2026-04-04T12:00:00Z REJECT WALLET reason="Low difficulty share"`. // // sl.OnReject(proxy.Event{Miner: miner, Error: "Low difficulty share"}) func (l *ShareLog) OnReject(event proxy.Event) { if event.Miner == nil { return } line := fmt.Sprintf("%s REJECT %s reason=%q\n", utcTimestamp(), event.Miner.User(), event.Error, ) l.writeLine(line) } // Close releases the append-only file handle if it has been opened. // // sl.Close() func (l *ShareLog) Close() { if l == nil { return } l.mu.Lock() defer l.mu.Unlock() if l.closed { return } l.closed = true if l.file != nil { _ = l.file.Close() l.file = nil } } func (l *ShareLog) writeLine(line string) { if l == nil || l.path == "" { return } l.mu.Lock() defer l.mu.Unlock() if l.closed { return } if l.file == nil { file, errorValue := os.OpenFile(l.path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644) if errorValue != nil { return } l.file = file } _, _ = l.file.WriteString(line) } func utcTimestamp() string { return time.Now().UTC().Format(time.RFC3339) }