package log import ( "fmt" "os" "sync" "time" "dappco.re/go/core/proxy" ) // ShareLog writes share result lines to an append-only text file. // // Line format (accept): 2026-04-04T12:00:00Z ACCEPT diff= latency=ms // Line format (reject): 2026-04-04T12:00:00Z REJECT reason="" // // 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 // f is opened append-only on first write; nil until first event. // Uses core.File for I/O abstraction. f *os.File closed bool } // NewShareLog stores the destination path and lazily opens it on first write. // // sl := log.NewShareLog("/var/log/proxy-shares.log") func NewShareLog(path string) *ShareLog { return &ShareLog{path: path} } // OnAccept writes an ACCEPT line. // // 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", timestamp(), event.Miner.User(), event.Diff, event.Latency, ) l.writeLine(line) } // OnReject writes a REJECT line with the rejection reason. // // 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", timestamp(), 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.f != nil { _ = l.f.Close() l.f = 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.f == nil { file, errorValue := os.OpenFile(l.path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644) if errorValue != nil { return } l.f = file } _, _ = l.f.WriteString(line) } func timestamp() string { return time.Now().UTC().Format(time.RFC3339) }