tailscale/tsweb/log.go
Adrian Dewhurst f75a36f9bc tsweb: add request ID for errors
If an optional request ID generating func is supplied to StdHandler,
then requests that return an error will be logged with a request ID that
is also shown as part of the response.

Updates tailscale/corp#2549

Change-Id: Ic7499706df42f95b6878d44d4aab253e2fc6a69b
Signed-off-by: Adrian Dewhurst <adrian@tailscale.com>
2023-08-16 12:55:31 -04:00

65 lines
2.2 KiB
Go

// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
package tsweb
import (
"encoding/json"
"strings"
"time"
)
// AccessLogRecord is a record of one HTTP request served.
type AccessLogRecord struct {
// Timestamp at which request processing started.
When time.Time `json:"when"`
// Time it took to finish processing the request. It does not
// include the entire lifetime of the underlying connection in
// cases like connection hijacking, only the lifetime of the HTTP
// request handler.
Seconds float64 `json:"duration,omitempty"`
// The client's ip:port.
RemoteAddr string `json:"remote_addr,omitempty"`
// The HTTP protocol version, usually "HTTP/1.1 or HTTP/2".
Proto string `json:"proto,omitempty"`
// Whether the request was received over TLS.
TLS bool `json:"tls,omitempty"`
// The target hostname in the request.
Host string `json:"host,omitempty"`
// The HTTP method invoked.
Method string `json:"method,omitempty"`
// The unescaped request URI, including query parameters.
RequestURI string `json:"request_uri,omitempty"`
// The client's user-agent
UserAgent string `json:"user_agent,omitempty"`
// Where the client was before making this request.
Referer string `json:"referer,omitempty"`
// The HTTP response code sent to the client.
Code int `json:"code,omitempty"`
// Number of bytes sent in response body to client. If the request
// was hijacked, only includes bytes sent up to the point of
// hijacking.
Bytes int `json:"bytes,omitempty"`
// Error encountered during request processing.
Err string `json:"err,omitempty"`
// RequestID is a unique ID for this request. When a request fails due to an
// error, the ID is generated and displayed to the client immediately after
// the error text, as well as logged here. This makes it easier to correlate
// support requests with server logs. If a RequestID generator is not
// configured, RequestID will be empty.
RequestID RequestID `json:"request_id,omitempty"`
}
// String returns m as a JSON string.
func (m AccessLogRecord) String() string {
if m.When.IsZero() {
m.When = time.Now()
}
var buf strings.Builder
json.NewEncoder(&buf).Encode(m)
return strings.TrimRight(buf.String(), "\n")
}