@ -9,6 +9,7 @@ package logpolicy
import (
"bufio"
"bytes"
"cmp"
"context"
"crypto/tls"
"encoding/json"
@ -449,25 +450,63 @@ func tryFixLogStateLocation(dir, cmdname string, logf logger.Logf) {
}
}
// New returns a new log policy (a logger and its instance ID) for a given
// collection name.
//
// The netMon parameter is optional. It should be specified in environments where
// Tailscaled is manipulating the routing table.
//
// The logf parameter is optional; if non-nil, information logs (e.g. when
// migrating state) are sent to that logger, and global changes to the log
// package are avoided. If nil, logs will be printed using log.Printf.
// Deprecated: Use [Options.New] instead.
func New ( collection string , netMon * netmon . Monitor , health * health . Tracker , logf logger . Logf ) * Policy {
return NewWithConfigPath ( collection , "" , "" , netMon , health , logf )
return Options {
Collection : collection ,
NetMon : netMon ,
Health : health ,
Logf : logf ,
} . New ( )
}
// NewWithConfigPath is identical to New, but uses the specified directory and
// command name. If either is empty, it derives them automatically.
//
// The netMon parameter is optional. It should be specified in environments where
// Tailscaled is manipulating the routing table.
// Deprecated: Use [Options.New] instead.
func NewWithConfigPath ( collection , dir , cmdName string , netMon * netmon . Monitor , health * health . Tracker , logf logger . Logf ) * Policy {
return Options {
Collection : collection ,
Dir : dir ,
CmdName : cmdName ,
NetMon : netMon ,
Health : health ,
Logf : logf ,
} . New ( )
}
// Options is used to construct a [Policy].
type Options struct {
// Collection is a required collection to upload logs under.
// Collection is a namespace for the type logs.
// For example, logs for a node use "tailnode.log.tailscale.io".
Collection string
// Dir is an optional directory to store the log configuration.
// If empty, [LogsDir] is used.
Dir string
// CmdName is an optional name of the current binary.
// If empty, [version.CmdName] is used.
CmdName string
// NetMon is an optional parameter for monitoring.
// If non-nil, it's used to do faster interface lookups.
NetMon * netmon . Monitor
// Health is an optional parameter for health status.
// If non-nil, it's used to construct the default HTTP client.
Health * health . Tracker
// Logf is an optional logger to use.
// If nil, [log.Printf] will be used instead.
Logf logger . Logf
// HTTPC is an optional client to use upload logs.
// If nil, [TransportOptions.New] is used to construct a new client
// with that particular transport sending logs to the default logs server.
HTTPC * http . Client
}
// New returns a new log policy (a logger and its instance ID).
func ( opts Options ) New ( ) * Policy {
if hostinfo . IsNATLabGuestVM ( ) {
// In NATLab Gokrazy instances, tailscaled comes up concurently with
// DHCP and the doesn't have DNS for a while. Wait for DHCP first.
@ -495,23 +534,23 @@ func NewWithConfigPath(collection, dir, cmdName string, netMon *netmon.Monitor,
earlyErrBuf . WriteByte ( '\n' )
}
if d ir == "" {
d ir = LogsDir ( earlyLogf )
if opts. D ir == "" {
opts. D ir = LogsDir ( earlyLogf )
}
if c mdName == "" {
c mdName = version . CmdName ( )
if opts. C mdName == "" {
opts. C mdName = version . CmdName ( )
}
useStdLogger := l ogf == nil
useStdLogger := opts. L ogf == nil
if useStdLogger {
l ogf = log . Printf
opts. L ogf = log . Printf
}
tryFixLogStateLocation ( dir, cmdName , l ogf)
tryFixLogStateLocation ( opts. Dir , opts . CmdName , opts . L ogf)
cfgPath := filepath . Join ( d ir, fmt . Sprintf ( "%s.log.conf" , c mdName) )
cfgPath := filepath . Join ( opts. D ir, fmt . Sprintf ( "%s.log.conf" , opts. C mdName) )
if runtime . GOOS == "windows" {
switch c mdName {
switch opts. C mdName {
case "tailscaled" :
// Tailscale 1.14 and before stored state under %LocalAppData%
// (usually "C:\WINDOWS\system32\config\systemprofile\AppData\Local"
@ -542,7 +581,7 @@ func NewWithConfigPath(collection, dir, cmdName string, netMon *netmon.Monitor,
cfgPath = paths . TryConfigFileMigration ( earlyLogf , oldPath , cfgPath )
case "tailscale-ipn" :
for _ , oldBase := range [ ] string { "wg64.log.conf" , "wg32.log.conf" } {
oldConf := filepath . Join ( d ir, oldBase )
oldConf := filepath . Join ( opts. D ir, oldBase )
if fi , err := os . Stat ( oldConf ) ; err == nil && fi . Mode ( ) . IsRegular ( ) {
cfgPath = paths . TryConfigFileMigration ( earlyLogf , oldConf , cfgPath )
break
@ -555,9 +594,9 @@ func NewWithConfigPath(collection, dir, cmdName string, netMon *netmon.Monitor,
if err != nil {
earlyLogf ( "logpolicy.ConfigFromFile %v: %v" , cfgPath , err )
}
if err := newc . Validate ( c ollection) ; err != nil {
if err := newc . Validate ( opts. C ollection) ; err != nil {
earlyLogf ( "logpolicy.Config.Validate for %v: %v" , cfgPath , err )
newc = NewConfig ( c ollection)
newc = NewConfig ( opts. C ollection)
if err := newc . Save ( cfgPath ) ; err != nil {
earlyLogf ( "logpolicy.Config.Save for %v: %v" , cfgPath , err )
}
@ -568,31 +607,39 @@ func NewWithConfigPath(collection, dir, cmdName string, netMon *netmon.Monitor,
PrivateID : newc . PrivateID ,
Stderr : logWriter { console } ,
CompressLogs : true ,
HTTPC : & http . Client { Transport : NewLogtailTransport ( logtail . DefaultHost , netMon , health , logf ) } ,
}
if c ollection == logtail . CollectionNode {
if opts. C ollection == logtail . CollectionNode {
conf . MetricsDelta = clientmetric . EncodeLogTailMetricsDelta
conf . IncludeProcID = true
conf . IncludeProcSequence = true
}
if envknob . NoLogsNoSupport ( ) || testenv . InTest ( ) {
l ogf( "You have disabled logging. Tailscale will not be able to provide support." )
opts . L ogf( "You have disabled logging. Tailscale will not be able to provide support." )
conf . HTTPC = & http . Client { Transport : noopPretendSuccessTransport { } }
} else {
// Only attach an on-disk filch buffer if we are going to be sending logs.
// No reason to persist them locally just to drop them later.
attachFilchBuffer ( & conf , dir , cmdName , logf )
if val := getLogTarget ( ) ; val != "" {
logf ( "You have enabled a non-default log target. Doing without being told to by Tailscale staff or your network administrator will make getting support difficult." )
conf . BaseURL = val
u , _ := url . Parse ( val )
conf . HTTPC = & http . Client { Transport : NewLogtailTransport ( u . Host , netMon , health , logf ) }
attachFilchBuffer ( & conf , opts . Dir , opts . CmdName , opts . Logf )
conf . HTTPC = opts . HTTPC
if conf . HTTPC == nil {
logHost := logtail . DefaultHost
if val := getLogTarget ( ) ; val != "" {
opts . Logf ( "You have enabled a non-default log target. Doing without being told to by Tailscale staff or your network administrator will make getting support difficult." )
conf . BaseURL = val
u , _ := url . Parse ( val )
logHost = u . Host
}
conf . HTTPC = & http . Client { Transport : TransportOptions {
Host : logHost ,
NetMon : opts . NetMon ,
Health : opts . Health ,
Logf : opts . Logf ,
} . New ( ) }
}
}
lw := logtail . NewLogger ( conf , logf )
lw := logtail . NewLogger ( conf , opts. L ogf)
var logOutput io . Writer = lw
@ -610,19 +657,19 @@ func NewWithConfigPath(collection, dir, cmdName string, netMon *netmon.Monitor,
log . SetOutput ( logOutput )
}
l ogf( "Program starting: v%v, Go %v: %#v" ,
opts . L ogf( "Program starting: v%v, Go %v: %#v" ,
version . Long ( ) ,
goVersion ( ) ,
os . Args )
l ogf( "LogID: %v" , newc . PublicID )
opts . L ogf( "LogID: %v" , newc . PublicID )
if earlyErrBuf . Len ( ) != 0 {
l ogf( "%s" , earlyErrBuf . Bytes ( ) )
opts . L ogf( "%s" , earlyErrBuf . Bytes ( ) )
}
return & Policy {
Logtail : lw ,
PublicID : newc . PublicID ,
Logf : l ogf,
Logf : opts. L ogf,
}
}
@ -763,23 +810,48 @@ func dialContext(ctx context.Context, netw, addr string, netMon *netmon.Monitor,
return c , err
}
// NewLogtailTransport returns an HTTP Transport particularly suited to uploading
// logs to the given host name. See DialContext for details on how it works.
//
// The netMon parameter is optional. It should be specified in environments where
// Tailscaled is manipulating the routing table.
//
// The logf parameter is optional; if non-nil, logs are printed using the
// provided function; if nil, log.Printf will be used instead.
// Deprecated: Use [TransportOptions.New] instead.
func NewLogtailTransport ( host string , netMon * netmon . Monitor , health * health . Tracker , logf logger . Logf ) http . RoundTripper {
return TransportOptions { Host : host , NetMon : netMon , Health : health , Logf : logf } . New ( )
}
// TransportOptions is used to construct an [http.RoundTripper].
type TransportOptions struct {
// Host is the optional hostname of the logs server.
// If empty, then [logtail.DefaultHost] is used.
Host string
// NetMon is an optional parameter for monitoring.
// If non-nil, it's used to do faster interface lookups.
NetMon * netmon . Monitor
// Health is an optional parameter for health status.
// If non-nil, it's used to construct the default HTTP client.
Health * health . Tracker
// Logf is an optional logger to use.
// If nil, [log.Printf] will be used instead.
Logf logger . Logf
// TLSClientConfig is an optional TLS configuration to use.
// If non-nil, the configuration will be cloned.
TLSClientConfig * tls . Config
}
// New returns an HTTP Transport particularly suited to uploading logs
// to the given host name. See [DialContext] for details on how it works.
func ( opts TransportOptions ) New ( ) http . RoundTripper {
if testenv . InTest ( ) {
return noopPretendSuccessTransport { }
}
if netMon == nil {
netMon = netmon . NewStatic ( )
if opts. N etMon == nil {
opts. N etMon = netmon . NewStatic ( )
}
// Start with a copy of http.DefaultTransport and tweak it a bit.
tr := http . DefaultTransport . ( * http . Transport ) . Clone ( )
if opts . TLSClientConfig != nil {
tr . TLSClientConfig = opts . TLSClientConfig . Clone ( )
}
tr . Proxy = tshttpproxy . ProxyFromEnvironment
tshttpproxy . SetTransportGetProxyConnectHeader ( tr )
@ -790,10 +862,10 @@ func NewLogtailTransport(host string, netMon *netmon.Monitor, health *health.Tra
tr . DisableCompression = true
// Log whenever we dial:
if l ogf == nil {
l ogf = log . Printf
if opts. L ogf == nil {
opts. L ogf = log . Printf
}
tr . DialContext = MakeDialFunc ( netMon, l ogf)
tr . DialContext = MakeDialFunc ( opts. NetMon , opts . L ogf)
// We're uploading logs ideally infrequently, with specific timing that will
// change over time. Try to keep the connection open, to avoid repeatedly
@ -815,7 +887,8 @@ func NewLogtailTransport(host string, netMon *netmon.Monitor, health *health.Tra
tr . TLSNextProto = map [ string ] func ( authority string , c * tls . Conn ) http . RoundTripper { }
}
tr . TLSClientConfig = tlsdial . Config ( host , health , tr . TLSClientConfig )
host := cmp . Or ( opts . Host , logtail . DefaultHost )
tr . TLSClientConfig = tlsdial . Config ( host , opts . Health , tr . TLSClientConfig )
// Force TLS 1.3 since we know log.tailscale.io supports it.
tr . TLSClientConfig . MinVersion = tls . VersionTLS13