diff --git a/control/controlclient/direct.go b/control/controlclient/direct.go index ed5f92590..35fa1d8a0 100644 --- a/control/controlclient/direct.go +++ b/control/controlclient/direct.go @@ -212,6 +212,7 @@ func NewDirect(opts Options) (*Direct, error) { Forward: dnscache.Get().Forward, // use default cache's forwarder UseLastGood: true, LookupIPFallback: dnsfallback.Lookup, + Logf: opts.Logf, } tr := http.DefaultTransport.(*http.Transport).Clone() tr.Proxy = tshttpproxy.ProxyFromEnvironment diff --git a/control/controlhttp/client.go b/control/controlhttp/client.go index 897da19fc..d6fd9a8c5 100644 --- a/control/controlhttp/client.go +++ b/control/controlhttp/client.go @@ -388,12 +388,14 @@ func (a *Dialer) tryURLUpgrade(ctx context.Context, u *url.URL, addr netip.Addr, dns = &dnscache.Resolver{ SingleHostStaticResult: []netip.Addr{addr}, SingleHost: u.Hostname(), + Logf: a.Logf, // not a.logf method; we want to propagate nil-ness } } else { dns = &dnscache.Resolver{ Forward: dnscache.Get().Forward, LookupIPFallback: dnsfallback.Lookup, UseLastGood: true, + Logf: a.Logf, // not a.logf method; we want to propagate nil-ness } } diff --git a/net/dns/resolver/forwarder.go b/net/dns/resolver/forwarder.go index a52108c0c..3619bac8c 100644 --- a/net/dns/resolver/forwarder.go +++ b/net/dns/resolver/forwarder.go @@ -384,6 +384,7 @@ func (f *forwarder) getKnownDoHClientForProvider(urlBase string) (c *http.Client dialer := dnscache.Dialer(nsDialer.DialContext, &dnscache.Resolver{ SingleHost: dohURL.Hostname(), SingleHostStaticResult: allIPs, + Logf: f.logf, }) c = &http.Client{ Transport: &http.Transport{ diff --git a/net/dnsfallback/dnsfallback.go b/net/dnsfallback/dnsfallback.go index 584a10807..a5465b32a 100644 --- a/net/dnsfallback/dnsfallback.go +++ b/net/dnsfallback/dnsfallback.go @@ -250,10 +250,13 @@ func SetCachePath(path string) { // logfunc stores the logging function to use for this package. var logfunc syncs.AtomicValue[logger.Logf] -// SetLogger sets the logging function that this package will use. The default -// logger if this function is not called is 'log.Printf'. -func SetLogger(log logger.Logf) { - logfunc.Store(log) +// SetLogger sets the logging function that this package will use, and returns +// the old value (which may be nil). +// +// If this function is never called, or if this function is called with a nil +// value, 'log.Printf' will be used to print logs. +func SetLogger(log logger.Logf) (old logger.Logf) { + return logfunc.Swap(log) } func logf(format string, args ...any) { diff --git a/tsnet/tsnet.go b/tsnet/tsnet.go index a4a4bda04..88b4f4d06 100644 --- a/tsnet/tsnet.go +++ b/tsnet/tsnet.go @@ -40,6 +40,7 @@ import ( "tailscale.com/logpolicy" "tailscale.com/logtail" "tailscale.com/logtail/filch" + "tailscale.com/net/dnsfallback" "tailscale.com/net/memnet" "tailscale.com/net/proxymux" "tailscale.com/net/socks5" @@ -619,6 +620,25 @@ func (s *Server) logf(format string, a ...interface{}) { log.Printf(format, a...) } +// ReplaceGlobalLoggers will replace any Tailscale-specific package-global +// loggers with this Server's logger. It returns a function that, when called, +// will undo any changes made. +// +// Note that calling this function from multiple Servers will result in the +// last call taking all logs; logs are not duplicated. +func (s *Server) ReplaceGlobalLoggers() (undo func()) { + var undos []func() + + oldDnsFallback := dnsfallback.SetLogger(s.logf) + undos = append(undos, func() { dnsfallback.SetLogger(oldDnsFallback) }) + + return func() { + for _, fn := range undos { + fn() + } + } +} + // printAuthURLLoop loops once every few seconds while the server is still running and // is in NeedsLogin state, printing out the auth URL. func (s *Server) printAuthURLLoop() {