mirror of
https://github.com/tailscale/tailscale.git
synced 2026-06-23 23:41:41 -04:00
When running `tailscale netcheck`, the reported timestamp used to be in UTC and formatted according to RFC 3339 with a `T` to separate the date from the time: sfllaw@h2co3:~$ tailscale netcheck | head -n3 Report: * Time: 2026-06-01T21:12:32.252620138Z This is machine-readable time leaking out to the user interface. Times in normal commands are formatted for humans to read: sfllaw@h2co3:~$ date Mon 01 Jun 2026 02:39:14 PM PDT sfllaw@h2co3:~$ journalctl -t tailscaled | tail -n1 Jun 01 14:35:21 h2co3 tailscaled[3328921]: wgengine: sending TSMP disco key advertisement to 100.90.144.102 sfllaw@h2co3:~$ timedatectl show Timezone=America/Los_Angeles LocalRTC=no CanNTP=yes NTP=yes NTPSynchronized=yes TimeUSec=Mon 2026-06-01 14:38:32 PDT RTCTimeUSec=Mon 2026-06-01 14:38:32 PDT sfllaw@h2co3:~$ uptime --since 2026-05-15 07:37:45 This PR makes the times printed by the CLI commands consistent: - For `tailscale routecheck`, it now prints local time as `2026-05-15 07:37:45-07:00`. - For `netlogfmt`, it has always printed local time with a space, but now includes the time zone. - All machine-readable outputs continue to be standard RFC 3339 in UTC, i.e. `--format=json`. As part of a general cleanup, this PR also adds standard common time.Format layouts as tstime constants. Fixes #19928 Signed-off-by: Simon Law <sfllaw@tailscale.com>
79 lines
2.7 KiB
Go
79 lines
2.7 KiB
Go
// Copyright (c) Tailscale Inc & contributors
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
package tsweb
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"time"
|
|
|
|
"tailscale.com/tstime"
|
|
"tailscale.com/util/ctxkey"
|
|
"tailscale.com/util/rands"
|
|
)
|
|
|
|
// RequestID is an opaque identifier for a HTTP request, used to correlate
|
|
// user-visible errors with backend server logs. The RequestID is typically
|
|
// threaded through an HTTP Middleware (WithRequestID) and then can be extracted
|
|
// by HTTP Handlers to include in their logs.
|
|
//
|
|
// RequestID is an opaque identifier for a HTTP request, used to correlate
|
|
// user-visible errors with backend server logs. If present in the context, the
|
|
// RequestID will be printed alongside the message text and logged in the
|
|
// AccessLogRecord.
|
|
//
|
|
// A RequestID has the format "REQ-1{ID}", and the ID should be treated as an
|
|
// opaque string. The current implementation uses a UUID.
|
|
type RequestID string
|
|
|
|
// String returns the string format of the request ID, for use in e.g. setting
|
|
// a [http.Header].
|
|
func (r RequestID) String() string {
|
|
return string(r)
|
|
}
|
|
|
|
// RequestIDKey stores and loads [RequestID] values within a [context.Context].
|
|
var RequestIDKey ctxkey.Key[RequestID]
|
|
|
|
// RequestIDHeader is a custom HTTP header that the WithRequestID middleware
|
|
// uses to determine whether to re-use a given request ID from the client
|
|
// or generate a new one.
|
|
const RequestIDHeader = "X-Tailscale-Request-Id"
|
|
|
|
// GenerateRequestID generates a new request ID with the current format.
|
|
func GenerateRequestID() RequestID {
|
|
// Return a string of the form "REQ-<VersionByte><...>"
|
|
// Previously we returned "REQ-1<UUIDString>".
|
|
// Now we return "REQ-2" version, where the "2" doubles as the year 2YYY
|
|
// in a leading date.
|
|
now := time.Now().UTC()
|
|
return RequestID("REQ-" + now.Format(tstime.NumericDateTime) + rands.HexString(16))
|
|
}
|
|
|
|
// SetRequestID is an HTTP middleware that injects a RequestID in the
|
|
// *http.Request Context. The value of that request id is either retrieved from
|
|
// the RequestIDHeader or a randomly generated one if not exists. Inner
|
|
// handlers can retrieve this ID from the RequestIDFromContext function.
|
|
func SetRequestID(h http.Handler) http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
var rid RequestID
|
|
if id := r.Header.Get(RequestIDHeader); id != "" {
|
|
rid = RequestID(id)
|
|
} else {
|
|
rid = GenerateRequestID()
|
|
}
|
|
ctx := RequestIDKey.WithValue(r.Context(), rid)
|
|
r = r.WithContext(ctx)
|
|
h.ServeHTTP(w, r)
|
|
})
|
|
}
|
|
|
|
// RequestIDFromContext retrieves the RequestID from context that can be set by
|
|
// the SetRequestID function.
|
|
//
|
|
// Deprecated: Use [RequestIDKey.Value] instead.
|
|
func RequestIDFromContext(ctx context.Context) RequestID {
|
|
return RequestIDKey.Value(ctx)
|
|
}
|