go-proxy/log/share.go
Virgil 48c6e0fc6d feat(proxy): implement RFC runtime primitives
Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-04 10:39:59 +00:00

89 lines
1.9 KiB
Go

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 <user> diff=<diff> latency=<ms>ms
// Line format (reject): 2026-04-04T12:00:00Z REJECT <user> reason="<message>"
//
// 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
}
// 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)
}
func (l *ShareLog) writeLine(line string) {
if l == nil || l.path == "" {
return
}
l.mu.Lock()
defer l.mu.Unlock()
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)
}