mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-25 19:15:34 +00:00
tsnet: split user facing and backend logging
This adds a new `UserLogf` field to the `Server` struct. When set this any logs generated by Server are logged using `UserLogf` and all spammy backend logs are logged to `Logf`. If it `UserLogf` is unset, we default to `log.Printf` and if `Logf` is unset we discard all the spammy logs. Fixes #12094 Signed-off-by: Maisem Ali <maisem@tailscale.com>
This commit is contained in:
parent
7209c4f91e
commit
486a423716
@ -28,7 +28,6 @@
|
|||||||
"tailscale.com/metrics"
|
"tailscale.com/metrics"
|
||||||
"tailscale.com/tsnet"
|
"tailscale.com/tsnet"
|
||||||
"tailscale.com/tsweb"
|
"tailscale.com/tsweb"
|
||||||
"tailscale.com/types/logger"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -58,8 +57,6 @@ func main() {
|
|||||||
ts := &tsnet.Server{
|
ts := &tsnet.Server{
|
||||||
Dir: *tailscaleDir,
|
Dir: *tailscaleDir,
|
||||||
Hostname: *hostname,
|
Hostname: *hostname,
|
||||||
// Make the stdout logs a clean audit log of connections.
|
|
||||||
Logf: logger.Discard,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if os.Getenv("TS_AUTHKEY") == "" {
|
if os.Getenv("TS_AUTHKEY") == "" {
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
@ -99,8 +100,8 @@ func startNode(t *testing.T, ctx context.Context, controlURL, hostname string) (
|
|||||||
Store: new(mem.Store),
|
Store: new(mem.Store),
|
||||||
Ephemeral: true,
|
Ephemeral: true,
|
||||||
}
|
}
|
||||||
if !*verboseNodes {
|
if *verboseNodes {
|
||||||
s.Logf = logger.Discard
|
s.Logf = log.Printf
|
||||||
}
|
}
|
||||||
t.Cleanup(func() { s.Close() })
|
t.Cleanup(func() { s.Close() })
|
||||||
|
|
||||||
|
@ -40,7 +40,6 @@
|
|||||||
"tailscale.com/tsnet"
|
"tailscale.com/tsnet"
|
||||||
"tailscale.com/types/key"
|
"tailscale.com/types/key"
|
||||||
"tailscale.com/types/lazy"
|
"tailscale.com/types/lazy"
|
||||||
"tailscale.com/types/logger"
|
|
||||||
"tailscale.com/types/views"
|
"tailscale.com/types/views"
|
||||||
"tailscale.com/util/mak"
|
"tailscale.com/util/mak"
|
||||||
"tailscale.com/util/must"
|
"tailscale.com/util/must"
|
||||||
@ -95,8 +94,8 @@ func main() {
|
|||||||
ts := &tsnet.Server{
|
ts := &tsnet.Server{
|
||||||
Hostname: "idp",
|
Hostname: "idp",
|
||||||
}
|
}
|
||||||
if !*flagVerbose {
|
if *flagVerbose {
|
||||||
ts.Logf = logger.Discard
|
ts.Logf = log.Printf
|
||||||
}
|
}
|
||||||
st, err = ts.Up(ctx)
|
st, err = ts.Up(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -16,14 +16,12 @@
|
|||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"tailscale.com/tsnet"
|
"tailscale.com/tsnet"
|
||||||
"tailscale.com/types/logger"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
s := &tsnet.Server{
|
s := &tsnet.Server{
|
||||||
Dir: "./funnel-demo-config",
|
Dir: "./funnel-demo-config",
|
||||||
Logf: logger.Discard,
|
|
||||||
Hostname: "fun",
|
Hostname: "fun",
|
||||||
}
|
}
|
||||||
defer s.Close()
|
defer s.Close()
|
||||||
|
@ -89,15 +89,6 @@ func ExampleServer_multipleInstances() {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExampleServer_ignoreLogs shows you how to ignore all of the log messages written
|
|
||||||
// by a tsnet instance.
|
|
||||||
func ExampleServer_ignoreLogs() {
|
|
||||||
srv := &tsnet.Server{
|
|
||||||
Logf: func(string, ...any) {},
|
|
||||||
}
|
|
||||||
_ = srv
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExampleServer_ignoreLogsSometimes shows you how to ignore all of the log messages
|
// ExampleServer_ignoreLogsSometimes shows you how to ignore all of the log messages
|
||||||
// written by a tsnet instance, but allows you to opt-into them if a command-line
|
// written by a tsnet instance, but allows you to opt-into them if a command-line
|
||||||
// flag is set.
|
// flag is set.
|
||||||
@ -107,7 +98,6 @@ func ExampleServer_ignoreLogsSometimes() {
|
|||||||
|
|
||||||
srv := &tsnet.Server{
|
srv := &tsnet.Server{
|
||||||
Hostname: *hostname,
|
Hostname: *hostname,
|
||||||
Logf: func(string, ...any) {},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if *tsnetVerbose {
|
if *tsnetVerbose {
|
||||||
|
@ -89,8 +89,14 @@ type Server struct {
|
|||||||
// If empty, the binary name is used.
|
// If empty, the binary name is used.
|
||||||
Hostname string
|
Hostname string
|
||||||
|
|
||||||
// Logf, if non-nil, specifies the logger to use. By default,
|
// UserLogf, if non-nil, specifies the logger to use for logs generated by
|
||||||
// log.Printf is used.
|
// the Server itself intended to be seen by the user such as the AuthURL for
|
||||||
|
// login and status updates. If unset, log.Printf is used.
|
||||||
|
UserLogf logger.Logf
|
||||||
|
|
||||||
|
// Logf, if set is used for logs generated by the backend such as the
|
||||||
|
// LocalBackend and MagicSock. It is verbose and intended for debugging.
|
||||||
|
// If unset, logs are discarded.
|
||||||
Logf logger.Logf
|
Logf logger.Logf
|
||||||
|
|
||||||
// Ephemeral, if true, specifies that the instance should register
|
// Ephemeral, if true, specifies that the instance should register
|
||||||
@ -244,15 +250,15 @@ func (s *Server) Loopback() (addr string, proxyCred, localAPICred string, err er
|
|||||||
s.logf("localapi tcp serve error: %v", err)
|
s.logf("localapi tcp serve error: %v", err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
s5l := logger.WithPrefix(s.logf, "socks5: ")
|
||||||
s5s := &socks5.Server{
|
s5s := &socks5.Server{
|
||||||
Logf: logger.WithPrefix(s.logf, "socks5: "),
|
Logf: s5l,
|
||||||
Dialer: s.dialer.UserDial,
|
Dialer: s.dialer.UserDial,
|
||||||
Username: "tsnet",
|
Username: "tsnet",
|
||||||
Password: s.proxyCred,
|
Password: s.proxyCred,
|
||||||
}
|
}
|
||||||
go func() {
|
go func() {
|
||||||
s.logf("SOCKS5 server exited: %v", s5s.Serve(socksLn))
|
s5l("SOCKS5 server exited: %v", s5s.Serve(socksLn))
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -484,14 +490,12 @@ func (s *Server) start() (reterr error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logf := s.logf
|
|
||||||
|
|
||||||
if s.rootPath == "" {
|
if s.rootPath == "" {
|
||||||
confDir, err := os.UserConfigDir()
|
confDir, err := os.UserConfigDir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
s.rootPath, err = getTSNetDir(logf, confDir, prog)
|
s.rootPath, err = getTSNetDir(s.logf, confDir, prog)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -505,19 +509,29 @@ func (s *Server) start() (reterr error) {
|
|||||||
return fmt.Errorf("%v is not a directory", s.rootPath)
|
return fmt.Errorf("%v is not a directory", s.rootPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tsLogf := func(format string, a ...any) {
|
||||||
|
if s.logtail != nil {
|
||||||
|
s.logtail.Logf(format, a...)
|
||||||
|
}
|
||||||
|
if s.Logf == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.Logf(format, a...)
|
||||||
|
}
|
||||||
|
|
||||||
sys := new(tsd.System)
|
sys := new(tsd.System)
|
||||||
if err := s.startLogger(&closePool, sys.HealthTracker()); err != nil {
|
if err := s.startLogger(&closePool, sys.HealthTracker(), tsLogf); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
s.netMon, err = netmon.New(logf)
|
s.netMon, err = netmon.New(tsLogf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
closePool.add(s.netMon)
|
closePool.add(s.netMon)
|
||||||
|
|
||||||
s.dialer = &tsdial.Dialer{Logf: logf} // mutated below (before used)
|
s.dialer = &tsdial.Dialer{Logf: tsLogf} // mutated below (before used)
|
||||||
eng, err := wgengine.NewUserspaceEngine(logf, wgengine.Config{
|
eng, err := wgengine.NewUserspaceEngine(tsLogf, wgengine.Config{
|
||||||
ListenPort: s.Port,
|
ListenPort: s.Port,
|
||||||
NetMon: s.netMon,
|
NetMon: s.netMon,
|
||||||
Dialer: s.dialer,
|
Dialer: s.dialer,
|
||||||
@ -532,7 +546,7 @@ func (s *Server) start() (reterr error) {
|
|||||||
sys.Set(eng)
|
sys.Set(eng)
|
||||||
|
|
||||||
// TODO(oxtoacart): do we need to support Taildrive on tsnet, and if so, how?
|
// TODO(oxtoacart): do we need to support Taildrive on tsnet, and if so, how?
|
||||||
ns, err := netstack.Create(logf, sys.Tun.Get(), eng, sys.MagicSock.Get(), s.dialer, sys.DNSManager.Get(), sys.ProxyMapper(), nil)
|
ns, err := netstack.Create(tsLogf, sys.Tun.Get(), eng, sys.MagicSock.Get(), s.dialer, sys.DNSManager.Get(), sys.ProxyMapper(), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("netstack.Create: %w", err)
|
return fmt.Errorf("netstack.Create: %w", err)
|
||||||
}
|
}
|
||||||
@ -559,8 +573,8 @@ func (s *Server) start() (reterr error) {
|
|||||||
|
|
||||||
if s.Store == nil {
|
if s.Store == nil {
|
||||||
stateFile := filepath.Join(s.rootPath, "tailscaled.state")
|
stateFile := filepath.Join(s.rootPath, "tailscaled.state")
|
||||||
logf("tsnet running state path %s", stateFile)
|
s.logf("tsnet running state path %s", stateFile)
|
||||||
s.Store, err = store.New(logf, stateFile)
|
s.Store, err = store.New(tsLogf, stateFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -571,13 +585,13 @@ func (s *Server) start() (reterr error) {
|
|||||||
if s.Ephemeral {
|
if s.Ephemeral {
|
||||||
loginFlags = controlclient.LoginEphemeral
|
loginFlags = controlclient.LoginEphemeral
|
||||||
}
|
}
|
||||||
lb, err := ipnlocal.NewLocalBackend(logf, s.logid, sys, loginFlags|controlclient.LocalBackendStartKeyOSNeutral)
|
lb, err := ipnlocal.NewLocalBackend(tsLogf, s.logid, sys, loginFlags|controlclient.LocalBackendStartKeyOSNeutral)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("NewLocalBackend: %v", err)
|
return fmt.Errorf("NewLocalBackend: %v", err)
|
||||||
}
|
}
|
||||||
lb.SetTCPHandlerForFunnelFlow(s.getTCPHandlerForFunnelFlow)
|
lb.SetTCPHandlerForFunnelFlow(s.getTCPHandlerForFunnelFlow)
|
||||||
lb.SetVarRoot(s.rootPath)
|
lb.SetVarRoot(s.rootPath)
|
||||||
logf("tsnet starting with hostname %q, varRoot %q", s.hostname, s.rootPath)
|
s.logf("tsnet starting with hostname %q, varRoot %q", s.hostname, s.rootPath)
|
||||||
s.lb = lb
|
s.lb = lb
|
||||||
if err := ns.Start(lb); err != nil {
|
if err := ns.Start(lb); err != nil {
|
||||||
return fmt.Errorf("failed to start netstack: %w", err)
|
return fmt.Errorf("failed to start netstack: %w", err)
|
||||||
@ -598,17 +612,17 @@ func (s *Server) start() (reterr error) {
|
|||||||
}
|
}
|
||||||
st := lb.State()
|
st := lb.State()
|
||||||
if st == ipn.NeedsLogin || envknob.Bool("TSNET_FORCE_LOGIN") {
|
if st == ipn.NeedsLogin || envknob.Bool("TSNET_FORCE_LOGIN") {
|
||||||
logf("LocalBackend state is %v; running StartLoginInteractive...", st)
|
s.logf("LocalBackend state is %v; running StartLoginInteractive...", st)
|
||||||
if err := s.lb.StartLoginInteractive(s.shutdownCtx); err != nil {
|
if err := s.lb.StartLoginInteractive(s.shutdownCtx); err != nil {
|
||||||
return fmt.Errorf("StartLoginInteractive: %w", err)
|
return fmt.Errorf("StartLoginInteractive: %w", err)
|
||||||
}
|
}
|
||||||
} else if authKey != "" {
|
} else if authKey != "" {
|
||||||
logf("Authkey is set; but state is %v. Ignoring authkey. Re-run with TSNET_FORCE_LOGIN=1 to force use of authkey.", st)
|
s.logf("Authkey is set; but state is %v. Ignoring authkey. Re-run with TSNET_FORCE_LOGIN=1 to force use of authkey.", st)
|
||||||
}
|
}
|
||||||
go s.printAuthURLLoop()
|
go s.printAuthURLLoop()
|
||||||
|
|
||||||
// Run the localapi handler, to allow fetching LetsEncrypt certs.
|
// Run the localapi handler, to allow fetching LetsEncrypt certs.
|
||||||
lah := localapi.NewHandler(lb, logf, s.logid)
|
lah := localapi.NewHandler(lb, tsLogf, s.logid)
|
||||||
lah.PermitWrite = true
|
lah.PermitWrite = true
|
||||||
lah.PermitRead = true
|
lah.PermitRead = true
|
||||||
|
|
||||||
@ -621,14 +635,14 @@ func (s *Server) start() (reterr error) {
|
|||||||
s.lb.ConfigureWebClient(s.localClient)
|
s.lb.ConfigureWebClient(s.localClient)
|
||||||
go func() {
|
go func() {
|
||||||
if err := s.localAPIServer.Serve(lal); err != nil {
|
if err := s.localAPIServer.Serve(lal); err != nil {
|
||||||
logf("localapi serve error: %v", err)
|
s.logf("localapi serve error: %v", err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
closePool.add(s.localAPIListener)
|
closePool.add(s.localAPIListener)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) startLogger(closePool *closeOnErrorPool, health *health.Tracker) error {
|
func (s *Server) startLogger(closePool *closeOnErrorPool, health *health.Tracker, tsLogf logger.Logf) error {
|
||||||
if testenv.InTest() {
|
if testenv.InTest() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -659,10 +673,10 @@ func (s *Server) startLogger(closePool *closeOnErrorPool, health *health.Tracker
|
|||||||
Stderr: io.Discard, // log everything to Buffer
|
Stderr: io.Discard, // log everything to Buffer
|
||||||
Buffer: s.logbuffer,
|
Buffer: s.logbuffer,
|
||||||
CompressLogs: true,
|
CompressLogs: true,
|
||||||
HTTPC: &http.Client{Transport: logpolicy.NewLogtailTransport(logtail.DefaultHost, s.netMon, health, s.logf)},
|
HTTPC: &http.Client{Transport: logpolicy.NewLogtailTransport(logtail.DefaultHost, s.netMon, health, tsLogf)},
|
||||||
MetricsDelta: clientmetric.EncodeLogTailMetricsDelta,
|
MetricsDelta: clientmetric.EncodeLogTailMetricsDelta,
|
||||||
}
|
}
|
||||||
s.logtail = logtail.NewLogger(c, s.logf)
|
s.logtail = logtail.NewLogger(c, tsLogf)
|
||||||
closePool.addFunc(func() { s.logtail.Shutdown(context.Background()) })
|
closePool.addFunc(func() { s.logtail.Shutdown(context.Background()) })
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -683,8 +697,8 @@ func (s *Server) logf(format string, a ...any) {
|
|||||||
if s.logtail != nil {
|
if s.logtail != nil {
|
||||||
s.logtail.Logf(format, a...)
|
s.logtail.Logf(format, a...)
|
||||||
}
|
}
|
||||||
if s.Logf != nil {
|
if s.UserLogf != nil {
|
||||||
s.Logf(format, a...)
|
s.UserLogf(format, a...)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.Printf(format, a...)
|
log.Printf(format, a...)
|
||||||
@ -697,8 +711,8 @@ func (s *Server) printAuthURLLoop() {
|
|||||||
if s.shutdownCtx.Err() != nil {
|
if s.shutdownCtx.Err() != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if st := s.lb.State(); st != ipn.NeedsLogin {
|
if st := s.lb.State(); st != ipn.NeedsLogin && st != ipn.NoState {
|
||||||
s.logf("printAuthURLLoop: state is %v; stopping", st)
|
s.logf("AuthLoop: state is %v; done", st)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
st := s.lb.StatusWithoutPeers()
|
st := s.lb.StatusWithoutPeers()
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"log"
|
||||||
"math/big"
|
"math/big"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -214,8 +215,8 @@ func startServer(t *testing.T, ctx context.Context, controlURL, hostname string)
|
|||||||
Ephemeral: true,
|
Ephemeral: true,
|
||||||
getCertForTesting: testCertRoot.getCert,
|
getCertForTesting: testCertRoot.getCert,
|
||||||
}
|
}
|
||||||
if !*verboseNodes {
|
if *verboseNodes {
|
||||||
s.Logf = logger.Discard
|
s.Logf = log.Printf
|
||||||
}
|
}
|
||||||
t.Cleanup(func() { s.Close() })
|
t.Cleanup(func() { s.Close() })
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user