summaryrefslogtreecommitdiff
path: root/internal/io/dlog/loggers/fout.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/io/dlog/loggers/fout.go')
-rw-r--r--internal/io/dlog/loggers/fout.go68
1 files changed, 62 insertions, 6 deletions
diff --git a/internal/io/dlog/loggers/fout.go b/internal/io/dlog/loggers/fout.go
index 6888d40..0d3fde5 100644
--- a/internal/io/dlog/loggers/fout.go
+++ b/internal/io/dlog/loggers/fout.go
@@ -4,16 +4,47 @@ import (
"context"
"sync"
"time"
+
+ "github.com/mimecast/dtail/internal/config"
)
+// fout logs to both a file and stdout. It is the default client logger.
+//
+// The two things a client emits are deliberately split at this seam:
+// - Diagnostics (connection INFO/WARN/ERROR/etc.) arrive via Log/LogWithColors
+// and are ALWAYS written to both stdout and the file — they are the small,
+// useful audit trail the daily log file is meant to keep.
+// - Retrieved payload (the bulk dcat/dgrep/dtail output) arrives via
+// Raw/RawWithColors. It always reaches stdout/terminal, but it is teed to
+// the file only when logPayload is set (opt-in via --log-payload /
+// Client.LogPayload). By default the file receives no payload, so a bulk
+// dcat no longer silently grows the daily log file by the full payload size.
type fout struct {
- file *file
- stdout *stdout
+ file Logger
+ stdout Logger
+ logPayload bool
}
-// Logs to both, a file and stdout
+// newFout builds the default client logger. Whether retrieved payload is teed
+// to the file is decided once at construction from the client config.
func newFout(strategy Strategy) *fout {
- return &fout{file: newFile(strategy), stdout: newStdout()}
+ return newFoutWithSinks(newFile(strategy), newStdout(), clientLogPayloadEnabled())
+}
+
+// newFoutWithSinks builds a fout over injectable sinks and an explicit payload
+// switch. Production uses newFout (concrete file+stdout, config-driven switch);
+// tests inject fakes to assert that diagnostics always reach the file while
+// payload reaches it only when opted in.
+func newFoutWithSinks(file, stdout Logger, logPayload bool) *fout {
+ return &fout{file: file, stdout: stdout, logPayload: logPayload}
+}
+
+// clientLogPayloadEnabled reports whether the client has opted in to teeing the
+// full retrieved payload into the daily log file. Default (false) keeps only
+// diagnostics in the file. config.Client is nil-guarded because a logger can be
+// constructed in early/unit contexts before config.Setup has populated it.
+func clientLogPayloadEnabled() bool {
+ return config.Client != nil && config.Client.LogPayload
}
func (f *fout) Start(ctx context.Context, wg *sync.WaitGroup) {
@@ -35,17 +66,42 @@ func (f *fout) Log(now time.Time, message string) {
func (f *fout) LogWithColors(now time.Time, message, coloredMessage string) {
f.stdout.LogWithColors(now, "", coloredMessage)
+ // The file logger does not support colors, so write the plain message via
+ // Log (its LogWithColors would route to RawWithColors, which panics).
f.file.Log(now, message)
}
+// Raw writes retrieved payload. It always reaches stdout/terminal; it is teed
+// to the file sink only when the client opted in via --log-payload /
+// Client.LogPayload. By default the file is left payload-free.
func (f *fout) Raw(now time.Time, message string) {
f.stdout.Raw(now, message)
- f.file.Raw(now, message)
+ if f.logPayload {
+ f.file.Raw(now, message)
+ }
}
func (f *fout) RawWithColors(now time.Time, message, coloredMessage string) {
f.stdout.RawWithColors(now, "", coloredMessage)
- f.file.Raw(now, message)
+ // Same opt-in gate as Raw; the file gets the plain (uncolored) payload.
+ if f.logPayload {
+ f.file.Raw(now, message)
+ }
+}
+
+// RawFileOnly tees retrieved payload into the daily log FILE sink only, never to
+// stdout, honoring the same --log-payload / Client.LogPayload opt-in as Raw.
+//
+// It exists for the serverless direct-output path: that path writes
+// payload straight to its own stdout sink and bypasses Raw entirely, so without
+// this hook --log-payload would silently no longer tee payload to the file in
+// serverless mode. The caller (the serverless output writer) already emits the
+// payload bytes to stdout itself, so this method deliberately writes ONLY to the
+// file to keep stdout byte-identical whether or not --log-payload is set.
+func (f *fout) RawFileOnly(now time.Time, message string) {
+ if f.logPayload {
+ f.file.Raw(now, message)
+ }
}
func (f *fout) Flush() { f.stdout.Flush(); f.file.Flush() }