summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorBrad Fitzpatrick <bradfitz@tailscale.com>2026-04-17 18:12:08 +0000
committerBrad Fitzpatrick <brad@danga.com>2026-04-17 12:19:39 -0700
commit1fbb834dc32a12f98cada56060b71bfffb37301e (patch)
tree3968c0968e79a85900146458e08c950af1139503
parent8dda62cc24692b17e9bec0156160d8e54046d762 (diff)
downloadtailscale-1fbb834dc32a12f98cada56060b71bfffb37301e.tar.xz
tailscale-1fbb834dc32a12f98cada56060b71bfffb37301e.zip
logtail: add Logger.SetEnabled to toggle uploads at runtime
Callers that need to turn logtail uploads on and off in response to user preference or policy changes previously had no choice: the package-level Disable is a one-way kill switch intended for the controlplane DisableLogTail debug message, and requires a process restart to undo. Add a per-Logger disabled flag, toggled via SetEnabled, that drops incoming entries without buffering while disabled. The process-wide Disable still takes precedence, so a controlplane-issued kill switch cannot be overridden by a client setting it back on. To simplify https://github.com/tailscale/tailscale-android/pull/695 Updates #13174 Change-Id: I06e75bd719c851f5f837ca5b2d1e17f7c68355f0 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
-rw-r--r--logtail/logtail.go16
-rw-r--r--logtail/logtail_omit.go2
-rw-r--r--logtail/logtail_test.go31
3 files changed, 48 insertions, 1 deletions
diff --git a/logtail/logtail.go b/logtail/logtail.go
index ed3872e79..39a48ba79 100644
--- a/logtail/logtail.go
+++ b/logtail/logtail.go
@@ -172,6 +172,11 @@ type Logger struct {
procID uint32
includeProcSequence bool
+ // disabled, when true, causes this logger to drop incoming log entries
+ // without buffering or uploading. It is independent of the process-wide
+ // Disable kill switch, which takes precedence. Toggled by SetEnabled.
+ disabled atomic.Bool
+
writeLock sync.Mutex // guards procSequence, flushTimer, buffer.Write calls
procSequence uint64
flushTimer tstime.TimerController // used when flushDelay is >0
@@ -594,6 +599,15 @@ func Disable() {
logtailDisabled.Store(true)
}
+// SetEnabled enables or disables log uploading by lg. When disabled, log
+// entries passed to lg are dropped rather than buffered or uploaded; already
+// buffered entries may still drain. The process-wide [Disable] kill switch
+// takes precedence: if Disable has been called, SetEnabled(true) does not
+// re-enable uploads.
+func (lg *Logger) SetEnabled(enabled bool) {
+ lg.disabled.Store(!enabled)
+}
+
var debugWakesAndUploads = envknob.RegisterBool("TS_DEBUG_LOGTAIL_WAKES")
// tryDrainWake tries to send to lg.drainWake, to cause an uploading wakeup.
@@ -613,7 +627,7 @@ func (lg *Logger) tryDrainWake() {
func (lg *Logger) sendLocked(jsonBlob []byte) (int, error) {
tapSend(jsonBlob)
- if logtailDisabled.Load() {
+ if logtailDisabled.Load() || lg.disabled.Load() {
return len(jsonBlob), nil
}
diff --git a/logtail/logtail_omit.go b/logtail/logtail_omit.go
index 21f18c980..98f1c6a0e 100644
--- a/logtail/logtail_omit.go
+++ b/logtail/logtail_omit.go
@@ -20,6 +20,8 @@ type Buffer any
func Disable() {}
+func (*Logger) SetEnabled(enabled bool) {}
+
func NewLogger(cfg Config, logf tslogger.Logf) *Logger {
return &Logger{}
}
diff --git a/logtail/logtail_test.go b/logtail/logtail_test.go
index 19e1eeb7a..eadbbc630 100644
--- a/logtail/logtail_test.go
+++ b/logtail/logtail_test.go
@@ -321,6 +321,37 @@ func TestLoggerWriteResult(t *testing.T) {
}
}
+func TestLoggerSetEnabled(t *testing.T) {
+ buf := NewMemoryBuffer(100)
+ lg := &Logger{
+ clock: tstest.NewClock(tstest.ClockOpts{Start: time.Unix(123, 0)}),
+ buffer: buf,
+ }
+
+ if _, err := lg.Write([]byte("enabled1")); err != nil {
+ t.Fatal(err)
+ }
+ if back, _ := buf.TryReadLine(); !strings.Contains(string(back), "enabled1") {
+ t.Fatalf("initial write not buffered; got %q", back)
+ }
+
+ lg.SetEnabled(false)
+ if _, err := lg.Write([]byte("disabled")); err != nil {
+ t.Fatal(err)
+ }
+ if back, _ := buf.TryReadLine(); len(back) != 0 {
+ t.Errorf("write while disabled leaked into buffer: %q", back)
+ }
+
+ lg.SetEnabled(true)
+ if _, err := lg.Write([]byte("enabled2")); err != nil {
+ t.Fatal(err)
+ }
+ if back, _ := buf.TryReadLine(); !strings.Contains(string(back), "enabled2") {
+ t.Errorf("write after re-enable not buffered; got %q", back)
+ }
+}
+
func TestAppendMetadata(t *testing.T) {
var lg Logger
lg.clock = tstest.NewClock(tstest.ClockOpts{Start: time.Date(2000, 01, 01, 0, 0, 0, 0, time.UTC)})