logtail: add stateless generic UploadLogs (#20005)

Add UploadLogs, a stateless alternative to NewLogger for callers that
want to push a batch of log entries without the background uploader,
ring buffer, stderr echoing, or network-up gating that a Logger
provides. Entries are encoded, batched up to the server's maximum
upload size, and POSTed synchronously; unlike Logger it does not retry.

The Logger construction is split into a new unexported newLogger so the
connection/encode/upload machinery is shared without starting the
background goroutine.

Log entries are modeled as a generic LogEntry[T] whose Value is inlined
(via go-json-experiment) alongside the reserved "logtail" metadata
member. T may be a struct (or pointer), a map with a string key, or a
jsontext.Value; use jsontext.Value to mix differently-shaped payloads in
a single upload. UploadLogs fills in client_time/proc_id/proc_seq from
the Config where the caller leaves them zero.

Updates tailscale/corp#40908

Change-Id: Idbf23cd0eb8233082fbdb9abed0f6f153b9225ba

Signed-off-by: James Scott <jim@tailscale.com>
This commit is contained in:
James Scott
2026-06-15 13:27:49 -07:00
committed by GitHub
parent eddd019ee4
commit 94fbb03352
3 changed files with 421 additions and 27 deletions
+17
View File
@@ -7,6 +7,8 @@ package logtail
import (
"context"
"iter"
"time"
tslogger "tailscale.com/types/logger"
"tailscale.com/types/logid"
@@ -18,6 +20,21 @@ type Logger struct{}
type Buffer any
type Logtail struct {
ClientTime time.Time `json:"client_time,omitzero"`
ProcID uint32 `json:"proc_id,omitzero"`
ProcSeq uint64 `json:"proc_seq,omitzero"`
}
type LogEntry[T any] struct {
Logtail Logtail `json:"logtail,omitzero"`
Value T `json:",inline"`
}
func UploadLogs[T any](ctx context.Context, conf Config, entries iter.Seq[LogEntry[T]]) error {
return nil
}
func Disable() {}
func (*Logger) SetEnabled(enabled bool) {}