From 9b6a25114583338831186779da38e8684885768b Mon Sep 17 00:00:00 2001 From: Virgil Date: Sat, 4 Apr 2026 22:29:00 +0000 Subject: [PATCH] fix(log): align access log formats with RFC Co-Authored-By: Virgil --- accesslog_impl.go | 33 +++++++++++++++++++++++++++++---- accesslog_test.go | 12 ++++++------ log/impl.go | 26 ++++++++++++++++++++++---- 3 files changed, 57 insertions(+), 14 deletions(-) diff --git a/accesslog_impl.go b/accesslog_impl.go index d2c5894..0be342f 100644 --- a/accesslog_impl.go +++ b/accesslog_impl.go @@ -50,17 +50,17 @@ func (l *accessLogSink) OnLogin(e Event) { if l == nil || e.Miner == nil { return } - l.writeLine("CONNECT", e.Miner.IP(), e.Miner.User(), e.Miner.Agent(), 0, 0) + l.writeConnectLine(e.Miner.IP(), e.Miner.User(), e.Miner.Agent()) } func (l *accessLogSink) OnClose(e Event) { if l == nil || e.Miner == nil { return } - l.writeLine("CLOSE", e.Miner.IP(), e.Miner.User(), "", e.Miner.RX(), e.Miner.TX()) + l.writeCloseLine(e.Miner.IP(), e.Miner.User(), e.Miner.RX(), e.Miner.TX()) } -func (l *accessLogSink) writeLine(kind, ip, user, agent string, rx, tx uint64) { +func (l *accessLogSink) writeConnectLine(ip, user, agent string) { l.mu.Lock() defer l.mu.Unlock() if strings.TrimSpace(l.path) == "" { @@ -76,13 +76,38 @@ func (l *accessLogSink) writeLine(kind, ip, user, agent string, rx, tx uint64) { var builder strings.Builder builder.WriteString(time.Now().UTC().Format(time.RFC3339)) builder.WriteByte(' ') - builder.WriteString(kind) + builder.WriteString("CONNECT") builder.WriteString(" ") builder.WriteString(ip) builder.WriteString(" ") builder.WriteString(user) builder.WriteString(" ") builder.WriteString(agent) + builder.WriteByte('\n') + _, _ = l.file.WriteString(builder.String()) +} + +func (l *accessLogSink) writeCloseLine(ip, user string, rx, tx uint64) { + l.mu.Lock() + defer l.mu.Unlock() + if strings.TrimSpace(l.path) == "" { + return + } + if l.file == nil { + file, err := os.OpenFile(l.path, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o644) + if err != nil { + return + } + l.file = file + } + var builder strings.Builder + builder.WriteString(time.Now().UTC().Format(time.RFC3339)) + builder.WriteByte(' ') + builder.WriteString("CLOSE") + builder.WriteString(" ") + builder.WriteString(ip) + builder.WriteString(" ") + builder.WriteString(user) builder.WriteString(" rx=") builder.WriteString(formatUint(rx)) builder.WriteString(" tx=") diff --git a/accesslog_test.go b/accesslog_test.go index f01d08b..e31b5a7 100644 --- a/accesslog_test.go +++ b/accesslog_test.go @@ -44,10 +44,10 @@ func TestProxy_AccessLog_WritesLifecycleLines(t *testing.T) { t.Fatalf("read access log: %v", err) } text := string(data) - if !strings.Contains(text, "CONNECT 10.0.0.1 WALLET XMRig/6.21.0 rx=0 tx=0") { + if !strings.Contains(text, "CONNECT 10.0.0.1 WALLET XMRig/6.21.0") { t.Fatalf("expected CONNECT line, got %q", text) } - if !strings.Contains(text, "CLOSE 10.0.0.1 WALLET rx=512 tx=4096") { + if !strings.Contains(text, "CLOSE 10.0.0.1 WALLET rx=512 tx=4096") { t.Fatalf("expected CLOSE line, got %q", text) } } @@ -82,11 +82,11 @@ func TestProxy_AccessLog_WritesFixedColumns(t *testing.T) { t.Fatalf("read access log: %v", err) } text := string(data) - if !strings.Contains(text, "CONNECT 10.0.0.1 WALLET rx=0 tx=0") { - t.Fatalf("expected CONNECT line with zero counters, got %q", text) + if !strings.Contains(text, "CONNECT 10.0.0.1 WALLET") { + t.Fatalf("expected CONNECT line without counters, got %q", text) } - if !strings.Contains(text, "CLOSE 10.0.0.1 WALLET rx=0 tx=0") { - t.Fatalf("expected CLOSE line with zero counters, got %q", text) + if !strings.Contains(text, "CLOSE 10.0.0.1 WALLET rx=0 tx=0") { + t.Fatalf("expected CLOSE line with counters only, got %q", text) } } diff --git a/log/impl.go b/log/impl.go index f11372b..ba1232c 100644 --- a/log/impl.go +++ b/log/impl.go @@ -19,7 +19,7 @@ func (l *AccessLog) OnLogin(e proxy.Event) { if l == nil || e.Miner == nil { return } - l.writeLine("CONNECT", e.Miner.IP(), e.Miner.User(), e.Miner.Agent(), 0, 0) + l.writeConnectLine(e.Miner.IP(), e.Miner.User(), e.Miner.Agent()) } // OnClose writes a CLOSE line with byte counts. @@ -27,7 +27,7 @@ func (l *AccessLog) OnClose(e proxy.Event) { if l == nil || e.Miner == nil { return } - l.writeLine("CLOSE", e.Miner.IP(), e.Miner.User(), "", e.Miner.RX(), e.Miner.TX()) + l.writeCloseLine(e.Miner.IP(), e.Miner.User(), e.Miner.RX(), e.Miner.TX()) } // NewShareLog creates an append-only share log. @@ -51,7 +51,7 @@ func (l *ShareLog) OnReject(e proxy.Event) { l.writeRejectLine(e.Miner.User(), e.Error) } -func (accessLog *AccessLog) writeLine(kind, ip, user, agent string, rx, tx uint64) { +func (accessLog *AccessLog) writeConnectLine(ip, user, agent string) { accessLog.mu.Lock() defer accessLog.mu.Unlock() if err := accessLog.ensureFile(); err != nil { @@ -60,13 +60,31 @@ func (accessLog *AccessLog) writeLine(kind, ip, user, agent string, rx, tx uint6 var builder strings.Builder builder.WriteString(time.Now().UTC().Format(time.RFC3339)) builder.WriteByte(' ') - builder.WriteString(kind) + builder.WriteString("CONNECT") builder.WriteString(" ") builder.WriteString(ip) builder.WriteString(" ") builder.WriteString(user) builder.WriteString(" ") builder.WriteString(agent) + builder.WriteByte('\n') + _, _ = accessLog.f.WriteString(builder.String()) +} + +func (accessLog *AccessLog) writeCloseLine(ip, user string, rx, tx uint64) { + accessLog.mu.Lock() + defer accessLog.mu.Unlock() + if err := accessLog.ensureFile(); err != nil { + return + } + var builder strings.Builder + builder.WriteString(time.Now().UTC().Format(time.RFC3339)) + builder.WriteByte(' ') + builder.WriteString("CLOSE") + builder.WriteString(" ") + builder.WriteString(ip) + builder.WriteString(" ") + builder.WriteString(user) builder.WriteString(" rx=") builder.WriteString(strconv.FormatUint(rx, 10)) builder.WriteString(" tx=")