From 849951be1d1a7ee9f9302006ccb187bf5b4e36f3 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Wed, 22 Jul 2026 23:51:18 +0300 Subject: =?UTF-8?q?feat:=20DTail=20fork=20=E2=80=94=20server/client=20feat?= =?UTF-8?q?ure=20development?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Squashed development of the snonux/dtail fork's product code (internal/, cmd/) since diverging from mimecast/dtail. Major areas: - Read/output path: the former "turbo" channel-less path is now the single, default server-side read/output path for cat/grep/tail and MapReduce; the old channel-based path and its config/env toggles were removed. - MapReduce: single aggregate implementation (server + serverless) fed directly by a processor pipeline, with input-exhausted finalization via the shutdown coordinator; high-concurrency and data-race fixes. - Journal source reads (journal:unit.service) via journalctl, Linux-gated behind a journal-v1 capability. - Auth-key fast reconnect: in-memory per-user public-key cache with TTL/max-keys, registered over an authenticated session (AUTHKEY), checked before authorized_keys. - Interactive query reload (--interactive-query) with SESSION START/UPDATE generation boundaries and capability negotiation. - Client-side deadlines: --timeout / --shutdownAfter as context deadlines; follow shutdown handling. - Client logging: diagnostics-only daily log by default, opt-in payload tee via --log-payload. - Numerous correctness fixes (buffer-pool double-recycle races, EOF-sentinel leaks, glob-expansion cap, TOCTOU in CSV parsing) with accompanying unit tests. Co-Authored-By: Claude Opus 4.8 --- internal/io/dlog/rawlog_test.go | 76 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 internal/io/dlog/rawlog_test.go (limited to 'internal/io/dlog/rawlog_test.go') diff --git a/internal/io/dlog/rawlog_test.go b/internal/io/dlog/rawlog_test.go new file mode 100644 index 0000000..7fb7a05 --- /dev/null +++ b/internal/io/dlog/rawlog_test.go @@ -0,0 +1,76 @@ +package dlog + +import ( + "context" + "sync" + "testing" + "time" + + "github.com/mimecast/dtail/internal/config" + "github.com/mimecast/dtail/internal/io/dlog/loggers" +) + +// recordingLogger records whether a message arrived via the diagnostic (Log) +// path or the payload (Raw) path, so the test can assert how RawLog vs Raw route +// their messages. It reports SupportsColors()=false so the callers take their +// non-color branch (logger.Log / logger.Raw), which is what the routing test +// needs to observe. +type recordingLogger struct { + mutex sync.Mutex + logs []string + raws []string +} + +func (r *recordingLogger) Log(now time.Time, message string) { + r.mutex.Lock() + defer r.mutex.Unlock() + r.logs = append(r.logs, message) +} +func (r *recordingLogger) LogWithColors(now time.Time, message, colored string) { r.Log(now, message) } +func (r *recordingLogger) Raw(now time.Time, message string) { + r.mutex.Lock() + defer r.mutex.Unlock() + r.raws = append(r.raws, message) +} +func (r *recordingLogger) RawWithColors(now time.Time, message, colored string) { r.Raw(now, message) } +func (r *recordingLogger) Start(ctx context.Context, wg *sync.WaitGroup) { wg.Done() } +func (r *recordingLogger) Flush() {} +func (r *recordingLogger) Pause() {} +func (r *recordingLogger) Resume() {} +func (r *recordingLogger) Rotate() {} +func (r *recordingLogger) SupportsColors() bool { return false } + +var _ loggers.Logger = (*recordingLogger)(nil) + +// TestRawLogUsesDiagnosticSink is the regression guard for the ReportServerError +// footgun: a server-error audit line must go through the diagnostic (Log) sink, +// not the payload (Raw) sink. Only the Log sink is written to the client log file +// by default (Client.LogPayload=false gates the Raw/payload sink out of the file), +// so a server error routed via Raw would silently vanish from the on-disk audit +// trail. This asserts RawLog -> Log and, for contrast, Raw -> Raw. +func TestRawLogUsesDiagnosticSink(t *testing.T) { + prevClient := config.Client + config.Client = &config.ClientConfig{TermColorsEnable: false} + t.Cleanup(func() { config.Client = prevClient }) + + rec := &recordingLogger{} + d := &DLog{logger: rec} + + const serverError = "SERVER|srv1|ERROR|journal file targets require server capability journal-v1" + d.RawLog(serverError) + + if len(rec.logs) != 1 || rec.logs[0] != serverError { + t.Fatalf("RawLog must reach the diagnostic (Log) sink verbatim; got logs=%v raws=%v", + rec.logs, rec.raws) + } + if len(rec.raws) != 0 { + t.Fatalf("RawLog must NOT use the payload (Raw) sink (it would be gated out of the file); got raws=%v", + rec.raws) + } + + // Contrast: bulk payload still goes through the Raw/payload sink. + d.Raw("payload-line\n") + if len(rec.raws) != 1 || rec.raws[0] != "payload-line\n" { + t.Fatalf("Raw must reach the payload (Raw) sink; got raws=%v", rec.raws) + } +} -- cgit v1.2.3