diff --git a/awsuploader/directory_upload_manager.go b/awsuploader/directory_upload_manager.go index c679f76e..4cafd984 100644 --- a/awsuploader/directory_upload_manager.go +++ b/awsuploader/directory_upload_manager.go @@ -5,12 +5,12 @@ import ( "path/filepath" "time" - "github.com/sirupsen/logrus" + "github.com/cloudflare/cloudflared/logger" ) // DirectoryUploadManager is used to manage file uploads on an interval from a directory type DirectoryUploadManager struct { - logger *logrus.Logger + logger logger.Service uploader Uploader rootDirectory string sweepInterval time.Duration @@ -23,7 +23,7 @@ type DirectoryUploadManager struct { // uploader is an Uploader to use as an actual uploading engine // directory is the directory to sweep for files to upload // sweepInterval is how often to iterate the directory and upload the files within -func NewDirectoryUploadManager(logger *logrus.Logger, uploader Uploader, directory string, sweepInterval time.Duration, shutdownC chan struct{}) *DirectoryUploadManager { +func NewDirectoryUploadManager(logger logger.Service, uploader Uploader, directory string, sweepInterval time.Duration, shutdownC chan struct{}) *DirectoryUploadManager { workerCount := 10 manager := &DirectoryUploadManager{ logger: logger, @@ -97,7 +97,7 @@ func (m *DirectoryUploadManager) worker() { return case filepath := <-m.workQueue: if err := m.Upload(filepath); err != nil { - m.logger.WithError(err).Error("Cannot upload file to s3 bucket") + m.logger.Errorf("Cannot upload file to s3 bucket: %s", err) } else { os.Remove(filepath) } diff --git a/awsuploader/directory_upload_manager_test.go b/awsuploader/directory_upload_manager_test.go index 5e9e8852..67b5d94e 100644 --- a/awsuploader/directory_upload_manager_test.go +++ b/awsuploader/directory_upload_manager_test.go @@ -9,7 +9,7 @@ import ( "testing" "time" - "github.com/sirupsen/logrus" + "github.com/cloudflare/cloudflared/logger" ) type MockUploader struct { @@ -49,7 +49,7 @@ func setupTestDirectory(t *testing.T) string { func createUploadManager(t *testing.T, shouldFailUpload bool) *DirectoryUploadManager { rootDirectory := setupTestDirectory(t) uploader := NewMockUploader(shouldFailUpload) - logger := logrus.New() + logger := logger.NewOutputWriter(logger.NewMockWriteManager()) shutdownC := make(chan struct{}) return NewDirectoryUploadManager(logger, uploader, rootDirectory, 1*time.Second, shutdownC) } diff --git a/carrier/carrier.go b/carrier/carrier.go index b36a0e51..1ef5a032 100644 --- a/carrier/carrier.go +++ b/carrier/carrier.go @@ -12,6 +12,7 @@ import ( "github.com/cloudflare/cloudflared/cmd/cloudflared/token" "github.com/cloudflare/cloudflared/h2mux" + "github.com/cloudflare/cloudflared/logger" "github.com/pkg/errors" ) @@ -129,13 +130,13 @@ func IsAccessResponse(resp *http.Response) bool { } // BuildAccessRequest builds an HTTP request with the Access token set -func BuildAccessRequest(options *StartOptions) (*http.Request, error) { +func BuildAccessRequest(options *StartOptions, logger logger.Service) (*http.Request, error) { req, err := http.NewRequest(http.MethodGet, options.OriginURL, nil) if err != nil { return nil, err } - token, err := token.FetchToken(req.URL) + token, err := token.FetchToken(req.URL, logger) if err != nil { return nil, err } diff --git a/carrier/carrier_test.go b/carrier/carrier_test.go index 306f4980..e5b4dc6f 100644 --- a/carrier/carrier_test.go +++ b/carrier/carrier_test.go @@ -9,8 +9,8 @@ import ( "sync" "testing" + "github.com/cloudflare/cloudflared/logger" ws "github.com/gorilla/websocket" - "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" ) @@ -43,7 +43,7 @@ func (s *testStreamer) Write(p []byte) (int, error) { func TestStartClient(t *testing.T) { message := "Good morning Austin! Time for another sunny day in the great state of Texas." - logger := logrus.New() + logger := logger.NewOutputWriter(logger.NewMockWriteManager()) wsConn := NewWSConnection(logger, false) ts := newTestWebSocketServer() defer ts.Close() @@ -68,7 +68,7 @@ func TestStartServer(t *testing.T) { t.Fatalf("Error starting listener: %v", err) } message := "Good morning Austin! Time for another sunny day in the great state of Texas." - logger := logrus.New() + logger := logger.NewOutputWriter(logger.NewMockWriteManager()) shutdownC := make(chan struct{}) wsConn := NewWSConnection(logger, false) ts := newTestWebSocketServer() diff --git a/carrier/websocket.go b/carrier/websocket.go index 3ea81d4c..e804f7bb 100644 --- a/carrier/websocket.go +++ b/carrier/websocket.go @@ -7,16 +7,16 @@ import ( "net/http" "github.com/cloudflare/cloudflared/cmd/cloudflared/token" + "github.com/cloudflare/cloudflared/logger" "github.com/cloudflare/cloudflared/socks" cfwebsocket "github.com/cloudflare/cloudflared/websocket" "github.com/gorilla/websocket" - "github.com/sirupsen/logrus" ) // Websocket is used to carry data via WS binary frames over the tunnel from client to the origin // This implements the functions for glider proxy (sock5) and the carrier interface type Websocket struct { - logger *logrus.Logger + logger logger.Service isSocks bool } @@ -35,7 +35,7 @@ func (d *wsdialer) Dial(address string) (io.ReadWriteCloser, *socks.AddrSpec, er } // NewWSConnection returns a new connection object -func NewWSConnection(logger *logrus.Logger, isSocks bool) Connection { +func NewWSConnection(logger logger.Service, isSocks bool) Connection { return &Websocket{ logger: logger, isSocks: isSocks, @@ -45,9 +45,9 @@ func NewWSConnection(logger *logrus.Logger, isSocks bool) Connection { // ServeStream will create a Websocket client stream connection to the edge // it blocks and writes the raw data from conn over the tunnel func (ws *Websocket) ServeStream(options *StartOptions, conn io.ReadWriter) error { - wsConn, err := createWebsocketStream(options) + wsConn, err := createWebsocketStream(options, ws.logger) if err != nil { - ws.logger.WithError(err).Errorf("failed to connect to %s", options.OriginURL) + ws.logger.Errorf("failed to connect to %s with error: %s", options.OriginURL, err) return err } defer wsConn.Close() @@ -73,7 +73,7 @@ func (ws *Websocket) StartServer(listener net.Listener, remote string, shutdownC // createWebsocketStream will create a WebSocket connection to stream data over // It also handles redirects from Access and will present that flow if // the token is not present on the request -func createWebsocketStream(options *StartOptions) (*cfwebsocket.Conn, error) { +func createWebsocketStream(options *StartOptions, logger logger.Service) (*cfwebsocket.Conn, error) { req, err := http.NewRequest(http.MethodGet, options.OriginURL, nil) if err != nil { return nil, err @@ -83,7 +83,7 @@ func createWebsocketStream(options *StartOptions) (*cfwebsocket.Conn, error) { wsConn, resp, err := cfwebsocket.ClientConnect(req, nil) defer closeRespBody(resp) if err != nil && IsAccessResponse(resp) { - wsConn, err = createAccessAuthenticatedStream(options) + wsConn, err = createAccessAuthenticatedStream(options, logger) if err != nil { return nil, err } @@ -99,8 +99,8 @@ func createWebsocketStream(options *StartOptions) (*cfwebsocket.Conn, error) { // this probably means the token in storage is invalid (expired/revoked). If that // happens it deletes the token and runs the connection again, so the user can // login again and generate a new one. -func createAccessAuthenticatedStream(options *StartOptions) (*websocket.Conn, error) { - wsConn, resp, err := createAccessWebSocketStream(options) +func createAccessAuthenticatedStream(options *StartOptions, logger logger.Service) (*websocket.Conn, error) { + wsConn, resp, err := createAccessWebSocketStream(options, logger) defer closeRespBody(resp) if err == nil { return wsConn, nil @@ -118,7 +118,7 @@ func createAccessAuthenticatedStream(options *StartOptions) (*websocket.Conn, er if err := token.RemoveTokenIfExists(originReq.URL); err != nil { return nil, err } - wsConn, resp, err = createAccessWebSocketStream(options) + wsConn, resp, err = createAccessWebSocketStream(options, logger) defer closeRespBody(resp) if err != nil { return nil, err @@ -128,8 +128,8 @@ func createAccessAuthenticatedStream(options *StartOptions) (*websocket.Conn, er } // createAccessWebSocketStream builds an Access request and makes a connection -func createAccessWebSocketStream(options *StartOptions) (*websocket.Conn, *http.Response, error) { - req, err := BuildAccessRequest(options) +func createAccessWebSocketStream(options *StartOptions, logger logger.Service) (*websocket.Conn, *http.Response, error) { + req, err := BuildAccessRequest(options, logger) if err != nil { return nil, nil, err } diff --git a/cmd/cloudflared/access/carrier.go b/cmd/cloudflared/access/carrier.go index c258f85b..337792ab 100644 --- a/cmd/cloudflared/access/carrier.go +++ b/cmd/cloudflared/access/carrier.go @@ -8,22 +8,23 @@ import ( "github.com/cloudflare/cloudflared/carrier" "github.com/cloudflare/cloudflared/cmd/cloudflared/config" "github.com/cloudflare/cloudflared/h2mux" + "github.com/cloudflare/cloudflared/logger" "github.com/cloudflare/cloudflared/validation" "github.com/pkg/errors" cli "gopkg.in/urfave/cli.v2" ) // StartForwarder starts a client side websocket forward -func StartForwarder(forwarder config.Forwarder, shutdown <-chan struct{}) error { +func StartForwarder(forwarder config.Forwarder, shutdown <-chan struct{}, logger logger.Service) error { validURLString, err := validation.ValidateUrl(forwarder.Listener) if err != nil { - logger.WithError(err).Error("Error validating origin URL") + logger.Errorf("Error validating origin URL: %s", err) return errors.Wrap(err, "error validating origin URL") } validURL, err := url.Parse(validURLString) if err != nil { - logger.WithError(err).Error("Error parsing origin URL") + logger.Errorf("Error parsing origin URL: %s", err) return errors.Wrap(err, "error parsing origin URL") } @@ -44,6 +45,12 @@ func StartForwarder(forwarder config.Forwarder, shutdown <-chan struct{}) error // useful for proxying other protocols (like ssh) over websockets // (which you can put Access in front of) func ssh(c *cli.Context) error { + logDirectory, logLevel := config.FindLogSettings() + logger, err := logger.New(logger.DefaultFile(logDirectory), logger.LogLevelString(logLevel)) + if err != nil { + return errors.Wrap(err, "error setting up logger") + } + // get the hostname from the cmdline and error out if its not provided rawHostName := c.String(sshHostnameFlag) hostname, err := validation.ValidateHostname(rawHostName) @@ -77,17 +84,21 @@ func ssh(c *cli.Context) error { if c.NArg() > 0 || c.IsSet(sshURLFlag) { localForwarder, err := config.ValidateUrl(c) if err != nil { - logger.WithError(err).Error("Error validating origin URL") + logger.Errorf("Error validating origin URL: %s", err) return errors.Wrap(err, "error validating origin URL") } forwarder, err := url.Parse(localForwarder) if err != nil { - logger.WithError(err).Error("Error validating origin URL") + logger.Errorf("Error validating origin URL: %s", err) return errors.Wrap(err, "error validating origin URL") } logger.Infof("Start Websocket listener on: %s", forwarder.Host) - return carrier.StartForwarder(wsConn, forwarder.Host, shutdownC, options) + err = carrier.StartForwarder(wsConn, forwarder.Host, shutdownC, options) + if err != nil { + logger.Errorf("Error on Websocket listener: %s", err) + } + return err } return carrier.StartClient(wsConn, &carrier.StdinoutStream{}, options) diff --git a/cmd/cloudflared/access/cmd.go b/cmd/cloudflared/access/cmd.go index 7f4f1abb..96359c7c 100644 --- a/cmd/cloudflared/access/cmd.go +++ b/cmd/cloudflared/access/cmd.go @@ -14,12 +14,12 @@ import ( "github.com/cloudflare/cloudflared/cmd/cloudflared/shell" "github.com/cloudflare/cloudflared/cmd/cloudflared/token" "github.com/cloudflare/cloudflared/h2mux" + "github.com/cloudflare/cloudflared/logger" "github.com/cloudflare/cloudflared/sshgen" "github.com/cloudflare/cloudflared/validation" "github.com/pkg/errors" "golang.org/x/net/idna" - "github.com/cloudflare/cloudflared/log" "github.com/getsentry/raven-go" "gopkg.in/urfave/cli.v2" ) @@ -53,7 +53,6 @@ Host cfpipe-{{.Hostname}} const sentryDSN = "https://56a9c9fa5c364ab28f34b14f35ea0f1b@sentry.io/189878" var ( - logger = log.CreateLogger() shutdownC chan struct{} graceShutdownC chan struct{} ) @@ -195,7 +194,12 @@ func login(c *cli.Context) error { if err := raven.SetDSN(sentryDSN); err != nil { return err } - logger := log.CreateLogger() + + logger, err := logger.New() + if err != nil { + return errors.Wrap(err, "error setting up logger") + } + args := c.Args() rawURL := ensureURLScheme(args.First()) appURL, err := url.Parse(rawURL) @@ -203,8 +207,8 @@ func login(c *cli.Context) error { logger.Errorf("Please provide the url of the Access application\n") return err } - if err := verifyTokenAtEdge(appURL, c); err != nil { - logger.WithError(err).Error("Could not verify token") + if err := verifyTokenAtEdge(appURL, c, logger); err != nil { + logger.Errorf("Could not verify token: %s", err) return err } @@ -236,7 +240,11 @@ func curl(c *cli.Context) error { if err := raven.SetDSN(sentryDSN); err != nil { return err } - logger := log.CreateLogger() + logger, err := logger.New() + if err != nil { + return errors.Wrap(err, "error setting up logger") + } + args := c.Args() if args.Len() < 1 { logger.Error("Please provide the access app and command you wish to run.") @@ -244,7 +252,7 @@ func curl(c *cli.Context) error { } cmdArgs, allowRequest := parseAllowRequest(args.Slice()) - appURL, err := getAppURL(cmdArgs) + appURL, err := getAppURL(cmdArgs, logger) if err != nil { return err } @@ -252,12 +260,12 @@ func curl(c *cli.Context) error { tok, err := token.GetTokenIfExists(appURL) if err != nil || tok == "" { if allowRequest { - logger.Warn("You don't have an Access token set. Please run access token to fetch one.") + logger.Info("You don't have an Access token set. Please run access token to fetch one.") return shell.Run("curl", cmdArgs...) } - tok, err = token.FetchToken(appURL) + tok, err = token.FetchToken(appURL, logger) if err != nil { - logger.Error("Failed to refresh token: ", err) + logger.Errorf("Failed to refresh token: %s", err) return err } } @@ -311,6 +319,11 @@ func sshConfig(c *cli.Context) error { // sshGen generates a short lived certificate for provided hostname func sshGen(c *cli.Context) error { + logger, err := logger.New() + if err != nil { + return errors.Wrap(err, "error setting up logger") + } + // get the hostname from the cmdline and error out if its not provided rawHostName := c.String(sshHostnameFlag) hostname, err := validation.ValidateHostname(rawHostName) @@ -326,7 +339,7 @@ func sshGen(c *cli.Context) error { // this fetchToken function mutates the appURL param. We should refactor that fetchTokenURL := &url.URL{} *fetchTokenURL = *originURL - cfdToken, err := token.FetchToken(fetchTokenURL) + cfdToken, err := token.FetchToken(fetchTokenURL, logger) if err != nil { return err } @@ -339,7 +352,7 @@ func sshGen(c *cli.Context) error { } // getAppURL will pull the appURL needed for fetching a user's Access token -func getAppURL(cmdArgs []string) (*url.URL, error) { +func getAppURL(cmdArgs []string, logger logger.Service) (*url.URL, error) { if len(cmdArgs) < 1 { logger.Error("Please provide a valid URL as the first argument to curl.") return nil, errors.New("not a valid url") @@ -413,7 +426,7 @@ func isFileThere(candidate string) bool { // verifyTokenAtEdge checks for a token on disk, or generates a new one. // Then makes a request to to the origin with the token to ensure it is valid. // Returns nil if token is valid. -func verifyTokenAtEdge(appUrl *url.URL, c *cli.Context) error { +func verifyTokenAtEdge(appUrl *url.URL, c *cli.Context, logger logger.Service) error { headers := buildRequestHeaders(c.StringSlice(sshHeaderFlag)) if c.IsSet(sshTokenIDFlag) { headers.Add(h2mux.CFAccessClientIDHeader, c.String(sshTokenIDFlag)) @@ -423,7 +436,7 @@ func verifyTokenAtEdge(appUrl *url.URL, c *cli.Context) error { } options := &carrier.StartOptions{OriginURL: appUrl.String(), Headers: headers} - if valid, err := isTokenValid(options); err != nil { + if valid, err := isTokenValid(options, logger); err != nil { return err } else if valid { return nil @@ -433,7 +446,7 @@ func verifyTokenAtEdge(appUrl *url.URL, c *cli.Context) error { return err } - if valid, err := isTokenValid(options); err != nil { + if valid, err := isTokenValid(options, logger); err != nil { return err } else if !valid { return errors.New("failed to verify token") @@ -443,8 +456,8 @@ func verifyTokenAtEdge(appUrl *url.URL, c *cli.Context) error { } // isTokenValid makes a request to the origin and returns true if the response was not a 302. -func isTokenValid(options *carrier.StartOptions) (bool, error) { - req, err := carrier.BuildAccessRequest(options) +func isTokenValid(options *carrier.StartOptions, logger logger.Service) (bool, error) { + req, err := carrier.BuildAccessRequest(options, logger) if err != nil { return false, errors.Wrap(err, "Could not create access request") } diff --git a/cmd/cloudflared/app_forward_service.go b/cmd/cloudflared/app_forward_service.go index 4c8446e2..f815b5c7 100644 --- a/cmd/cloudflared/app_forward_service.go +++ b/cmd/cloudflared/app_forward_service.go @@ -3,6 +3,7 @@ package main import ( "github.com/cloudflare/cloudflared/cmd/cloudflared/access" "github.com/cloudflare/cloudflared/cmd/cloudflared/config" + "github.com/cloudflare/cloudflared/logger" ) // ForwardServiceType is used to identify what kind of overwatch service this is @@ -14,11 +15,12 @@ const ForwardServiceType = "forward" type ForwarderService struct { forwarder config.Forwarder shutdown chan struct{} + logger logger.Service } // NewForwardService creates a new forwarder service -func NewForwardService(f config.Forwarder) *ForwarderService { - return &ForwarderService{forwarder: f, shutdown: make(chan struct{}, 1)} +func NewForwardService(f config.Forwarder, logger logger.Service) *ForwarderService { + return &ForwarderService{forwarder: f, shutdown: make(chan struct{}, 1), logger: logger} } // Name is used to figure out this service is related to the others (normally the addr it binds to) @@ -44,5 +46,5 @@ func (s *ForwarderService) Shutdown() { // Run is the run loop that is started by the overwatch service func (s *ForwarderService) Run() error { - return access.StartForwarder(s.forwarder, s.shutdown) + return access.StartForwarder(s.forwarder, s.shutdown, s.logger) } diff --git a/cmd/cloudflared/app_resolver_service.go b/cmd/cloudflared/app_resolver_service.go index f98ca4c8..1ba19f17 100644 --- a/cmd/cloudflared/app_resolver_service.go +++ b/cmd/cloudflared/app_resolver_service.go @@ -2,8 +2,8 @@ package main import ( "github.com/cloudflare/cloudflared/cmd/cloudflared/config" + "github.com/cloudflare/cloudflared/logger" "github.com/cloudflare/cloudflared/tunneldns" - "github.com/sirupsen/logrus" ) // ResolverServiceType is used to identify what kind of overwatch service this is @@ -15,11 +15,11 @@ const ResolverServiceType = "resolver" type ResolverService struct { resolver config.DNSResolver shutdown chan struct{} - logger *logrus.Logger + logger logger.Service } // NewResolverService creates a new resolver service -func NewResolverService(r config.DNSResolver, logger *logrus.Logger) *ResolverService { +func NewResolverService(r config.DNSResolver, logger logger.Service) *ResolverService { return &ResolverService{resolver: r, shutdown: make(chan struct{}), logger: logger, @@ -51,7 +51,7 @@ func (s *ResolverService) Shutdown() { func (s *ResolverService) Run() error { // create a listener l, err := tunneldns.CreateListener(s.resolver.AddressOrDefault(), s.resolver.PortOrDefault(), - s.resolver.UpstreamsOrDefault(), s.resolver.BootstrapsOrDefault()) + s.resolver.UpstreamsOrDefault(), s.resolver.BootstrapsOrDefault(), s.logger) if err != nil { return err } diff --git a/cmd/cloudflared/app_service.go b/cmd/cloudflared/app_service.go index 9407d6e2..d2c0b0c3 100644 --- a/cmd/cloudflared/app_service.go +++ b/cmd/cloudflared/app_service.go @@ -2,8 +2,8 @@ package main import ( "github.com/cloudflare/cloudflared/cmd/cloudflared/config" + "github.com/cloudflare/cloudflared/logger" "github.com/cloudflare/cloudflared/overwatch" - "github.com/sirupsen/logrus" ) // AppService is the main service that runs when no command lines flags are passed to cloudflared @@ -13,11 +13,11 @@ type AppService struct { serviceManager overwatch.Manager shutdownC chan struct{} configUpdateChan chan config.Root - logger *logrus.Logger + logger logger.Service } // NewAppService creates a new AppService with needed supporting services -func NewAppService(configManager config.Manager, serviceManager overwatch.Manager, shutdownC chan struct{}, logger *logrus.Logger) *AppService { +func NewAppService(configManager config.Manager, serviceManager overwatch.Manager, shutdownC chan struct{}, logger logger.Service) *AppService { return &AppService{ configManager: configManager, serviceManager: serviceManager, @@ -66,7 +66,7 @@ func (s *AppService) handleConfigUpdate(c config.Root) { // handle the client forward listeners activeServices := map[string]struct{}{} for _, f := range c.Forwarders { - service := NewForwardService(f) + service := NewForwardService(f, s.logger) s.serviceManager.Add(service) activeServices[service.Name()] = struct{}{} } diff --git a/cmd/cloudflared/buildinfo/build_info.go b/cmd/cloudflared/buildinfo/build_info.go index 80481716..aedface1 100644 --- a/cmd/cloudflared/buildinfo/build_info.go +++ b/cmd/cloudflared/buildinfo/build_info.go @@ -3,7 +3,7 @@ package buildinfo import ( "runtime" - "github.com/sirupsen/logrus" + "github.com/cloudflare/cloudflared/logger" ) type BuildInfo struct { @@ -22,7 +22,7 @@ func GetBuildInfo(cloudflaredVersion string) *BuildInfo { } } -func (bi *BuildInfo) Log(logger *logrus.Logger) { +func (bi *BuildInfo) Log(logger logger.Service) { logger.Infof("Version %s", bi.CloudflaredVersion) logger.Infof("GOOS: %s, GOVersion: %s, GoArch: %s", bi.GoOS, bi.GoVersion, bi.GoArch) } diff --git a/cmd/cloudflared/config/configuration.go b/cmd/cloudflared/config/configuration.go index cb497dc2..9512782f 100644 --- a/cmd/cloudflared/config/configuration.go +++ b/cmd/cloudflared/config/configuration.go @@ -9,6 +9,7 @@ import ( homedir "github.com/mitchellh/go-homedir" "gopkg.in/urfave/cli.v2" "gopkg.in/urfave/cli.v2/altsrc" + "gopkg.in/yaml.v2" ) var ( @@ -63,6 +64,35 @@ func FindDefaultConfigPath() string { return "" } +// FindLogSettings gets the log directory and level from the config file +func FindLogSettings() (string, string) { + configPath := FindDefaultConfigPath() + defaultDirectory := filepath.Dir(configPath) + defaultLevel := "info" + + file, err := os.Open(configPath) + if err != nil { + return defaultDirectory, defaultLevel + } + defer file.Close() + + var config Root + if err := yaml.NewDecoder(file).Decode(&config); err != nil { + return defaultDirectory, defaultLevel + } + + directory := defaultDirectory + if config.LogDirectory != "" { + directory = config.LogDirectory + } + + level := defaultLevel + if config.LogLevel != "" { + level = config.LogLevel + } + return directory, level +} + // ValidateUnixSocket ensures --unix-socket param is used exclusively // i.e. it fails if a user specifies both --url and --unix-socket func ValidateUnixSocket(c *cli.Context) (string, error) { diff --git a/cmd/cloudflared/config/manager.go b/cmd/cloudflared/config/manager.go index 8723b70d..a286ab48 100644 --- a/cmd/cloudflared/config/manager.go +++ b/cmd/cloudflared/config/manager.go @@ -4,9 +4,8 @@ import ( "errors" "os" + "github.com/cloudflare/cloudflared/logger" "github.com/cloudflare/cloudflared/watcher" - - "github.com/sirupsen/logrus" "gopkg.in/yaml.v2" ) @@ -27,12 +26,12 @@ type FileManager struct { watcher watcher.Notifier notifier Notifier configPath string - logger *logrus.Logger + logger logger.Service ReadConfig func(string) (Root, error) } // NewFileManager creates a config manager -func NewFileManager(watcher watcher.Notifier, configPath string, logger *logrus.Logger) (*FileManager, error) { +func NewFileManager(watcher watcher.Notifier, configPath string, logger logger.Service) (*FileManager, error) { m := &FileManager{ watcher: watcher, configPath: configPath, @@ -94,7 +93,7 @@ func readConfigFromPath(configPath string) (Root, error) { func (m *FileManager) WatcherItemDidChange(filepath string) { config, err := m.GetConfig() if err != nil { - m.logger.WithError(err).Error("Failed to read new config") + m.logger.Errorf("Failed to read new config: %s", err) return } m.logger.Info("Config file has been updated") @@ -103,5 +102,5 @@ func (m *FileManager) WatcherItemDidChange(filepath string) { // WatcherDidError notifies of errors with the file watcher func (m *FileManager) WatcherDidError(err error) { - m.logger.WithError(err).Error("Config watcher encountered an error") + m.logger.Errorf("Config watcher encountered an error: %s", err) } diff --git a/cmd/cloudflared/config/manager_test.go b/cmd/cloudflared/config/manager_test.go index 419976aa..d64675af 100644 --- a/cmd/cloudflared/config/manager_test.go +++ b/cmd/cloudflared/config/manager_test.go @@ -4,7 +4,7 @@ import ( "os" "testing" - "github.com/cloudflare/cloudflared/log" + "github.com/cloudflare/cloudflared/logger" "github.com/cloudflare/cloudflared/watcher" "github.com/stretchr/testify/assert" ) @@ -65,7 +65,8 @@ func TestConfigChanged(t *testing.T) { wait := make(chan struct{}) w := &mockFileWatcher{path: filePath, ready: wait} - logger := log.CreateLogger() + logger := logger.NewOutputWriter(logger.NewMockWriteManager()) + service, err := NewFileManager(w, filePath, logger) service.ReadConfig = configRead assert.NoError(t, err) diff --git a/cmd/cloudflared/config/model.go b/cmd/cloudflared/config/model.go index 95d266e8..015afef9 100644 --- a/cmd/cloudflared/config/model.go +++ b/cmd/cloudflared/config/model.go @@ -23,20 +23,22 @@ type Tunnel struct { // DNSResolver represents a client side DNS resolver type DNSResolver struct { Enabled bool `json:"enabled"` - Address string `json:"address"` - Port uint16 `json:"port"` - Upstreams []string `json:"upstreams"` - Bootstraps []string `json:"bootstraps"` + Address string `json:"address,omitempty"` + Port uint16 `json:"port,omitempty"` + Upstreams []string `json:"upstreams,omitempty"` + Bootstraps []string `json:"bootstraps,omitempty"` } // Root is the base options to configure the service type Root struct { - OrgKey string `json:"org_key"` + OrgKey string `json:"org_key" yaml:"orgKey"` ConfigType string `json:"type"` - CheckinInterval int `json:"checkin_interval"` + LogDirectory string `json:"log_directory" yaml:"logDirectory,omitempty"` + LogLevel string `json:"log_level" yaml:"logLevel"` + CheckinInterval int `json:"checkin_interval" yaml:"checkinInterval"` Forwarders []Forwarder `json:"forwarders,omitempty"` Tunnels []Tunnel `json:"tunnels,omitempty"` - Resolver DNSResolver `json:"resolver"` + Resolver DNSResolver `json:"resolver,omitempty"` } // Hash returns the computed values to see if the forwarder values change diff --git a/cmd/cloudflared/linux_service.go b/cmd/cloudflared/linux_service.go index 62559bf4..43d4ca83 100644 --- a/cmd/cloudflared/linux_service.go +++ b/cmd/cloudflared/linux_service.go @@ -8,6 +8,8 @@ import ( "path/filepath" "github.com/cloudflare/cloudflared/cmd/cloudflared/config" + "github.com/cloudflare/cloudflared/logger" + "github.com/pkg/errors" cli "gopkg.in/urfave/cli.v2" ) @@ -178,7 +180,7 @@ func isSystemd() bool { return false } -func copyUserConfiguration(userConfigDir, userConfigFile, userCredentialFile string) error { +func copyUserConfiguration(userConfigDir, userConfigFile, userCredentialFile string, logger logger.Service) error { if err := ensureConfigDirExists(serviceConfigDir); err != nil { return err } @@ -192,10 +194,16 @@ func copyUserConfiguration(userConfigDir, userConfigFile, userCredentialFile str if err := copyConfig(srcConfigPath, destConfigPath); err != nil { return err } + logger.Infof("Copied %s to %s", srcConfigPath, destConfigPath) return nil } func installLinuxService(c *cli.Context) error { + logger, err := logger.New() + if err != nil { + return errors.Wrap(err, "error setting up logger") + } + etPath, err := os.Executable() if err != nil { return fmt.Errorf("error determining executable path: %v", err) @@ -205,8 +213,8 @@ func installLinuxService(c *cli.Context) error { userConfigDir := filepath.Dir(c.String("config")) userConfigFile := filepath.Base(c.String("config")) userCredentialFile := config.DefaultCredentialFile - if err = copyUserConfiguration(userConfigDir, userConfigFile, userCredentialFile); err != nil { - logger.WithError(err).Infof("Failed to copy user configuration. Before running the service, ensure that %s contains two files, %s and %s", + if err = copyUserConfiguration(userConfigDir, userConfigFile, userCredentialFile, logger); err != nil { + logger.Errorf("Failed to copy user configuration: %s. Before running the service, ensure that %s contains two files, %s and %s", err, serviceConfigDir, serviceCredentialFile, serviceConfigFile) return err } @@ -214,41 +222,41 @@ func installLinuxService(c *cli.Context) error { switch { case isSystemd(): logger.Infof("Using Systemd") - return installSystemd(&templateArgs) + return installSystemd(&templateArgs, logger) default: logger.Infof("Using Sysv") - return installSysv(&templateArgs) + return installSysv(&templateArgs, logger) } } -func installSystemd(templateArgs *ServiceTemplateArgs) error { +func installSystemd(templateArgs *ServiceTemplateArgs, logger logger.Service) error { for _, serviceTemplate := range systemdTemplates { err := serviceTemplate.Generate(templateArgs) if err != nil { - logger.WithError(err).Infof("error generating service template") + logger.Errorf("error generating service template: %s", err) return err } } if err := runCommand("systemctl", "enable", "cloudflared.service"); err != nil { - logger.WithError(err).Infof("systemctl enable cloudflared.service error") + logger.Errorf("systemctl enable cloudflared.service error: %s", err) return err } if err := runCommand("systemctl", "start", "cloudflared-update.timer"); err != nil { - logger.WithError(err).Infof("systemctl start cloudflared-update.timer error") + logger.Errorf("systemctl start cloudflared-update.timer error: %s", err) return err } logger.Infof("systemctl daemon-reload") return runCommand("systemctl", "daemon-reload") } -func installSysv(templateArgs *ServiceTemplateArgs) error { +func installSysv(templateArgs *ServiceTemplateArgs, logger logger.Service) error { confPath, err := sysvTemplate.ResolvePath() if err != nil { - logger.WithError(err).Infof("error resolving system path") + logger.Errorf("error resolving system path: %s", err) return err } if err := sysvTemplate.Generate(templateArgs); err != nil { - logger.WithError(err).Infof("error generating system template") + logger.Errorf("error generating system template: %s", err) return err } for _, i := range [...]string{"2", "3", "4", "5"} { @@ -265,28 +273,33 @@ func installSysv(templateArgs *ServiceTemplateArgs) error { } func uninstallLinuxService(c *cli.Context) error { + logger, err := logger.New() + if err != nil { + return errors.Wrap(err, "error setting up logger") + } + switch { case isSystemd(): logger.Infof("Using Systemd") - return uninstallSystemd() + return uninstallSystemd(logger) default: logger.Infof("Using Sysv") - return uninstallSysv() + return uninstallSysv(logger) } } -func uninstallSystemd() error { +func uninstallSystemd(logger logger.Service) error { if err := runCommand("systemctl", "disable", "cloudflared.service"); err != nil { - logger.WithError(err).Infof("systemctl disable cloudflared.service error") + logger.Errorf("systemctl disable cloudflared.service error: %s", err) return err } if err := runCommand("systemctl", "stop", "cloudflared-update.timer"); err != nil { - logger.WithError(err).Infof("systemctl stop cloudflared-update.timer error") + logger.Errorf("systemctl stop cloudflared-update.timer error: %s", err) return err } for _, serviceTemplate := range systemdTemplates { if err := serviceTemplate.Remove(); err != nil { - logger.WithError(err).Infof("error removing service template") + logger.Errorf("error removing service template: %s", err) return err } } @@ -294,9 +307,9 @@ func uninstallSystemd() error { return nil } -func uninstallSysv() error { +func uninstallSysv(logger logger.Service) error { if err := sysvTemplate.Remove(); err != nil { - logger.WithError(err).Infof("error removing service template") + logger.Errorf("error removing service template: %s", err) return err } for _, i := range [...]string{"2", "3", "4", "5"} { diff --git a/cmd/cloudflared/macos_service.go b/cmd/cloudflared/macos_service.go index 9ec162fa..2c280dfa 100644 --- a/cmd/cloudflared/macos_service.go +++ b/cmd/cloudflared/macos_service.go @@ -8,6 +8,7 @@ import ( "gopkg.in/urfave/cli.v2" + "github.com/cloudflare/cloudflared/logger" "github.com/pkg/errors" ) @@ -105,6 +106,11 @@ func stderrPath() (string, error) { } func installLaunchd(c *cli.Context) error { + logger, err := logger.New() + if err != nil { + return errors.Wrap(err, "error setting up logger") + } + if isRootUser() { logger.Infof("Installing Argo Tunnel client as a system launch daemon. " + "Argo Tunnel client will run at boot") @@ -116,35 +122,38 @@ func installLaunchd(c *cli.Context) error { } etPath, err := os.Executable() if err != nil { - logger.WithError(err).Errorf("Error determining executable path") + logger.Errorf("Error determining executable path: %s", err) return fmt.Errorf("Error determining executable path: %v", err) } installPath, err := installPath() if err != nil { + logger.Errorf("Error determining install path: %s", err) return errors.Wrap(err, "Error determining install path") } stdoutPath, err := stdoutPath() if err != nil { + logger.Errorf("error determining stdout path: %s", err) return errors.Wrap(err, "error determining stdout path") } stderrPath, err := stderrPath() if err != nil { + logger.Errorf("error determining stderr path: %s", err) return errors.Wrap(err, "error determining stderr path") } launchdTemplate := newLaunchdTemplate(installPath, stdoutPath, stderrPath) if err != nil { - logger.WithError(err).Errorf("error creating launchd template") + logger.Errorf("error creating launchd template: %s", err) return errors.Wrap(err, "error creating launchd template") } templateArgs := ServiceTemplateArgs{Path: etPath} err = launchdTemplate.Generate(&templateArgs) if err != nil { - logger.WithError(err).Errorf("error generating launchd template") + logger.Errorf("error generating launchd template: %s", err) return err } plistPath, err := launchdTemplate.ResolvePath() if err != nil { - logger.WithError(err).Infof("error resolving launchd template path") + logger.Errorf("error resolving launchd template path: %s", err) return err } @@ -153,6 +162,11 @@ func installLaunchd(c *cli.Context) error { } func uninstallLaunchd(c *cli.Context) error { + logger, err := logger.New() + if err != nil { + return errors.Wrap(err, "error setting up logger") + } + if isRootUser() { logger.Infof("Uninstalling Argo Tunnel as a system launch daemon") } else { @@ -176,12 +190,12 @@ func uninstallLaunchd(c *cli.Context) error { } plistPath, err := launchdTemplate.ResolvePath() if err != nil { - logger.WithError(err).Infof("error resolving launchd template path") + logger.Errorf("error resolving launchd template path: %s", err) return err } err = runCommand("launchctl", "unload", plistPath) if err != nil { - logger.WithError(err).Infof("error unloading") + logger.Errorf("error unloading: %s", err) return err } diff --git a/cmd/cloudflared/main.go b/cmd/cloudflared/main.go index 97687984..20fd0743 100644 --- a/cmd/cloudflared/main.go +++ b/cmd/cloudflared/main.go @@ -9,7 +9,7 @@ import ( "github.com/cloudflare/cloudflared/cmd/cloudflared/config" "github.com/cloudflare/cloudflared/cmd/cloudflared/tunnel" "github.com/cloudflare/cloudflared/cmd/cloudflared/updater" - "github.com/cloudflare/cloudflared/log" + "github.com/cloudflare/cloudflared/logger" "github.com/cloudflare/cloudflared/metrics" "github.com/cloudflare/cloudflared/overwatch" "github.com/cloudflare/cloudflared/watcher" @@ -28,7 +28,6 @@ const ( var ( Version = "DEV" BuildTime = "unknown" - logger = log.CreateLogger() // Mostly network errors that we don't want reported back to Sentry, this is done by substring match. ignoredErrors = []string{ "connection reset by peer", @@ -148,7 +147,6 @@ func userHomeDir() (string, error) { // use with sudo. homeDir, err := homedir.Dir() if err != nil { - logger.WithError(err).Error("Cannot determine home directory for the user") return "", errors.Wrap(err, "Cannot determine home directory for the user") } return homeDir, nil @@ -167,17 +165,25 @@ func handleError(err error) { // cloudflared was started without any flags func handleServiceMode(shutdownC chan struct{}) error { + logDirectory, logLevel := config.FindLogSettings() + + logger, err := logger.New(logger.DefaultFile(logDirectory), logger.LogLevelString(logLevel)) + if err != nil { + return errors.Wrap(err, "error setting up logger") + } + logger.Infof("logging to directory: %s", logDirectory) + // start the main run loop that reads from the config file f, err := watcher.NewFile() if err != nil { - logger.WithError(err).Error("Cannot load config file") + logger.Errorf("Cannot load config file: %s", err) return err } configPath := config.FindDefaultConfigPath() configManager, err := config.NewFileManager(f, configPath, logger) if err != nil { - logger.WithError(err).Error("Cannot setup config file for monitoring") + logger.Errorf("Cannot setup config file for monitoring: %s", err) return err } @@ -185,7 +191,7 @@ func handleServiceMode(shutdownC chan struct{}) error { appService := NewAppService(configManager, serviceManager, shutdownC, logger) if err := appService.Run(); err != nil { - logger.WithError(err).Error("Failed to start app service") + logger.Errorf("Failed to start app service: %s", err) return err } return nil diff --git a/cmd/cloudflared/service_template.go b/cmd/cloudflared/service_template.go index 13d56b75..5d997f82 100644 --- a/cmd/cloudflared/service_template.go +++ b/cmd/cloudflared/service_template.go @@ -73,21 +73,16 @@ func runCommand(command string, args ...string) error { cmd := exec.Command(command, args...) stderr, err := cmd.StderrPipe() if err != nil { - logger.WithError(err).Infof("error getting stderr pipe") return fmt.Errorf("error getting stderr pipe: %v", err) } err = cmd.Start() if err != nil { - logger.WithError(err).Infof("error starting %s", command) return fmt.Errorf("error starting %s: %v", command, err) } - commandErr, _ := ioutil.ReadAll(stderr) - if len(commandErr) > 0 { - logger.Errorf("%s: %s", command, commandErr) - } + + ioutil.ReadAll(stderr) err = cmd.Wait() if err != nil { - logger.WithError(err).Infof("%s returned error", command) return fmt.Errorf("%s returned with error: %v", command, err) } return nil @@ -148,8 +143,7 @@ func copyConfig(srcConfigPath, destConfigPath string) error { // Copy or create config destFile, exists, err := openFile(destConfigPath, true) if err != nil { - logger.WithError(err).Infof("cannot open %s", destConfigPath) - return err + return fmt.Errorf("cannot open %s with error: %s", destConfigPath, err) } else if exists { // config already exists, do nothing return nil @@ -173,7 +167,6 @@ func copyConfig(srcConfigPath, destConfigPath string) error { if err != nil { return fmt.Errorf("unable to copy %s to %s: %v", srcConfigPath, destConfigPath, err) } - logger.Infof("Copied %s to %s", srcConfigPath, destConfigPath) } return nil diff --git a/cmd/cloudflared/token/token.go b/cmd/cloudflared/token/token.go index e28b1fd6..433187b2 100644 --- a/cmd/cloudflared/token/token.go +++ b/cmd/cloudflared/token/token.go @@ -14,7 +14,7 @@ import ( "github.com/cloudflare/cloudflared/cmd/cloudflared/config" "github.com/cloudflare/cloudflared/cmd/cloudflared/path" "github.com/cloudflare/cloudflared/cmd/cloudflared/transfer" - "github.com/cloudflare/cloudflared/log" + "github.com/cloudflare/cloudflared/logger" "github.com/cloudflare/cloudflared/origin" "github.com/coreos/go-oidc/jose" ) @@ -23,8 +23,6 @@ const ( keyName = "token" ) -var logger = log.CreateLogger() - type lock struct { lockFilePath string backoff *origin.BackoffHandler @@ -130,7 +128,7 @@ func isTokenLocked(lockFilePath string) bool { } // FetchToken will either load a stored token or generate a new one -func FetchToken(appURL *url.URL) (string, error) { +func FetchToken(appURL *url.URL, logger logger.Service) (string, error) { if token, err := GetTokenIfExists(appURL); token != "" && err == nil { return token, nil } @@ -156,7 +154,7 @@ func FetchToken(appURL *url.URL) (string, error) { // this weird parameter is the resource name (token) and the key/value // we want to send to the transfer service. the key is token and the value // is blank (basically just the id generated in the transfer service) - token, err := transfer.Run(appURL, keyName, keyName, "", path, true) + token, err := transfer.Run(appURL, keyName, keyName, "", path, true, logger) if err != nil { return "", err } diff --git a/cmd/cloudflared/transfer/transfer.go b/cmd/cloudflared/transfer/transfer.go index dbe5536a..24cfa745 100644 --- a/cmd/cloudflared/transfer/transfer.go +++ b/cmd/cloudflared/transfer/transfer.go @@ -14,7 +14,7 @@ import ( "github.com/cloudflare/cloudflared/cmd/cloudflared/encrypter" "github.com/cloudflare/cloudflared/cmd/cloudflared/shell" - "github.com/cloudflare/cloudflared/log" + "github.com/cloudflare/cloudflared/logger" ) const ( @@ -22,15 +22,13 @@ const ( clientTimeout = time.Second * 60 ) -var logger = log.CreateLogger() - // Run does the transfer "dance" with the end result downloading the supported resource. // The expanded description is run is encapsulation of shared business logic needed // to request a resource (token/cert/etc) from the transfer service (loginhelper). // The "dance" we refer to is building a HTTP request, opening that in a browser waiting for // the user to complete an action, while it long polls in the background waiting for an // action to be completed to download the resource. -func Run(transferURL *url.URL, resourceName, key, value, path string, shouldEncrypt bool) ([]byte, error) { +func Run(transferURL *url.URL, resourceName, key, value, path string, shouldEncrypt bool, logger logger.Service) ([]byte, error) { encrypterClient, err := encrypter.New("cloudflared_priv.pem", "cloudflared_pub.pem") if err != nil { return nil, err @@ -51,7 +49,7 @@ func Run(transferURL *url.URL, resourceName, key, value, path string, shouldEncr var resourceData []byte if shouldEncrypt { - buf, key, err := transferRequest(baseStoreURL + "transfer/" + encrypterClient.PublicKey()) + buf, key, err := transferRequest(baseStoreURL+"transfer/"+encrypterClient.PublicKey(), logger) if err != nil { return nil, err } @@ -67,7 +65,7 @@ func Run(transferURL *url.URL, resourceName, key, value, path string, shouldEncr resourceData = decrypted } else { - buf, _, err := transferRequest(baseStoreURL + encrypterClient.PublicKey()) + buf, _, err := transferRequest(baseStoreURL+encrypterClient.PublicKey(), logger) if err != nil { return nil, err } @@ -99,17 +97,17 @@ func buildRequestURL(baseURL *url.URL, key, value string, cli bool) (string, err } // transferRequest downloads the requested resource from the request URL -func transferRequest(requestURL string) ([]byte, string, error) { +func transferRequest(requestURL string, logger logger.Service) ([]byte, string, error) { client := &http.Client{Timeout: clientTimeout} const pollAttempts = 10 // we do "long polling" on the endpoint to get the resource. for i := 0; i < pollAttempts; i++ { - buf, key, err := poll(client, requestURL) + buf, key, err := poll(client, requestURL, logger) if err != nil { return nil, "", err } else if len(buf) > 0 { if err := putSuccess(client, requestURL); err != nil { - logger.WithError(err).Error("Failed to update resource success") + logger.Errorf("Failed to update resource success: %s", err) } return buf, key, nil } @@ -118,7 +116,7 @@ func transferRequest(requestURL string) ([]byte, string, error) { } // poll the endpoint for the request resource, waiting for the user interaction -func poll(client *http.Client, requestURL string) ([]byte, string, error) { +func poll(client *http.Client, requestURL string, logger logger.Service) ([]byte, string, error) { resp, err := client.Get(requestURL) if err != nil { return nil, "", err diff --git a/cmd/cloudflared/tunnel/cmd.go b/cmd/cloudflared/tunnel/cmd.go index 5a9bdb85..898b034c 100644 --- a/cmd/cloudflared/tunnel/cmd.go +++ b/cmd/cloudflared/tunnel/cmd.go @@ -25,6 +25,7 @@ import ( "github.com/cloudflare/cloudflared/dbconnect" "github.com/cloudflare/cloudflared/h2mux" "github.com/cloudflare/cloudflared/hello" + "github.com/cloudflare/cloudflared/logger" "github.com/cloudflare/cloudflared/metrics" "github.com/cloudflare/cloudflared/origin" "github.com/cloudflare/cloudflared/signal" @@ -93,6 +94,9 @@ const ( bastionFlag = "bastion" noIntentMsg = "The --intent argument is required. Cloudflared looks up an Intent to determine what configuration to use (i.e. which tunnels to start). If you don't have any Intents yet, you can use a placeholder Intent Label for now. Then, when you make an Intent with that label, cloudflared will get notified and open the tunnels you specified in that Intent." + + debugLevelWarning = "At debug level, request URL, method, protocol, content legnth and header will be logged. " + + "Response status, content length and header will also be logged in debug level." ) var ( @@ -212,7 +216,28 @@ func Init(v string, s, g chan struct{}) { version, shutdownC, graceShutdownC = v, s, g } +func createLogger(c *cli.Context, isTransport bool) (logger.Service, error) { + loggerOpts := []logger.Option{} + logPath := c.String("logfile") + if logPath != "" { + loggerOpts = append(loggerOpts, logger.DefaultFile(logPath)) + } + + logLevel := c.String("loglevel") + if isTransport { + logLevel = c.String("transport-loglevel") + } + loggerOpts = append(loggerOpts, logger.LogLevelString(logLevel)) + + return logger.New(loggerOpts...) +} + func StartServer(c *cli.Context, version string, shutdownC, graceShutdownC chan struct{}) error { + logger, err := createLogger(c, false) + if err != nil { + return errors.Wrap(err, "error setting up logger") + } + _ = raven.SetDSN(sentryDSN) var wg sync.WaitGroup listeners := gracenet.Net{} @@ -221,64 +246,45 @@ func StartServer(c *cli.Context, version string, shutdownC, graceShutdownC chan dnsReadySignal := make(chan struct{}) if c.String("config") == "" { - logger.Warnf("Cannot determine default configuration path. No file %v in %v", config.DefaultConfigFiles, config.DefaultConfigDirs) - } - - if err := configMainLogger(c); err != nil { - return errors.Wrap(err, "Error configuring logger") - } - - transportLogger, err := configTransportLogger(c) - if err != nil { - return errors.Wrap(err, "Error configuring transport logger") + logger.Infof("Cannot determine default configuration path. No file %v in %v", config.DefaultConfigFiles, config.DefaultConfigDirs) } if c.IsSet("trace-output") { tmpTraceFile, err := ioutil.TempFile("", "trace") if err != nil { - logger.WithError(err).Error("Failed to create new temporary file to save trace output") + logger.Errorf("Failed to create new temporary file to save trace output: %s", err) } defer func() { if err := tmpTraceFile.Close(); err != nil { - logger.WithError(err).Errorf("Failed to close trace output file %s", tmpTraceFile.Name()) + logger.Errorf("Failed to close trace output file %s with error: %s", tmpTraceFile.Name(), err) } if err := os.Rename(tmpTraceFile.Name(), c.String("trace-output")); err != nil { - logger.WithError(err).Errorf("Failed to rename temporary trace output file %s to %s", tmpTraceFile.Name(), c.String("trace-output")) + logger.Errorf("Failed to rename temporary trace output file %s to %s with error: %s", tmpTraceFile.Name(), c.String("trace-output"), err) } else { err := os.Remove(tmpTraceFile.Name()) if err != nil { - logger.WithError(err).Errorf("Failed to remove the temporary trace file %s", tmpTraceFile.Name()) + logger.Errorf("Failed to remove the temporary trace file %s with error: %s", tmpTraceFile.Name(), err) } } }() if err := trace.Start(tmpTraceFile); err != nil { - logger.WithError(err).Error("Failed to start trace") + logger.Errorf("Failed to start trace: %s", err) return errors.Wrap(err, "Error starting tracing") } defer trace.Stop() } - if c.String("logfile") != "" { - if err := initLogFile(c, logger, transportLogger); err != nil { - logger.Error(err) - } - } - - if err := handleDeprecatedOptions(c); err != nil { - return err - } - buildInfo := buildinfo.GetBuildInfo(version) buildInfo.Log(logger) - logClientOptions(c) + logClientOptions(c, logger) if c.IsSet("proxy-dns") { wg.Add(1) go func() { defer wg.Done() - errC <- runDNSProxyServer(c, dnsReadySignal, shutdownC) + errC <- runDNSProxyServer(c, dnsReadySignal, shutdownC, logger) }() } else { close(dnsReadySignal) @@ -289,7 +295,7 @@ func StartServer(c *cli.Context, version string, shutdownC, graceShutdownC chan metricsListener, err := listeners.Listen("tcp", c.String("metrics")) if err != nil { - logger.WithError(err).Error("Error opening metrics server listener") + logger.Errorf("Error opening metrics server listener: %s", err) return errors.Wrap(err, "Error opening metrics server listener") } defer metricsListener.Close() @@ -301,12 +307,12 @@ func StartServer(c *cli.Context, version string, shutdownC, graceShutdownC chan go notifySystemd(connectedSignal) if c.IsSet("pidfile") { - go writePidFile(connectedSignal, c.String("pidfile")) + go writePidFile(connectedSignal, c.String("pidfile"), logger) } cloudflaredID, err := uuid.NewRandom() if err != nil { - logger.WithError(err).Error("Cannot generate cloudflared ID") + logger.Errorf("Cannot generate cloudflared ID: %s", err) return err } @@ -317,16 +323,16 @@ func StartServer(c *cli.Context, version string, shutdownC, graceShutdownC chan }() if c.IsSet("use-declarative-tunnels") { - return startDeclarativeTunnel(ctx, c, cloudflaredID, buildInfo, &listeners) + return startDeclarativeTunnel(ctx, c, cloudflaredID, buildInfo, &listeners, logger) } // update needs to be after DNS proxy is up to resolve equinox server address - if updater.IsAutoupdateEnabled(c) { + if updater.IsAutoupdateEnabled(c, logger) { logger.Infof("Autoupdate frequency is set to %v", c.Duration("autoupdate-freq")) wg.Add(1) go func() { defer wg.Done() - autoupdater := updater.NewAutoUpdater(c.Duration("autoupdate-freq"), &listeners) + autoupdater := updater.NewAutoUpdater(c.Duration("autoupdate-freq"), &listeners, logger) errC <- autoupdater.Run(ctx) }() } @@ -335,14 +341,14 @@ func StartServer(c *cli.Context, version string, shutdownC, graceShutdownC chan if dnsProxyStandAlone(c) { connectedSignal.Notify() // no grace period, handle SIGINT/SIGTERM immediately - return waitToShutdown(&wg, errC, shutdownC, graceShutdownC, 0) + return waitToShutdown(&wg, errC, shutdownC, graceShutdownC, 0, logger) } if c.IsSet("hello-world") { logger.Infof("hello-world set") helloListener, err := hello.CreateTLSListener("127.0.0.1:") if err != nil { - logger.WithError(err).Error("Cannot start Hello World Server") + logger.Errorf("Cannot start Hello World Server: %s", err) return errors.Wrap(err, "Cannot start Hello World Server") } defer helloListener.Close() @@ -369,13 +375,13 @@ func StartServer(c *cli.Context, version string, shutdownC, graceShutdownC chan c.String(accessKeyIDFlag), c.String(secretIDFlag), c.String(sessionTokenIDFlag), c.String(s3URLFlag)) if err != nil { msg := "Cannot create uploader for SSH Server" - logger.WithError(err).Error(msg) + logger.Errorf("%s: %s", msg, err) return errors.Wrap(err, msg) } if err := os.MkdirAll(sshLogFileDirectory, 0700); err != nil { msg := fmt.Sprintf("Cannot create SSH log file directory %s", sshLogFileDirectory) - logger.WithError(err).Errorf(msg) + logger.Errorf("%s: %s", msg, err) return errors.Wrap(err, msg) } @@ -389,14 +395,14 @@ func StartServer(c *cli.Context, version string, shutdownC, graceShutdownC chan server, err := sshserver.New(logManager, logger, version, localServerAddress, c.String("hostname"), c.Path(hostKeyPath), shutdownC, c.Duration(sshIdleTimeoutFlag), c.Duration(sshMaxTimeoutFlag)) if err != nil { msg := "Cannot create new SSH Server" - logger.WithError(err).Error(msg) + logger.Errorf("%s: %s", msg, err) return errors.Wrap(err, msg) } wg.Add(1) go func() { defer wg.Done() if err = server.Start(); err != nil && err != ssh.ErrServerClosed { - logger.WithError(err).Error("SSH server error") + logger.Errorf("SSH server error: %s", err) // TODO: remove when declarative tunnels are implemented. close(shutdownC) } @@ -407,7 +413,7 @@ func StartServer(c *cli.Context, version string, shutdownC, graceShutdownC chan if staticHost := hostnameFromURI(c.String("url")); isProxyDestinationConfigured(staticHost, c) { listener, err := net.Listen("tcp", "127.0.0.1:") if err != nil { - logger.WithError(err).Error("Cannot start Websocket Proxy Server") + logger.Errorf("Cannot start Websocket Proxy Server: %s", err) return errors.Wrap(err, "Cannot start Websocket Proxy Server") } wg.Add(1) @@ -428,7 +434,7 @@ func StartServer(c *cli.Context, version string, shutdownC, graceShutdownC chan if finalDestination := requestHeaders.Get(h2mux.CFJumpDestinationHeader); finalDestination != "" { token := requestHeaders.Get(h2mux.CFAccessTokenHeader) if err := websocket.SendSSHPreamble(remoteConn, finalDestination, token); err != nil { - logger.WithError(err).Error("Failed to send SSH preamble") + logger.Errorf("Failed to send SSH preamble: %s", err) return } } @@ -440,6 +446,11 @@ func StartServer(c *cli.Context, version string, shutdownC, graceShutdownC chan c.Set("url", "http://"+listener.Addr().String()) } + transportLogger, err := createLogger(c, true) + if err != nil { + return errors.Wrap(err, "error setting up transport logger") + } + tunnelConfig, err := prepareTunnelConfig(c, buildInfo, version, logger, transportLogger) if err != nil { return err @@ -447,8 +458,8 @@ func StartServer(c *cli.Context, version string, shutdownC, graceShutdownC chan reconnectCh := make(chan origin.ReconnectSignal, 1) if c.IsSet("stdin-control") { - logger.Warn("Enabling control through stdin") - go stdinControl(reconnectCh) + logger.Info("Enabling control through stdin") + go stdinControl(reconnectCh, logger) } wg.Add(1) @@ -456,21 +467,27 @@ func StartServer(c *cli.Context, version string, shutdownC, graceShutdownC chan defer wg.Done() errC <- origin.StartTunnelDaemon(ctx, tunnelConfig, connectedSignal, cloudflaredID, reconnectCh) }() - return waitToShutdown(&wg, errC, shutdownC, graceShutdownC, c.Duration("grace-period")) + + return waitToShutdown(&wg, errC, shutdownC, graceShutdownC, c.Duration("grace-period"), logger) } func Before(c *cli.Context) error { + logger, err := createLogger(c, false) + if err != nil { + return errors.Wrap(err, "error setting up logger") + } + if c.String("config") == "" { logger.Debugf("Cannot determine default configuration path. No file %v in %v", config.DefaultConfigFiles, config.DefaultConfigDirs) } inputSource, err := config.FindInputSourceContext(c) if err != nil { - logger.WithError(err).Errorf("Cannot load configuration from %s", c.String("config")) + logger.Errorf("Cannot load configuration from %s: %s", c.String("config"), err) return err } else if inputSource != nil { err := altsrc.ApplyInputSourceValues(c, inputSource, c.App.Flags) if err != nil { - logger.WithError(err).Errorf("Cannot apply configuration from %s", c.String("config")) + logger.Errorf("Cannot apply configuration from %s: %s", c.String("config"), err) return err } logger.Debugf("Applied configuration from %s", c.String("config")) @@ -488,10 +505,11 @@ func startDeclarativeTunnel(ctx context.Context, cloudflaredID uuid.UUID, buildInfo *buildinfo.BuildInfo, listeners *gracenet.Net, + logger logger.Service, ) error { reverseProxyOrigin, err := defaultOriginConfig(c) if err != nil { - logger.WithError(err) + logger.Errorf("%s", err) return err } reverseProxyConfig, err := pogs.NewReverseProxyConfig( @@ -502,7 +520,7 @@ func startDeclarativeTunnel(ctx context.Context, c.Uint64("compression-quality"), ) if err != nil { - logger.WithError(err).Error("Cannot initialize default client config because reverse proxy config is invalid") + logger.Errorf("Cannot initialize default client config because reverse proxy config is invalid: %s", err) return err } defaultClientConfig := &pogs.ClientConfig{ @@ -522,22 +540,22 @@ func startDeclarativeTunnel(ctx context.Context, ReverseProxyConfigs: []*pogs.ReverseProxyConfig{reverseProxyConfig}, } - autoupdater := updater.NewAutoUpdater(defaultClientConfig.SupervisorConfig.AutoUpdateFrequency, listeners) + autoupdater := updater.NewAutoUpdater(defaultClientConfig.SupervisorConfig.AutoUpdateFrequency, listeners, logger) - originCert, err := getOriginCert(c) + originCert, err := getOriginCert(c, logger) if err != nil { - logger.WithError(err).Error("error getting origin cert") + logger.Errorf("error getting origin cert: %s", err) return err } toEdgeTLSConfig, err := tlsconfig.CreateTunnelConfig(c) if err != nil { - logger.WithError(err).Error("unable to create TLS config to connect with edge") + logger.Errorf("unable to create TLS config to connect with edge: %s", err) return err } tags, err := NewTagSliceFromCLI(c.StringSlice("tag")) if err != nil { - logger.WithError(err).Error("unable to parse tag") + logger.Errorf("unable to parse tag: %s", err) return err } @@ -556,13 +574,13 @@ func startDeclarativeTunnel(ctx context.Context, serviceDiscoverer, err := serviceDiscoverer(c, logger) if err != nil { - logger.WithError(err).Error("unable to create service discoverer") + logger.Errorf("unable to create service discoverer: %s", err) return err } supervisor, err := supervisor.NewSupervisor(defaultClientConfig, originCert, toEdgeTLSConfig, - serviceDiscoverer, cloudflaredConfig, autoupdater, updater.SupportAutoUpdate(), logger) + serviceDiscoverer, cloudflaredConfig, autoupdater, updater.SupportAutoUpdate(logger), logger) if err != nil { - logger.WithError(err).Error("unable to create Supervisor") + logger.Errorf("unable to create Supervisor: %s", err) return err } return supervisor.Run(ctx) @@ -604,17 +622,18 @@ func waitToShutdown(wg *sync.WaitGroup, errC chan error, shutdownC, graceShutdownC chan struct{}, gracePeriod time.Duration, + logger logger.Service, ) error { var err error if gracePeriod > 0 { - err = waitForSignalWithGraceShutdown(errC, shutdownC, graceShutdownC, gracePeriod) + err = waitForSignalWithGraceShutdown(errC, shutdownC, graceShutdownC, gracePeriod, logger) } else { - err = waitForSignal(errC, shutdownC) + err = waitForSignal(errC, shutdownC, logger) close(graceShutdownC) } if err != nil { - logger.WithError(err).Error("Quitting due to error") + logger.Errorf("Quitting due to error: %s", err) } else { logger.Info("Quitting...") } @@ -632,16 +651,16 @@ func notifySystemd(waitForSignal *signal.Signal) { daemon.SdNotify(false, "READY=1") } -func writePidFile(waitForSignal *signal.Signal, pidFile string) { +func writePidFile(waitForSignal *signal.Signal, pidFile string, logger logger.Service) { <-waitForSignal.Wait() expandedPath, err := homedir.Expand(pidFile) if err != nil { - logger.WithError(err).Errorf("Unable to expand %s, try to use absolute path in --pidfile", pidFile) + logger.Errorf("Unable to expand %s, try to use absolute path in --pidfile: %s", pidFile, err) return } file, err := os.Create(expandedPath) if err != nil { - logger.WithError(err).Errorf("Unable to write pid to %s", expandedPath) + logger.Errorf("Unable to write pid to %s: %s", expandedPath, err) return } defer file.Close() @@ -888,15 +907,15 @@ func tunnelFlags(shouldHide bool) []cli.Flag { altsrc.NewStringFlag(&cli.StringFlag{ Name: "loglevel", Value: "info", - Usage: "Application logging level {panic, fatal, error, warn, info, debug}. " + debugLevelWarning, + Usage: "Application logging level {fatal, error, info, debug}. " + debugLevelWarning, EnvVars: []string{"TUNNEL_LOGLEVEL"}, Hidden: shouldHide, }), altsrc.NewStringFlag(&cli.StringFlag{ Name: "transport-loglevel", Aliases: []string{"proto-loglevel"}, // This flag used to be called proto-loglevel - Value: "warn", - Usage: "Transport logging level(previously called protocol logging level) {panic, fatal, error, warn, info, debug}", + Value: "fatal", + Usage: "Transport logging level(previously called protocol logging level) {fatal, error, info, debug}", EnvVars: []string{"TUNNEL_PROTO_LOGLEVEL", "TUNNEL_TRANSPORT_LOGLEVEL"}, Hidden: shouldHide, }), @@ -1163,7 +1182,7 @@ func tunnelFlags(shouldHide bool) []cli.Flag { } } -func stdinControl(reconnectCh chan origin.ReconnectSignal) { +func stdinControl(reconnectCh chan origin.ReconnectSignal, logger logger.Service) { for { scanner := bufio.NewScanner(os.Stdin) for scanner.Scan() { @@ -1185,7 +1204,7 @@ func stdinControl(reconnectCh chan origin.ReconnectSignal) { logger.Infof("Sending reconnect signal %+v", reconnect) reconnectCh <- reconnect default: - logger.Warn("Unknown command: ", command) + logger.Infof("Unknown command: %s", command) fallthrough case "help": logger.Info(`Supported command: diff --git a/cmd/cloudflared/tunnel/configuration.go b/cmd/cloudflared/tunnel/configuration.go index 883d3542..a085f1b5 100644 --- a/cmd/cloudflared/tunnel/configuration.go +++ b/cmd/cloudflared/tunnel/configuration.go @@ -15,6 +15,7 @@ import ( "github.com/cloudflare/cloudflared/cmd/cloudflared/buildinfo" "github.com/cloudflare/cloudflared/cmd/cloudflared/config" "github.com/cloudflare/cloudflared/edgediscovery" + "github.com/cloudflare/cloudflared/logger" "github.com/cloudflare/cloudflared/origin" "github.com/cloudflare/cloudflared/tlsconfig" tunnelpogs "github.com/cloudflare/cloudflared/tunnelrpc/pogs" @@ -23,7 +24,6 @@ import ( "github.com/google/uuid" "github.com/mitchellh/go-homedir" "github.com/pkg/errors" - "github.com/sirupsen/logrus" "golang.org/x/crypto/ssh/terminal" "gopkg.in/urfave/cli.v2" ) @@ -47,25 +47,16 @@ func findDefaultOriginCertPath() string { return "" } -func generateRandomClientID(logger *logrus.Logger) (string, error) { +func generateRandomClientID(logger logger.Service) (string, error) { u, err := uuid.NewRandom() if err != nil { - logger.WithError(err).Error("couldn't create UUID for client ID") + logger.Errorf("couldn't create UUID for client ID %s", err) return "", err } return u.String(), nil } -func handleDeprecatedOptions(c *cli.Context) error { - // Fail if the user provided an old authentication method - if c.IsSet("api-key") || c.IsSet("api-email") || c.IsSet("api-ca-key") { - logger.Error("You don't need to give us your api-key anymore. Please use the new login method. Just run cloudflared login") - return fmt.Errorf("Client provided deprecated options") - } - return nil -} - -func logClientOptions(c *cli.Context) { +func logClientOptions(c *cli.Context, logger logger.Service) { flags := make(map[string]interface{}) for _, flag := range c.LocalFlagNames() { flags[flag] = c.Generic(flag) @@ -79,7 +70,7 @@ func logClientOptions(c *cli.Context) { } if len(flags) > 0 { - logger.WithFields(flags).Info("Flags") + logger.Infof("Environment variables %v", flags) } envs := make(map[string]string) @@ -102,10 +93,10 @@ func dnsProxyStandAlone(c *cli.Context) bool { return c.IsSet("proxy-dns") && (!c.IsSet("hostname") && !c.IsSet("tag") && !c.IsSet("hello-world")) } -func findOriginCert(c *cli.Context) (string, error) { +func findOriginCert(c *cli.Context, logger logger.Service) (string, error) { originCertPath := c.String("origincert") if originCertPath == "" { - logger.Warnf("Cannot determine default origin certificate path. No file %s in %v", config.DefaultCredentialFile, config.DefaultConfigDirs) + logger.Infof("Cannot determine default origin certificate path. No file %s in %v", config.DefaultCredentialFile, config.DefaultConfigDirs) if isRunningFromTerminal() { logger.Errorf("You need to specify the origin certificate path with --origincert option, or set TUNNEL_ORIGIN_CERT environment variable. See %s for more information.", argumentsUrl) return "", fmt.Errorf("Client didn't specify origincert path when running from terminal") @@ -117,7 +108,7 @@ func findOriginCert(c *cli.Context) (string, error) { var err error originCertPath, err = homedir.Expand(originCertPath) if err != nil { - logger.WithError(err).Errorf("Cannot resolve path %s", originCertPath) + logger.Errorf("Cannot resolve path %s: %s", originCertPath, err) return "", fmt.Errorf("Cannot resolve path %s", originCertPath) } // Check that the user has acquired a certificate using the login command @@ -142,35 +133,36 @@ If you don't have a certificate signed by Cloudflare, run the command: return originCertPath, nil } -func readOriginCert(originCertPath string) ([]byte, error) { +func readOriginCert(originCertPath string, logger logger.Service) ([]byte, error) { logger.Debugf("Reading origin cert from %s", originCertPath) // Easier to send the certificate as []byte via RPC than decoding it at this point originCert, err := ioutil.ReadFile(originCertPath) if err != nil { - logger.WithError(err).Errorf("Cannot read %s to load origin certificate", originCertPath) + logger.Errorf("Cannot read %s to load origin certificate: %s", originCertPath, err) return nil, fmt.Errorf("Cannot read %s to load origin certificate", originCertPath) } return originCert, nil } -func getOriginCert(c *cli.Context) ([]byte, error) { - if originCertPath, err := findOriginCert(c); err != nil { +func getOriginCert(c *cli.Context, logger logger.Service) ([]byte, error) { + if originCertPath, err := findOriginCert(c, logger); err != nil { return nil, err } else { - return readOriginCert(originCertPath) + return readOriginCert(originCertPath, logger) } } func prepareTunnelConfig( c *cli.Context, buildInfo *buildinfo.BuildInfo, - version string, logger, - transportLogger *logrus.Logger, + version string, + logger logger.Service, + transportLogger logger.Service, ) (*origin.TunnelConfig, error) { hostname, err := validation.ValidateHostname(c.String("hostname")) if err != nil { - logger.WithError(err).Error("Invalid hostname") + logger.Errorf("Invalid hostname: %s", err) return nil, errors.Wrap(err, "Invalid hostname") } isFreeTunnel := hostname == "" @@ -184,7 +176,7 @@ func prepareTunnelConfig( tags, err := NewTagSliceFromCLI(c.StringSlice("tag")) if err != nil { - logger.WithError(err).Error("Tag parse failure") + logger.Errorf("Tag parse failure: %s", err) return nil, errors.Wrap(err, "Tag parse failure") } @@ -192,13 +184,13 @@ func prepareTunnelConfig( originURL, err := config.ValidateUrl(c) if err != nil { - logger.WithError(err).Error("Error validating origin URL") + logger.Errorf("Error validating origin URL: %s", err) return nil, errors.Wrap(err, "Error validating origin URL") } var originCert []byte if !isFreeTunnel { - originCert, err = getOriginCert(c) + originCert, err = getOriginCert(c, logger) if err != nil { return nil, errors.Wrap(err, "Error getting origin cert") } @@ -206,7 +198,7 @@ func prepareTunnelConfig( originCertPool, err := tlsconfig.LoadOriginCA(c, logger) if err != nil { - logger.WithError(err).Error("Error loading cert pool") + logger.Errorf("Error loading cert pool: %s", err) return nil, errors.Wrap(err, "Error loading cert pool") } @@ -233,7 +225,7 @@ func prepareTunnelConfig( if c.IsSet("unix-socket") { unixSocket, err := config.ValidateUnixSocket(c) if err != nil { - logger.WithError(err).Error("Error validating --unix-socket") + logger.Errorf("Error validating --unix-socket: %s", err) return nil, errors.Wrap(err, "Error validating --unix-socket") } @@ -253,13 +245,13 @@ func prepareTunnelConfig( // If tunnel running in bastion mode, a connection to origin will not exist until initiated by the client. if !c.IsSet(bastionFlag) { if err = validation.ValidateHTTPService(originURL, hostname, httpTransport); err != nil { - logger.WithError(err).Error("unable to connect to the origin") + logger.Errorf("unable to connect to the origin: %s", err) } } toEdgeTLSConfig, err := tlsconfig.CreateTunnelConfig(c) if err != nil { - logger.WithError(err).Error("unable to create TLS config to connect with edge") + logger.Errorf("unable to create TLS config to connect with edge: %s", err) return nil, errors.Wrap(err, "unable to create TLS config to connect with edge") } @@ -280,6 +272,7 @@ func prepareTunnelConfig( IsFreeTunnel: isFreeTunnel, LBPool: c.String("lb-pool"), Logger: logger, + TransportLogger: transportLogger, MaxHeartbeats: c.Uint64("heartbeat-count"), Metrics: tunnelMetrics, MetricsUpdateFreq: c.Duration("metrics-update-freq"), @@ -291,14 +284,13 @@ func prepareTunnelConfig( RunFromTerminal: isRunningFromTerminal(), Tags: tags, TlsConfig: toEdgeTLSConfig, - TransportLogger: transportLogger, UseDeclarativeTunnel: c.Bool("use-declarative-tunnels"), UseReconnectToken: c.Bool("use-reconnect-token"), UseQuickReconnects: c.Bool("use-quick-reconnects"), }, nil } -func serviceDiscoverer(c *cli.Context, logger *logrus.Logger) (*edgediscovery.Edge, error) { +func serviceDiscoverer(c *cli.Context, logger logger.Service) (*edgediscovery.Edge, error) { // If --edge is specfied, resolve edge server addresses if len(c.StringSlice("edge")) > 0 { return edgediscovery.StaticEdge(logger, c.StringSlice("edge")) diff --git a/cmd/cloudflared/tunnel/logger.go b/cmd/cloudflared/tunnel/logger.go deleted file mode 100644 index 164de01b..00000000 --- a/cmd/cloudflared/tunnel/logger.go +++ /dev/null @@ -1,75 +0,0 @@ -package tunnel - -import ( - "fmt" - "os" - - "github.com/cloudflare/cloudflared/log" - "github.com/rifflock/lfshook" - "github.com/sirupsen/logrus" - "gopkg.in/urfave/cli.v2" - - "github.com/mitchellh/go-homedir" - "github.com/pkg/errors" -) - -const debugLevelWarning = "At debug level, request URL, method, protocol, content legnth and header will be logged. " + - "Response status, content length and header will also be logged in debug level." - -var logger = log.CreateLogger() - -func configMainLogger(c *cli.Context) error { - logLevel, err := logrus.ParseLevel(c.String("loglevel")) - if err != nil { - logger.WithError(err).Error("Unknown logging level specified") - return errors.Wrap(err, "Unknown logging level specified") - } - logger.SetLevel(logLevel) - if logLevel == logrus.DebugLevel { - logger.Warn(debugLevelWarning) - } - return nil -} - -func configTransportLogger(c *cli.Context) (*logrus.Logger, error) { - transportLogLevel, err := logrus.ParseLevel(c.String("transport-loglevel")) - if err != nil { - logger.WithError(err).Fatal("Unknown transport logging level specified") - return nil, errors.Wrap(err, "Unknown transport logging level specified") - } - transportLogger := logrus.New() - transportLogger.Level = transportLogLevel - return transportLogger, nil -} - -func initLogFile(c *cli.Context, loggers ...*logrus.Logger) error { - filePath, err := homedir.Expand(c.String("logfile")) - if err != nil { - return errors.Wrap(err, "Cannot resolve logfile path") - } - - fileMode := os.O_WRONLY | os.O_APPEND | os.O_CREATE | os.O_TRUNC - // do not truncate log file if the client has been autoupdated - if c.Bool("is-autoupdated") { - fileMode = os.O_WRONLY | os.O_APPEND | os.O_CREATE - } - f, err := os.OpenFile(filePath, fileMode, 0664) - if err != nil { - errors.Wrap(err, fmt.Sprintf("Cannot open file %s", filePath)) - } - defer f.Close() - pathMap := lfshook.PathMap{ - logrus.DebugLevel: filePath, - logrus.WarnLevel: filePath, - logrus.InfoLevel: filePath, - logrus.ErrorLevel: filePath, - logrus.FatalLevel: filePath, - logrus.PanicLevel: filePath, - } - - for _, l := range loggers { - l.Hooks.Add(lfshook.NewHook(pathMap, &logrus.JSONFormatter{})) - } - - return nil -} diff --git a/cmd/cloudflared/tunnel/login.go b/cmd/cloudflared/tunnel/login.go index 1eb94936..3a9fbdb3 100644 --- a/cmd/cloudflared/tunnel/login.go +++ b/cmd/cloudflared/tunnel/login.go @@ -9,7 +9,9 @@ import ( "github.com/cloudflare/cloudflared/cmd/cloudflared/config" "github.com/cloudflare/cloudflared/cmd/cloudflared/transfer" + "github.com/cloudflare/cloudflared/logger" homedir "github.com/mitchellh/go-homedir" + "github.com/pkg/errors" cli "gopkg.in/urfave/cli.v2" ) @@ -19,6 +21,11 @@ const ( ) func login(c *cli.Context) error { + logger, err := logger.New() + if err != nil { + return errors.Wrap(err, "error setting up logger") + } + path, ok, err := checkForExistingCert() if ok { fmt.Fprintf(os.Stdout, "You have an existing certificate at %s which login would overwrite.\nIf this is intentional, please move or delete that file then run this command again.\n", path) @@ -33,7 +40,7 @@ func login(c *cli.Context) error { return err } - _, err = transfer.Run(loginURL, "cert", "callback", callbackStoreURL, path, false) + _, err = transfer.Run(loginURL, "cert", "callback", callbackStoreURL, path, false, logger) if err != nil { fmt.Fprintf(os.Stderr, "Failed to write the certificate due to the following error:\n%v\n\nYour browser will download the certificate instead. You will have to manually\ncopy it to the following path:\n\n%s\n", err, path) return err diff --git a/cmd/cloudflared/tunnel/server.go b/cmd/cloudflared/tunnel/server.go index 4595566d..bb6598e3 100644 --- a/cmd/cloudflared/tunnel/server.go +++ b/cmd/cloudflared/tunnel/server.go @@ -1,6 +1,7 @@ package tunnel import ( + "github.com/cloudflare/cloudflared/logger" "github.com/cloudflare/cloudflared/tunneldns" "gopkg.in/urfave/cli.v2" @@ -8,23 +9,20 @@ import ( "github.com/pkg/errors" ) -func runDNSProxyServer(c *cli.Context, dnsReadySignal, shutdownC chan struct{}) error { +func runDNSProxyServer(c *cli.Context, dnsReadySignal, shutdownC chan struct{}, logger logger.Service) error { port := c.Int("proxy-dns-port") if port <= 0 || port > 65535 { - logger.Errorf("The 'proxy-dns-port' must be a valid port number in <1, 65535> range.") return errors.New("The 'proxy-dns-port' must be a valid port number in <1, 65535> range.") } - listener, err := tunneldns.CreateListener(c.String("proxy-dns-address"), uint16(port), c.StringSlice("proxy-dns-upstream"), c.StringSlice("proxy-dns-bootstrap")) + listener, err := tunneldns.CreateListener(c.String("proxy-dns-address"), uint16(port), c.StringSlice("proxy-dns-upstream"), c.StringSlice("proxy-dns-bootstrap"), logger) if err != nil { close(dnsReadySignal) listener.Stop() - logger.WithError(err).Error("Cannot create the DNS over HTTPS proxy server") return errors.Wrap(err, "Cannot create the DNS over HTTPS proxy server") } err = listener.Start(dnsReadySignal) if err != nil { - logger.WithError(err).Error("Cannot start the DNS over HTTPS proxy server") return errors.Wrap(err, "Cannot start the DNS over HTTPS proxy server") } <-shutdownC diff --git a/cmd/cloudflared/tunnel/signal.go b/cmd/cloudflared/tunnel/signal.go index b1ec77e2..41a92d69 100644 --- a/cmd/cloudflared/tunnel/signal.go +++ b/cmd/cloudflared/tunnel/signal.go @@ -5,12 +5,14 @@ import ( "os/signal" "syscall" "time" + + "github.com/cloudflare/cloudflared/logger" ) // waitForSignal notifies all routines to shutdownC immediately by closing the // shutdownC when one of the routines in main exits, or when this process receives // SIGTERM/SIGINT -func waitForSignal(errC chan error, shutdownC chan struct{}) error { +func waitForSignal(errC chan error, shutdownC chan struct{}, logger logger.Service) error { signals := make(chan os.Signal, 10) signal.Notify(signals, syscall.SIGTERM, syscall.SIGINT) defer signal.Stop(signals) @@ -39,6 +41,7 @@ func waitForSignal(errC chan error, shutdownC chan struct{}) error { func waitForSignalWithGraceShutdown(errC chan error, shutdownC, graceShutdownC chan struct{}, gracePeriod time.Duration, + logger logger.Service, ) error { signals := make(chan os.Signal, 10) signal.Notify(signals, syscall.SIGTERM, syscall.SIGINT) @@ -53,9 +56,9 @@ func waitForSignalWithGraceShutdown(errC chan error, case s := <-signals: logger.Infof("Initiating graceful shutdown due to signal %s ...", s) close(graceShutdownC) - waitForGracePeriod(signals, errC, shutdownC, gracePeriod) + waitForGracePeriod(signals, errC, shutdownC, gracePeriod, logger) case <-graceShutdownC: - waitForGracePeriod(signals, errC, shutdownC, gracePeriod) + waitForGracePeriod(signals, errC, shutdownC, gracePeriod, logger) case <-shutdownC: close(graceShutdownC) } @@ -67,6 +70,7 @@ func waitForGracePeriod(signals chan os.Signal, errC chan error, shutdownC chan struct{}, gracePeriod time.Duration, + logger logger.Service, ) { // Unregister signal handler early, so the client can send a second SIGTERM/SIGINT // to force shutdown cloudflared diff --git a/cmd/cloudflared/tunnel/signal_test.go b/cmd/cloudflared/tunnel/signal_test.go index 942706c2..0f5e735f 100644 --- a/cmd/cloudflared/tunnel/signal_test.go +++ b/cmd/cloudflared/tunnel/signal_test.go @@ -6,6 +6,7 @@ import ( "testing" "time" + "github.com/cloudflare/cloudflared/logger" "github.com/stretchr/testify/assert" ) @@ -27,6 +28,8 @@ func testChannelClosed(t *testing.T, c chan struct{}) { } func TestWaitForSignal(t *testing.T) { + logger := logger.NewOutputWriter(logger.NewMockWriteManager()) + // Test handling server error errC := make(chan error) shutdownC := make(chan struct{}) @@ -36,7 +39,7 @@ func TestWaitForSignal(t *testing.T) { }() // received error, shutdownC should be closed - err := waitForSignal(errC, shutdownC) + err := waitForSignal(errC, shutdownC, logger) assert.Equal(t, serverErr, err) testChannelClosed(t, shutdownC) @@ -56,7 +59,7 @@ func TestWaitForSignal(t *testing.T) { syscall.Kill(syscall.Getpid(), sig) }(sig) - err = waitForSignal(errC, shutdownC) + err = waitForSignal(errC, shutdownC, logger) assert.Equal(t, nil, err) assert.Equal(t, shutdownErr, <-errC) testChannelClosed(t, shutdownC) @@ -73,8 +76,10 @@ func TestWaitForSignalWithGraceShutdown(t *testing.T) { errC <- serverErr }() + logger := logger.NewOutputWriter(logger.NewMockWriteManager()) + // received error, both shutdownC and graceshutdownC should be closed - err := waitForSignalWithGraceShutdown(errC, shutdownC, graceshutdownC, tick) + err := waitForSignalWithGraceShutdown(errC, shutdownC, graceshutdownC, tick, logger) assert.Equal(t, serverErr, err) testChannelClosed(t, shutdownC) testChannelClosed(t, graceshutdownC) @@ -84,7 +89,7 @@ func TestWaitForSignalWithGraceShutdown(t *testing.T) { shutdownC = make(chan struct{}) graceshutdownC = make(chan struct{}) close(shutdownC) - err = waitForSignalWithGraceShutdown(errC, shutdownC, graceshutdownC, tick) + err = waitForSignalWithGraceShutdown(errC, shutdownC, graceshutdownC, tick, logger) assert.NoError(t, err) testChannelClosed(t, shutdownC) testChannelClosed(t, graceshutdownC) @@ -94,7 +99,7 @@ func TestWaitForSignalWithGraceShutdown(t *testing.T) { shutdownC = make(chan struct{}) graceshutdownC = make(chan struct{}) close(graceshutdownC) - err = waitForSignalWithGraceShutdown(errC, shutdownC, graceshutdownC, tick) + err = waitForSignalWithGraceShutdown(errC, shutdownC, graceshutdownC, tick, logger) assert.NoError(t, err) testChannelClosed(t, shutdownC) testChannelClosed(t, graceshutdownC) @@ -117,7 +122,7 @@ func TestWaitForSignalWithGraceShutdown(t *testing.T) { syscall.Kill(syscall.Getpid(), sig) }(sig) - err = waitForSignalWithGraceShutdown(errC, shutdownC, graceshutdownC, tick) + err = waitForSignalWithGraceShutdown(errC, shutdownC, graceshutdownC, tick, logger) assert.Equal(t, nil, err) assert.Equal(t, graceShutdownErr, <-errC) testChannelClosed(t, shutdownC) @@ -143,7 +148,7 @@ func TestWaitForSignalWithGraceShutdown(t *testing.T) { syscall.Kill(syscall.Getpid(), sig) }(sig) - err = waitForSignalWithGraceShutdown(errC, shutdownC, graceshutdownC, tick) + err = waitForSignalWithGraceShutdown(errC, shutdownC, graceshutdownC, tick, logger) assert.Equal(t, nil, err) assert.Equal(t, shutdownErr, <-errC) testChannelClosed(t, shutdownC) diff --git a/cmd/cloudflared/tunnel/subcommands.go b/cmd/cloudflared/tunnel/subcommands.go index f9493f57..9a6bb517 100644 --- a/cmd/cloudflared/tunnel/subcommands.go +++ b/cmd/cloudflared/tunnel/subcommands.go @@ -12,14 +12,15 @@ import ( "github.com/cloudflare/cloudflared/certutil" "github.com/cloudflare/cloudflared/cmd/cloudflared/cliutil" + "github.com/cloudflare/cloudflared/logger" "github.com/cloudflare/cloudflared/tunnelstore" ) var ( outputFormatFlag = &cli.StringFlag{ - Name: "output", + Name: "output", Aliases: []string{"o"}, - Usage: "Render output using given `FORMAT`. Valid options are 'json' or 'yaml'", + Usage: "Render output using given `FORMAT`. Valid options are 'json' or 'yaml'", } ) @@ -42,7 +43,12 @@ func createTunnel(c *cli.Context) error { } name := c.Args().First() - client, err := newTunnelstoreClient(c) + logger, err := logger.New() + if err != nil { + return errors.Wrap(err, "error setting up logger") + } + + client, err := newTunnelstoreClient(c, logger) if err != nil { return err } @@ -72,7 +78,12 @@ func buildListCommand() *cli.Command { } func listTunnels(c *cli.Context) error { - client, err := newTunnelstoreClient(c) + logger, err := logger.New() + if err != nil { + return errors.Wrap(err, "error setting up logger") + } + + client, err := newTunnelstoreClient(c, logger) if err != nil { return err } @@ -114,7 +125,12 @@ func deleteTunnel(c *cli.Context) error { } id := c.Args().First() - client, err := newTunnelstoreClient(c) + logger, err := logger.New() + if err != nil { + return errors.Wrap(err, "error setting up logger") + } + + client, err := newTunnelstoreClient(c, logger) if err != nil { return err } @@ -139,13 +155,13 @@ func renderOutput(format string, v interface{}) error { } } -func newTunnelstoreClient(c *cli.Context) (tunnelstore.Client, error) { - originCertPath, err := findOriginCert(c) +func newTunnelstoreClient(c *cli.Context, logger logger.Service) (tunnelstore.Client, error) { + originCertPath, err := findOriginCert(c, logger) if err != nil { return nil, errors.Wrap(err, "Error locating origin cert") } - blocks, err := readOriginCert(originCertPath) + blocks, err := readOriginCert(originCertPath, logger) if err != nil { return nil, errors.Wrapf(err, "Can't read origin cert from %s", originCertPath) } @@ -159,7 +175,7 @@ func newTunnelstoreClient(c *cli.Context) (tunnelstore.Client, error) { return nil, errors.Errorf(`Origin certificate needs to be refreshed before creating new tunnels.\nDelete %s and run "cloudflared login" to obtain a new cert.`, originCertPath) } - client := tunnelstore.NewRESTClient(c.String("api-url"), cert.AccountID, cert.ServiceKey) + client := tunnelstore.NewRESTClient(c.String("api-url"), cert.AccountID, cert.ServiceKey, logger) return client, nil } diff --git a/cmd/cloudflared/updater/update.go b/cmd/cloudflared/updater/update.go index 8f9c481b..4de6b406 100644 --- a/cmd/cloudflared/updater/update.go +++ b/cmd/cloudflared/updater/update.go @@ -10,9 +10,10 @@ import ( "golang.org/x/crypto/ssh/terminal" "gopkg.in/urfave/cli.v2" - "github.com/cloudflare/cloudflared/log" + "github.com/cloudflare/cloudflared/logger" "github.com/equinox-io/equinox" "github.com/facebookgo/grace/gracenet" + "github.com/pkg/errors" ) const ( @@ -30,7 +31,6 @@ dsCmJ/QZ6aw0w9qkkwEpne1Lmo6+0pGexZzFZOH6w5amShn+RXt7qkSid9iWlzGq EKx0BZogHSor9Wy5VztdFaAaVbsJiCbO -----END ECDSA PUBLIC KEY----- `) - logger = log.CreateLogger() ) // BinaryUpdated implements ExitCoder interface, the app will exit with status code 11 @@ -93,7 +93,12 @@ func checkForUpdateAndApply() UpdateOutcome { } func Update(_ *cli.Context) error { - updateOutcome := loggedUpdate() + logger, err := logger.New() + if err != nil { + return errors.Wrap(err, "error setting up logger") + } + + updateOutcome := loggedUpdate(logger) if updateOutcome.Error != nil { return &statusErr{updateOutcome.Error} } @@ -107,13 +112,13 @@ func Update(_ *cli.Context) error { } // Checks for an update and applies it if one is available -func loggedUpdate() UpdateOutcome { +func loggedUpdate(logger logger.Service) UpdateOutcome { updateOutcome := checkForUpdateAndApply() if updateOutcome.Updated { logger.Infof("cloudflared has been updated to version %s", updateOutcome.Version) } if updateOutcome.Error != nil { - logger.WithError(updateOutcome.Error).Error("update check failed") + logger.Errorf("update check failed: %s", updateOutcome.Error) } return updateOutcome @@ -124,6 +129,7 @@ type AutoUpdater struct { configurable *configurable listeners *gracenet.Net updateConfigChan chan *configurable + logger logger.Service } // AutoUpdaterConfigurable is the attributes of AutoUpdater that can be reconfigured during runtime @@ -132,7 +138,7 @@ type configurable struct { freq time.Duration } -func NewAutoUpdater(freq time.Duration, listeners *gracenet.Net) *AutoUpdater { +func NewAutoUpdater(freq time.Duration, listeners *gracenet.Net, logger logger.Service) *AutoUpdater { updaterConfigurable := &configurable{ enabled: true, freq: freq, @@ -145,6 +151,7 @@ func NewAutoUpdater(freq time.Duration, listeners *gracenet.Net) *AutoUpdater { configurable: updaterConfigurable, listeners: listeners, updateConfigChan: make(chan *configurable), + logger: logger, } } @@ -152,20 +159,20 @@ func (a *AutoUpdater) Run(ctx context.Context) error { ticker := time.NewTicker(a.configurable.freq) for { if a.configurable.enabled { - updateOutcome := loggedUpdate() + updateOutcome := loggedUpdate(a.logger) if updateOutcome.Updated { os.Args = append(os.Args, "--is-autoupdated=true") if IsSysV() { // SysV doesn't have a mechanism to keep service alive, we have to restart the process - logger.Infof("Restarting service managed by SysV...") + a.logger.Info("Restarting service managed by SysV...") pid, err := a.listeners.StartProcess() if err != nil { - logger.WithError(err).Error("Unable to restart server automatically") + a.logger.Errorf("Unable to restart server automatically: %s", err) return &statusErr{err: err} } // stop old process after autoupdate. Otherwise we create a new process // after each update - logger.Infof("PID of the new process is %d", pid) + a.logger.Infof("PID of the new process is %d", pid) } return &statusSuccess{newVersion: updateOutcome.Version} } @@ -197,14 +204,14 @@ func (a *AutoUpdater) Update(newFreq time.Duration) { a.updateConfigChan <- newConfigurable } -func IsAutoupdateEnabled(c *cli.Context) bool { - if !SupportAutoUpdate() { +func IsAutoupdateEnabled(c *cli.Context, l logger.Service) bool { + if !SupportAutoUpdate(l) { return false } return !c.Bool("no-autoupdate") && c.Duration("autoupdate-freq") != 0 } -func SupportAutoUpdate() bool { +func SupportAutoUpdate(logger logger.Service) bool { if runtime.GOOS == "windows" { logger.Info(noUpdateOnWindowsMessage) return false diff --git a/cmd/cloudflared/updater/update_test.go b/cmd/cloudflared/updater/update_test.go index 218b22b4..d4ad93ee 100644 --- a/cmd/cloudflared/updater/update_test.go +++ b/cmd/cloudflared/updater/update_test.go @@ -4,13 +4,15 @@ import ( "context" "testing" + "github.com/cloudflare/cloudflared/logger" "github.com/facebookgo/grace/gracenet" "github.com/stretchr/testify/assert" ) func TestDisabledAutoUpdater(t *testing.T) { listeners := &gracenet.Net{} - autoupdater := NewAutoUpdater(0, listeners) + logger := logger.NewOutputWriter(logger.NewMockWriteManager()) + autoupdater := NewAutoUpdater(0, listeners, logger) ctx, cancel := context.WithCancel(context.Background()) errC := make(chan error) go func() { diff --git a/cmd/cloudflared/windows_service.go b/cmd/cloudflared/windows_service.go index 1c25699d..1cc34990 100644 --- a/cmd/cloudflared/windows_service.go +++ b/cmd/cloudflared/windows_service.go @@ -12,8 +12,11 @@ import ( "time" "unsafe" + "github.com/cloudflare/cloudflared/logger" + "github.com/pkg/errors" cli "gopkg.in/urfave/cli.v2" + "github.com/pkg/errors" "golang.org/x/sys/windows" "golang.org/x/sys/windows/svc" "golang.org/x/sys/windows/svc/eventlog" @@ -65,6 +68,12 @@ func runApp(app *cli.App, shutdownC, graceShutdownC chan struct{}) { // 2. get ERROR_FAILED_SERVICE_CONTROLLER_CONNECT // This involves actually trying to start the service. + logger, err := logger.New() + if err != nil { + os.Exit(1) + return + } + isIntSess, err := svc.IsAnInteractiveSession() if err != nil { logger.Fatalf("failed to determine if we are running in an interactive session: %v", err) @@ -97,9 +106,15 @@ type windowsService struct { // called by the package code at the start of the service func (s *windowsService) Execute(serviceArgs []string, r <-chan svc.ChangeRequest, statusChan chan<- svc.Status) (ssec bool, errno uint32) { + logger, err := logger.New() + if err != nil { + os.Exit(1) + return + } + elog, err := eventlog.Open(windowsServiceName) if err != nil { - logger.WithError(err).Errorf("Cannot open event log for %s", windowsServiceName) + logger.Errorf("Cannot open event log for %s with error: %s", windowsServiceName, err) return } defer elog.Close() @@ -160,6 +175,11 @@ func (s *windowsService) Execute(serviceArgs []string, r <-chan svc.ChangeReques } func installWindowsService(c *cli.Context) error { + logger, err := logger.New() + if err != nil { + return errors.Wrap(err, "error setting up logger") + } + logger.Infof("Installing Argo Tunnel Windows service") exepath, err := os.Executable() if err != nil { @@ -168,7 +188,7 @@ func installWindowsService(c *cli.Context) error { } m, err := mgr.Connect() if err != nil { - logger.WithError(err).Errorf("Cannot establish a connection to the service control manager") + logger.Errorf("Cannot establish a connection to the service control manager: %s", err) return err } defer m.Disconnect() @@ -189,18 +209,23 @@ func installWindowsService(c *cli.Context) error { err = eventlog.InstallAsEventCreate(windowsServiceName, eventlog.Error|eventlog.Warning|eventlog.Info) if err != nil { s.Delete() - logger.WithError(err).Errorf("Cannot install event logger") + logger.Errorf("Cannot install event logger: %s", err) return fmt.Errorf("SetupEventLogSource() failed: %s", err) } err = configRecoveryOption(s.Handle) if err != nil { - logger.WithError(err).Errorf("Cannot set service recovery actions") + logger.Errorf("Cannot set service recovery actions: %s", err) logger.Infof("See %s to manually configure service recovery actions", windowsServiceUrl) } return nil } func uninstallWindowsService(c *cli.Context) error { + logger, err := logger.New() + if err != nil { + return errors.Wrap(err, "error setting up logger") + } + logger.Infof("Uninstalling Argo Tunnel Windows Service") m, err := mgr.Connect() if err != nil { diff --git a/connection/connection.go b/connection/connection.go index b898403f..a49bf966 100644 --- a/connection/connection.go +++ b/connection/connection.go @@ -7,9 +7,9 @@ import ( "github.com/google/uuid" "github.com/pkg/errors" - "github.com/sirupsen/logrus" "github.com/cloudflare/cloudflared/h2mux" + "github.com/cloudflare/cloudflared/logger" tunnelpogs "github.com/cloudflare/cloudflared/tunnelrpc/pogs" ) @@ -43,8 +43,8 @@ func (c *Connection) Serve(ctx context.Context) error { } // Connect is used to establish connections with cloudflare's edge network -func (c *Connection) Connect(ctx context.Context, parameters *tunnelpogs.ConnectParameters, logger *logrus.Entry) (tunnelpogs.ConnectResult, error) { - tsClient, err := NewRPCClient(ctx, c.muxer, logger.WithField("rpc", "connect"), openStreamTimeout) +func (c *Connection) Connect(ctx context.Context, parameters *tunnelpogs.ConnectParameters, logger logger.Service) (tunnelpogs.ConnectResult, error) { + tsClient, err := NewRPCClient(ctx, c.muxer, logger, openStreamTimeout) if err != nil { return nil, errors.Wrap(err, "cannot create new RPC connection") } diff --git a/connection/manager.go b/connection/manager.go index eeac2e01..00f85ba7 100644 --- a/connection/manager.go +++ b/connection/manager.go @@ -10,11 +10,11 @@ import ( "github.com/google/uuid" "github.com/pkg/errors" "github.com/prometheus/client_golang/prometheus" - "github.com/sirupsen/logrus" "github.com/cloudflare/cloudflared/cmd/cloudflared/buildinfo" "github.com/cloudflare/cloudflared/edgediscovery" "github.com/cloudflare/cloudflared/h2mux" + "github.com/cloudflare/cloudflared/logger" "github.com/cloudflare/cloudflared/streamhandler" tunnelpogs "github.com/cloudflare/cloudflared/tunnelrpc/pogs" ) @@ -40,7 +40,7 @@ type EdgeManager struct { // state is attributes of ConnectionManager that can change during runtime. state *edgeManagerState - logger *logrus.Entry + logger logger.Service metrics *metrics } @@ -76,7 +76,7 @@ func NewEdgeManager( tlsConfig *tls.Config, serviceDiscoverer *edgediscovery.Edge, cloudflaredConfig *CloudflaredConfig, - logger *logrus.Logger, + logger logger.Service, ) *EdgeManager { return &EdgeManager{ streamHandler: streamHandler, @@ -84,7 +84,7 @@ func NewEdgeManager( cloudflaredConfig: cloudflaredConfig, serviceDiscoverer: serviceDiscoverer, state: newEdgeConnectionManagerState(edgeConnMgrConfigurable, userCredential), - logger: logger.WithField("subsystem", "connectionManager"), + logger: logger, metrics: newMetrics(packageNamespace, edgeManagerSubsystem), } } @@ -109,16 +109,16 @@ func (em *EdgeManager) Run(ctx context.Context) error { if em.state.shouldCreateConnection(em.serviceDiscoverer.AvailableAddrs()) { if connErr := em.newConnection(ctx, connIndex); connErr != nil { if !connErr.ShouldRetry { - em.logger.WithError(connErr).Error(em.noRetryMessage()) + em.logger.Errorf("connectionManager: %s with error: %s", em.noRetryMessage(), connErr) return connErr } - em.logger.WithError(connErr).Error("cannot create new connection") + em.logger.Errorf("connectionManager: cannot create new connection: %s", connErr) } else { connIndex++ } } else if em.state.shouldReduceConnection() { if err := em.closeConnection(ctx); err != nil { - em.logger.WithError(err).Error("cannot close connection") + em.logger.Errorf("connectionManager: cannot close connection: %s", err) } } } @@ -147,7 +147,7 @@ func (em *EdgeManager) newConnection(ctx context.Context, index int) *tunnelpogs IsClient: true, HeartbeatInterval: configurable.HeartbeatInterval, MaxHeartbeats: configurable.MaxFailedHeartbeats, - Logger: em.logger.WithField("subsystem", "muxer"), + Logger: em.logger, }, em.metrics.activeStreams) if err != nil { retryConnection(fmt.Sprintf("couldn't perform handshake with edge: %v", err)) @@ -178,7 +178,7 @@ func (em *EdgeManager) newConnection(ctx context.Context, index int) *tunnelpogs } em.state.newConnection(h2muxConn) - em.logger.Infof("connected to %s", connResult.ConnectedTo()) + em.logger.Infof("connectionManager: connected to %s", connResult.ConnectedTo()) if connResult.ClientConfig() != nil { em.streamHandler.UseConfiguration(ctx, connResult.ClientConfig()) @@ -198,7 +198,7 @@ func (em *EdgeManager) closeConnection(ctx context.Context) error { func (em *EdgeManager) serveConn(ctx context.Context, conn *Connection) { err := conn.Serve(ctx) - em.logger.WithError(err).Warn("Connection closed") + em.logger.Errorf("connectionManager: Connection closed: %s", err) em.state.closeConnection(conn) em.serviceDiscoverer.GiveBack(conn.addr) } diff --git a/connection/manager_test.go b/connection/manager_test.go index 465fc951..791c5add 100644 --- a/connection/manager_test.go +++ b/connection/manager_test.go @@ -6,12 +6,12 @@ import ( "time" "github.com/google/uuid" - "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" "github.com/cloudflare/cloudflared/cmd/cloudflared/buildinfo" "github.com/cloudflare/cloudflared/edgediscovery" "github.com/cloudflare/cloudflared/h2mux" + "github.com/cloudflare/cloudflared/logger" "github.com/cloudflare/cloudflared/streamhandler" "github.com/cloudflare/cloudflared/tunnelrpc/pogs" ) @@ -48,7 +48,7 @@ var ( func mockEdgeManager() *EdgeManager { newConfigChan := make(chan<- *pogs.ClientConfig) useConfigResultChan := make(<-chan *pogs.UseConfigurationResult) - logger := logrus.New() + logger := logger.NewOutputWriter(logger.NewMockWriteManager()) edge := edgediscovery.MockEdge(logger, []*net.TCPAddr{}) return NewEdgeManager( streamhandler.NewStreamHandler(newConfigChan, useConfigResultChan, logger), diff --git a/connection/rpc.go b/connection/rpc.go index 9c10c334..8c125d92 100644 --- a/connection/rpc.go +++ b/connection/rpc.go @@ -5,10 +5,10 @@ import ( "fmt" "time" - "github.com/sirupsen/logrus" rpc "zombiezen.com/go/capnproto2/rpc" "github.com/cloudflare/cloudflared/h2mux" + "github.com/cloudflare/cloudflared/logger" "github.com/cloudflare/cloudflared/tunnelrpc" tunnelpogs "github.com/cloudflare/cloudflared/tunnelrpc/pogs" ) @@ -18,7 +18,7 @@ import ( func NewRPCClient( ctx context.Context, muxer *h2mux.Muxer, - logger *logrus.Entry, + logger logger.Service, openStreamTimeout time.Duration, ) (client tunnelpogs.TunnelServer_PogsClient, err error) { openStreamCtx, openStreamCancel := context.WithTimeout(ctx, openStreamTimeout) diff --git a/dbconnect/proxy.go b/dbconnect/proxy.go index 2a1a53ad..fbdfb0b9 100644 --- a/dbconnect/proxy.go +++ b/dbconnect/proxy.go @@ -11,17 +11,17 @@ import ( "time" "github.com/cloudflare/cloudflared/hello" + "github.com/cloudflare/cloudflared/logger" "github.com/cloudflare/cloudflared/validation" "github.com/gorilla/mux" "github.com/pkg/errors" - "github.com/sirupsen/logrus" ) // Proxy is an HTTP server that proxies requests to a Client. type Proxy struct { client Client accessValidator *validation.Access - logger *logrus.Logger + logger logger.Service } // NewInsecureProxy creates a Proxy that talks to a Client at an origin. @@ -43,7 +43,12 @@ func NewInsecureProxy(ctx context.Context, origin string) (*Proxy, error) { return nil, errors.Wrap(err, "could not connect to the database") } - return &Proxy{client, nil, logrus.New()}, nil + logger, err := logger.New() + if err != nil { + return nil, errors.Wrap(err, "error setting up logger") + } + + return &Proxy{client, nil, logger}, nil } // NewSecureProxy creates a Proxy that talks to a Client at an origin. @@ -90,7 +95,8 @@ func (proxy *Proxy) IsAllowed(r *http.Request, verbose ...bool) bool { // Warn administrators that invalid JWTs are being rejected. This is indicative // of either a misconfiguration of the CLI or a massive failure of upstream systems. if len(verbose) > 0 { - proxy.httpLog(r, err).Error("Failed JWT authentication") + cfRay := proxy.getRayHeader(r) + proxy.logger.Infof("dbproxy: Failed JWT authentication: cf-ray: %s %s", cfRay, err) } return false @@ -234,13 +240,14 @@ func (proxy *Proxy) httpRespondErr(w http.ResponseWriter, r *http.Request, defau proxy.httpRespond(w, r, status, err.Error()) if len(err.Error()) > 0 { - proxy.httpLog(r, err).Warn("Database proxy error") + cfRay := proxy.getRayHeader(r) + proxy.logger.Infof("dbproxy: Database proxy error: cf-ray: %s %s", cfRay, err) } } -// httpLog returns a logrus.Entry that is formatted to output a request Cf-ray. -func (proxy *Proxy) httpLog(r *http.Request, err error) *logrus.Entry { - return proxy.logger.WithContext(r.Context()).WithField("CF-RAY", r.Header.Get("Cf-ray")).WithError(err) +// getRayHeader returns the request's Cf-ray header. +func (proxy *Proxy) getRayHeader(r *http.Request) string { + return r.Header.Get("Cf-ray") } // httpError extracts common errors and returns an status code and friendly error. diff --git a/edgediscovery/allregions/discovery.go b/edgediscovery/allregions/discovery.go index b18e3df6..8640f494 100644 --- a/edgediscovery/allregions/discovery.go +++ b/edgediscovery/allregions/discovery.go @@ -7,8 +7,8 @@ import ( "net" "time" + "github.com/cloudflare/cloudflared/logger" "github.com/pkg/errors" - "github.com/sirupsen/logrus" ) const ( @@ -58,15 +58,15 @@ var friendlyDNSErrorLines = []string{ } // EdgeDiscovery implements HA service discovery lookup. -func edgeDiscovery(logger *logrus.Entry) ([][]*net.TCPAddr, error) { +func edgeDiscovery(logger logger.Service) ([][]*net.TCPAddr, error) { _, addrs, err := netLookupSRV(srvService, srvProto, srvName) if err != nil { _, fallbackAddrs, fallbackErr := fallbackLookupSRV(srvService, srvProto, srvName) if fallbackErr != nil || len(fallbackAddrs) == 0 { // use the original DNS error `err` in messages, not `fallbackErr` - logger.Errorln("Error looking up Cloudflare edge IPs: the DNS query failed:", err) + logger.Errorf("Error looking up Cloudflare edge IPs: the DNS query failed: %s", err) for _, s := range friendlyDNSErrorLines { - logger.Errorln(s) + logger.Error(s) } return nil, errors.Wrapf(err, "Could not lookup srv records on _%v._%v.%v", srvService, srvProto, srvName) } diff --git a/edgediscovery/allregions/discovery_test.go b/edgediscovery/allregions/discovery_test.go index 0ead20bc..b2be4350 100644 --- a/edgediscovery/allregions/discovery_test.go +++ b/edgediscovery/allregions/discovery_test.go @@ -3,7 +3,7 @@ package allregions import ( "testing" - "github.com/sirupsen/logrus" + "github.com/cloudflare/cloudflared/logger" "github.com/stretchr/testify/assert" ) @@ -19,7 +19,8 @@ func TestEdgeDiscovery(t *testing.T) { } } - addrLists, err := edgeDiscovery(logrus.New().WithFields(logrus.Fields{})) + l := logger.NewOutputWriter(logger.NewMockWriteManager()) + addrLists, err := edgeDiscovery(l) assert.NoError(t, err) actualAddrSet := map[string]bool{} for _, addrs := range addrLists { diff --git a/edgediscovery/allregions/region.go b/edgediscovery/allregions/region.go index bf17a496..aee3fd93 100644 --- a/edgediscovery/allregions/region.go +++ b/edgediscovery/allregions/region.go @@ -2,8 +2,6 @@ package allregions import ( "net" - - "github.com/sirupsen/logrus" ) // Region contains cloudflared edge addresses. The edge is partitioned into several regions for @@ -59,7 +57,7 @@ func (r Region) GetUnusedIP(excluding *net.TCPAddr) *net.TCPAddr { // Use the address, assigning it to a proxy connection. func (r Region) Use(addr *net.TCPAddr, connID int) { if addr == nil { - logrus.Errorf("Attempted to use nil address for connection %d", connID) + //logrus.Errorf("Attempted to use nil address for connection %d", connID) return } r.connFor[addr] = InUse(connID) diff --git a/edgediscovery/allregions/regions.go b/edgediscovery/allregions/regions.go index d51c3299..bacc75a1 100644 --- a/edgediscovery/allregions/regions.go +++ b/edgediscovery/allregions/regions.go @@ -4,7 +4,7 @@ import ( "fmt" "net" - "github.com/sirupsen/logrus" + "github.com/cloudflare/cloudflared/logger" ) // Regions stores Cloudflare edge network IPs, partitioned into two regions. @@ -19,7 +19,7 @@ type Regions struct { // ------------------------------------ // ResolveEdge resolves the Cloudflare edge, returning all regions discovered. -func ResolveEdge(logger *logrus.Entry) (*Regions, error) { +func ResolveEdge(logger logger.Service) (*Regions, error) { addrLists, err := edgeDiscovery(logger) if err != nil { return nil, err diff --git a/edgediscovery/edgediscovery.go b/edgediscovery/edgediscovery.go index 0b90efce..c4db7249 100644 --- a/edgediscovery/edgediscovery.go +++ b/edgediscovery/edgediscovery.go @@ -6,8 +6,7 @@ import ( "sync" "github.com/cloudflare/cloudflared/edgediscovery/allregions" - - "github.com/sirupsen/logrus" + "github.com/cloudflare/cloudflared/logger" ) const ( @@ -20,7 +19,7 @@ var errNoAddressesLeft = fmt.Errorf("There are no free edge addresses left") type Edge struct { regions *allregions.Regions sync.Mutex - logger *logrus.Entry + logger logger.Service } // ------------------------------------ @@ -29,37 +28,34 @@ type Edge struct { // ResolveEdge runs the initial discovery of the Cloudflare edge, finding Addrs that can be allocated // to connections. -func ResolveEdge(l *logrus.Logger) (*Edge, error) { - logger := l.WithField("subsystem", subsystem) - regions, err := allregions.ResolveEdge(logger) +func ResolveEdge(l logger.Service) (*Edge, error) { + regions, err := allregions.ResolveEdge(l) if err != nil { return new(Edge), err } return &Edge{ - logger: logger, + logger: l, regions: regions, }, nil } // StaticEdge creates a list of edge addresses from the list of hostnames. Mainly used for testing connectivity. -func StaticEdge(l *logrus.Logger, hostnames []string) (*Edge, error) { - logger := l.WithField("subsystem", subsystem) +func StaticEdge(l logger.Service, hostnames []string) (*Edge, error) { regions, err := allregions.StaticEdge(hostnames) if err != nil { return new(Edge), err } return &Edge{ - logger: logger, + logger: l, regions: regions, }, nil } // MockEdge creates a Cloudflare Edge from arbitrary TCP addresses. Used for testing. -func MockEdge(l *logrus.Logger, addrs []*net.TCPAddr) *Edge { - logger := l.WithField("subsystem", subsystem) +func MockEdge(l logger.Service, addrs []*net.TCPAddr) *Edge { regions := allregions.NewNoResolve(addrs) return &Edge{ - logger: logger, + logger: l, regions: regions, } } @@ -83,24 +79,20 @@ func (ed *Edge) GetAddrForRPC() (*net.TCPAddr, error) { func (ed *Edge) GetAddr(connID int) (*net.TCPAddr, error) { ed.Lock() defer ed.Unlock() - logger := ed.logger.WithFields(logrus.Fields{ - "connID": connID, - "function": "GetAddr", - }) // If this connection has already used an edge addr, return it. if addr := ed.regions.AddrUsedBy(connID); addr != nil { - logger.Debug("Returning same address back to proxy connection") + ed.logger.Debugf("edgediscovery - GetAddr: Returning same address back to proxy connection: connID: %d", connID) return addr, nil } // Otherwise, give it an unused one addr := ed.regions.GetUnusedAddr(nil, connID) if addr == nil { - logger.Debug("No addresses left to give proxy connection") + ed.logger.Debugf("edgediscovery - GetAddr: No addresses left to give proxy connection: connID: %d", connID) return nil, errNoAddressesLeft } - logger.Debugf("Giving connection its new address %s", addr) + ed.logger.Debugf("edgediscovery - GetAddr: Giving connection its new address %s: connID: %d", addr, connID) return addr, nil } @@ -108,10 +100,6 @@ func (ed *Edge) GetAddr(connID int) (*net.TCPAddr, error) { func (ed *Edge) GetDifferentAddr(connID int) (*net.TCPAddr, error) { ed.Lock() defer ed.Unlock() - logger := ed.logger.WithFields(logrus.Fields{ - "connID": connID, - "function": "GetDifferentAddr", - }) oldAddr := ed.regions.AddrUsedBy(connID) if oldAddr != nil { @@ -119,11 +107,11 @@ func (ed *Edge) GetDifferentAddr(connID int) (*net.TCPAddr, error) { } addr := ed.regions.GetUnusedAddr(oldAddr, connID) if addr == nil { - logger.Debug("No addresses left to give proxy connection") + ed.logger.Debugf("edgediscovery - GetDifferentAddr: No addresses left to give proxy connection: connID: %d", connID) // note: if oldAddr were not nil, it will become available on the next iteration return nil, errNoAddressesLeft } - logger.Debugf("Giving connection its new address %s", addr) + ed.logger.Debugf("edgediscovery - GetDifferentAddr: Giving connection its new address %s: connID: %d", addr, connID) return addr, nil } @@ -139,6 +127,6 @@ func (ed *Edge) AvailableAddrs() int { func (ed *Edge) GiveBack(addr *net.TCPAddr) bool { ed.Lock() defer ed.Unlock() - ed.logger.WithField("function", "GiveBack").Debug("Address now unused") + ed.logger.Debug("edgediscovery - GiveBack: Address now unused") return ed.regions.GiveBack(addr) } diff --git a/edgediscovery/edgediscovery_test.go b/edgediscovery/edgediscovery_test.go index 4b111be1..3439861a 100644 --- a/edgediscovery/edgediscovery_test.go +++ b/edgediscovery/edgediscovery_test.go @@ -4,7 +4,7 @@ import ( "net" "testing" - "github.com/sirupsen/logrus" + "github.com/cloudflare/cloudflared/logger" "github.com/stretchr/testify/assert" ) @@ -32,7 +32,7 @@ var ( ) func TestGiveBack(t *testing.T) { - l := logrus.New() + l := logger.NewOutputWriter(logger.NewMockWriteManager()) edge := MockEdge(l, []*net.TCPAddr{&addr0, &addr1, &addr2, &addr3}) // Give this connection an address @@ -49,7 +49,7 @@ func TestGiveBack(t *testing.T) { } func TestRPCAndProxyShareSingleEdgeIP(t *testing.T) { - l := logrus.New() + l := logger.NewOutputWriter(logger.NewMockWriteManager()) // Make an edge with a single IP edge := MockEdge(l, []*net.TCPAddr{&addr0}) @@ -66,7 +66,7 @@ func TestRPCAndProxyShareSingleEdgeIP(t *testing.T) { } func TestGetAddrForRPC(t *testing.T) { - l := logrus.New() + l := logger.NewOutputWriter(logger.NewMockWriteManager()) edge := MockEdge(l, []*net.TCPAddr{&addr0, &addr1, &addr2, &addr3}) // Get a connection @@ -84,7 +84,7 @@ func TestGetAddrForRPC(t *testing.T) { } func TestOnePerRegion(t *testing.T) { - l := logrus.New() + l := logger.NewOutputWriter(logger.NewMockWriteManager()) // Make an edge with only one address edge := MockEdge(l, []*net.TCPAddr{&addr0, &addr1}) @@ -108,7 +108,7 @@ func TestOnePerRegion(t *testing.T) { } func TestOnlyOneAddrLeft(t *testing.T) { - l := logrus.New() + l := logger.NewOutputWriter(logger.NewMockWriteManager()) // Make an edge with only one address edge := MockEdge(l, []*net.TCPAddr{&addr0}) @@ -130,7 +130,7 @@ func TestOnlyOneAddrLeft(t *testing.T) { } func TestNoAddrsLeft(t *testing.T) { - l := logrus.New() + l := logger.NewOutputWriter(logger.NewMockWriteManager()) // Make an edge with no addresses edge := MockEdge(l, []*net.TCPAddr{}) @@ -142,7 +142,7 @@ func TestNoAddrsLeft(t *testing.T) { } func TestGetAddr(t *testing.T) { - l := logrus.New() + l := logger.NewOutputWriter(logger.NewMockWriteManager()) edge := MockEdge(l, []*net.TCPAddr{&addr0, &addr1, &addr2, &addr3}) // Give this connection an address @@ -158,7 +158,7 @@ func TestGetAddr(t *testing.T) { } func TestGetDifferentAddr(t *testing.T) { - l := logrus.New() + l := logger.NewOutputWriter(logger.NewMockWriteManager()) edge := MockEdge(l, []*net.TCPAddr{&addr0, &addr1, &addr2, &addr3}) // Give this connection an address diff --git a/go.mod b/go.mod index 974a0b3f..85aee5c3 100644 --- a/go.mod +++ b/go.mod @@ -5,12 +5,14 @@ go 1.12 require ( github.com/BurntSushi/go-sumtype v0.0.0-20190304192233-fcb4a6205bdc // indirect github.com/DATA-DOG/go-sqlmock v1.3.3 + github.com/acmacalister/skittles v0.0.0-20160609003031-7423546701e1 + github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4 github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 // indirect github.com/aws/aws-sdk-go v1.25.8 github.com/beorn7/perks v1.0.1 // indirect github.com/certifi/gocertifi v0.0.0-20200211180108-c7c1fbc02894 // indirect github.com/cloudflare/brotli-go v0.0.0-20191101163834-d34379f7ff93 - github.com/cloudflare/cfssl v0.0.0-20141119014638-2f7f44e802e2 + github.com/cloudflare/cfssl v0.0.0-20141119014638-2f7f44e802e2 // indirect github.com/cloudflare/golibs v0.0.0-20170913112048-333127dbecfc github.com/coredns/coredns v1.2.0 github.com/coreos/go-oidc v0.0.0-20171002155002-a93f71fdfe73 @@ -37,7 +39,7 @@ require ( github.com/kshvakov/clickhouse v1.3.11 github.com/kylelemons/godebug v1.1.0 // indirect github.com/lib/pq v1.2.0 - github.com/mattn/go-colorable v0.1.4 + github.com/mattn/go-colorable v0.1.4 // indirect github.com/mattn/go-isatty v0.0.10 // indirect github.com/mattn/go-sqlite3 v1.11.0 github.com/mholt/caddy v0.0.0-20180807230124-d3b731e9255b // indirect @@ -51,8 +53,8 @@ require ( github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 // indirect github.com/prometheus/common v0.7.0 // indirect github.com/prometheus/procfs v0.0.5 // indirect - github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5 - github.com/sirupsen/logrus v1.4.2 + github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5 // indirect + github.com/sirupsen/logrus v1.4.2 // indirect github.com/stretchr/testify v1.3.0 github.com/tinylib/msgp v1.1.0 // indirect github.com/xo/dburl v0.0.0-20191005012637-293c3298d6c0 diff --git a/go.sum b/go.sum index 5c41f7d0..774e3261 100644 --- a/go.sum +++ b/go.sum @@ -10,7 +10,10 @@ github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo4seqhv0i0kdATSkM0= github.com/GeertJohan/go.rice v1.0.0/go.mod h1:eH6gbSOAUv07dQuZVnBmoDP8mgsM1rtixis4Tib9if0= github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c= +github.com/acmacalister/skittles v0.0.0-20160609003031-7423546701e1 h1:RKnVV4C7qoN/sToLX2y1dqH7T6kKLMHcwRJlgwb9Ggk= +github.com/acmacalister/skittles v0.0.0-20160609003031-7423546701e1/go.mod h1:gI5CyA/CEnS6eqNV22rqs4dG3aGfaSbXgPORIlwr2r0= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4 h1:Hs82Z41s6SdL1CELW+XaDYmOH4hkBN4/N9og/AsOv7E= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= diff --git a/h2mux/h2mux.go b/h2mux/h2mux.go index 6f7d407d..a9af47bb 100644 --- a/h2mux/h2mux.go +++ b/h2mux/h2mux.go @@ -7,8 +7,8 @@ import ( "sync" "time" + "github.com/cloudflare/cloudflared/logger" "github.com/prometheus/client_golang/prometheus" - log "github.com/sirupsen/logrus" "golang.org/x/net/http2" "golang.org/x/net/http2/hpack" "golang.org/x/sync/errgroup" @@ -48,7 +48,7 @@ type MuxerConfig struct { // The minimum number of heartbeats to send before terminating the connection. MaxHeartbeats uint64 // Logger to use - Logger *log.Entry + Logger logger.Service CompressionQuality CompressionSetting // Initial size for HTTP2 flow control windows DefaultWindowSize uint32 @@ -136,10 +136,10 @@ func Handshake( handshakeSetting := http2.Setting{ID: SettingMuxerMagic, Val: MuxerMagicEdge} compressionSetting := http2.Setting{ID: SettingCompression, Val: config.CompressionQuality.toH2Setting()} if CompressionIsSupported() { - log.Debug("Compression is supported") + config.Logger.Debug("muxer: Compression is supported") m.compressionQuality = config.CompressionQuality.getPreset() } else { - log.Debug("Compression is not supported") + config.Logger.Debug("muxer: Compression is not supported") compressionSetting = http2.Setting{ID: SettingCompression, Val: 0} } @@ -176,12 +176,12 @@ func Handshake( // Sanity check to enusre idelDuration is sane if idleDuration == 0 || idleDuration < defaultTimeout { idleDuration = defaultTimeout - config.Logger.Warn("Minimum idle time has been adjusted to ", defaultTimeout) + config.Logger.Infof("muxer: Minimum idle time has been adjusted to %d", defaultTimeout) } maxRetries := config.MaxHeartbeats if maxRetries == 0 { maxRetries = defaultRetries - config.Logger.Warn("Minimum number of unacked heartbeats to send before closing the connection has been adjusted to ", maxRetries) + config.Logger.Infof("muxer: Minimum number of unacked heartbeats to send before closing the connection has been adjusted to %d", maxRetries) } compBytesBefore, compBytesAfter := NewAtomicCounter(0), NewAtomicCounter(0) diff --git a/h2mux/h2mux_test.go b/h2mux/h2mux_test.go index b7995232..5f6b8b5b 100644 --- a/h2mux/h2mux_test.go +++ b/h2mux/h2mux_test.go @@ -15,8 +15,8 @@ import ( "testing" "time" + "github.com/cloudflare/cloudflared/logger" "github.com/pkg/errors" - log "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" "golang.org/x/sync/errgroup" ) @@ -28,7 +28,7 @@ const ( func TestMain(m *testing.M) { if os.Getenv("VERBOSE") == "1" { - log.SetLevel(log.DebugLevel) + //TODO: set log level } os.Exit(m.Run()) } @@ -51,7 +51,7 @@ func NewDefaultMuxerPair(t assert.TestingT, testName string, f MuxedStreamFunc) Handler: f, IsClient: true, Name: "origin", - Logger: log.NewEntry(log.New()), + Logger: logger.NewOutputWriter(logger.NewMockWriteManager()), DefaultWindowSize: (1 << 8) - 1, MaxWindowSize: (1 << 15) - 1, StreamWriteBufferMaxLen: 1024, @@ -63,7 +63,7 @@ func NewDefaultMuxerPair(t assert.TestingT, testName string, f MuxedStreamFunc) Timeout: testHandshakeTimeout, IsClient: false, Name: "edge", - Logger: log.NewEntry(log.New()), + Logger: logger.NewOutputWriter(logger.NewMockWriteManager()), DefaultWindowSize: (1 << 8) - 1, MaxWindowSize: (1 << 15) - 1, StreamWriteBufferMaxLen: 1024, @@ -86,7 +86,7 @@ func NewCompressedMuxerPair(t assert.TestingT, testName string, quality Compress IsClient: true, Name: "origin", CompressionQuality: quality, - Logger: log.NewEntry(log.New()), + Logger: logger.NewOutputWriter(logger.NewMockWriteManager()), HeartbeatInterval: defaultTimeout, MaxHeartbeats: defaultRetries, }, @@ -96,7 +96,7 @@ func NewCompressedMuxerPair(t assert.TestingT, testName string, quality Compress IsClient: false, Name: "edge", CompressionQuality: quality, - Logger: log.NewEntry(log.New()), + Logger: logger.NewOutputWriter(logger.NewMockWriteManager()), HeartbeatInterval: defaultTimeout, MaxHeartbeats: defaultRetries, }, @@ -301,6 +301,7 @@ func TestSingleStreamLargeResponseBody(t *testing.T) { } func TestMultipleStreams(t *testing.T) { + l := logger.NewOutputWriter(logger.NewMockWriteManager()) f := MuxedStreamFunc(func(stream *MuxedStream) error { if len(stream.Headers) != 1 { t.Fatalf("expected %d headers, got %d", 1, len(stream.Headers)) @@ -308,13 +309,13 @@ func TestMultipleStreams(t *testing.T) { if stream.Headers[0].Name != "client-token" { t.Fatalf("expected header name %s, got %s", "client-token", stream.Headers[0].Name) } - log.Debugf("Got request for stream %s", stream.Headers[0].Value) + l.Debugf("Got request for stream %s", stream.Headers[0].Value) stream.WriteHeaders([]Header{ {Name: "response-token", Value: stream.Headers[0].Value}, }) - log.Debugf("Wrote headers for stream %s", stream.Headers[0].Value) + l.Debugf("Wrote headers for stream %s", stream.Headers[0].Value) stream.Write([]byte("OK")) - log.Debugf("Wrote body for stream %s", stream.Headers[0].Value) + l.Debugf("Wrote body for stream %s", stream.Headers[0].Value) return nil }) muxPair := NewDefaultMuxerPair(t, t.Name(), f) @@ -332,7 +333,7 @@ func TestMultipleStreams(t *testing.T) { []Header{{Name: "client-token", Value: tokenString}}, nil, ) - log.Debugf("Got headers for stream %d", tokenId) + l.Debugf("Got headers for stream %d", tokenId) if err != nil { errorsC <- err return @@ -370,7 +371,7 @@ func TestMultipleStreams(t *testing.T) { testFail := false for err := range errorsC { testFail = true - log.Error(err) + l.Errorf("%s", err) } if testFail { t.Fatalf("TestMultipleStreams failed") @@ -448,6 +449,8 @@ func TestMultipleStreamsFlowControl(t *testing.T) { } func TestGracefulShutdown(t *testing.T) { + l := logger.NewOutputWriter(logger.NewMockWriteManager()) + sendC := make(chan struct{}) responseBuf := bytes.Repeat([]byte("Hello world"), 65536) @@ -456,17 +459,17 @@ func TestGracefulShutdown(t *testing.T) { {Name: "response-header", Value: "responseValue"}, }) <-sendC - log.Debugf("Writing %d bytes", len(responseBuf)) + l.Debugf("Writing %d bytes", len(responseBuf)) stream.Write(responseBuf) stream.CloseWrite() - log.Debugf("Wrote %d bytes", len(responseBuf)) + l.Debugf("Wrote %d bytes", len(responseBuf)) // Reading from the stream will block until the edge closes its end of the stream. // Otherwise, we'll close the whole connection before receiving the 'stream closed' // message from the edge. // Graceful shutdown works if you omit this, it just gives spurious errors for now - // TODO ignore errors when writing 'stream closed' and we're shutting down. stream.Read([]byte{0}) - log.Debugf("Handler ends") + l.Debugf("Handler ends") return nil }) muxPair := NewDefaultMuxerPair(t, t.Name(), f) @@ -483,7 +486,7 @@ func TestGracefulShutdown(t *testing.T) { muxPair.EdgeMux.Shutdown() close(sendC) responseBody := make([]byte, len(responseBuf)) - log.Debugf("Waiting for %d bytes", len(responseBuf)) + l.Debugf("Waiting for %d bytes", len(responseBuf)) n, err := io.ReadFull(stream, responseBody) if err != nil { t.Fatalf("error from (*MuxedStream).Read with %d bytes read: %s", n, err) @@ -676,6 +679,7 @@ func AssertIfPipeReadable(t *testing.T, pipe io.ReadCloser) { } func TestMultipleStreamsWithDictionaries(t *testing.T) { + l := logger.NewOutputWriter(logger.NewMockWriteManager()) for q := CompressionNone; q <= CompressionMax; q++ { htmlBody := ` ` -func StartHelloWorldServer(logger *logrus.Logger, listener net.Listener, shutdownC <-chan struct{}) error { +func StartHelloWorldServer(logger logger.Service, listener net.Listener, shutdownC <-chan struct{}) error { logger.Infof("Starting Hello World server at %s", listener.Addr()) serverName := defaultServerName if hostname, err := os.Hostname(); err == nil { @@ -148,7 +148,7 @@ func uptimeHandler(startTime time.Time) http.HandlerFunc { } // This handler will echo message -func websocketHandler(logger *logrus.Logger, upgrader websocket.Upgrader) http.HandlerFunc { +func websocketHandler(logger logger.Service, upgrader websocket.Upgrader) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { conn, err := upgrader.Upgrade(w, r, nil) if err != nil { @@ -158,12 +158,12 @@ func websocketHandler(logger *logrus.Logger, upgrader websocket.Upgrader) http.H for { mt, message, err := conn.ReadMessage() if err != nil { - logger.WithError(err).Error("websocket read message error") + logger.Errorf("websocket read message error: %s", err) break } if err := conn.WriteMessage(mt, message); err != nil { - logger.WithError(err).Error("websocket write message error") + logger.Errorf("websocket write message error: %s", err) break } } diff --git a/log/log.go b/log/log.go deleted file mode 100644 index bc94e779..00000000 --- a/log/log.go +++ /dev/null @@ -1,85 +0,0 @@ -// this forks the logrus json formatter to rename msg -> message as that's the -// expected field. Ideally the logger should make it easier for us. -package log - -import ( - "encoding/json" - "fmt" - "runtime" - "time" - - "github.com/mattn/go-colorable" - "github.com/sirupsen/logrus" -) - -var ( - DefaultTimestampFormat = time.RFC3339Nano -) - -type JSONFormatter struct { - // TimestampFormat sets the format used for marshaling timestamps. - TimestampFormat string -} - -func CreateLogger() *logrus.Logger { - logger := logrus.New() - logger.Out = colorable.NewColorableStderr() - logger.Formatter = &logrus.TextFormatter{ForceColors: runtime.GOOS == "windows"} - return logger -} - -func (f *JSONFormatter) Format(entry *logrus.Entry) ([]byte, error) { - data := make(logrus.Fields, len(entry.Data)+3) - for k, v := range entry.Data { - switch v := v.(type) { - case error: - // Otherwise errors are ignored by `encoding/json` - // https://github.com/sirupsen/logrus/issues/137 - data[k] = v.Error() - default: - data[k] = v - } - } - prefixFieldClashes(data) - - timestampFormat := f.TimestampFormat - if timestampFormat == "" { - timestampFormat = DefaultTimestampFormat - } - - data["time"] = entry.Time.Format(timestampFormat) - data["message"] = entry.Message - data["level"] = entry.Level.String() - - serialized, err := json.Marshal(data) - if err != nil { - return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err) - } - return append(serialized, '\n'), nil -} - -// This is to not silently overwrite `time`, `msg` and `level` fields when -// dumping it. If this code wasn't there doing: -// -// logrus.WithField("level", 1).Info("hello") -// -// Would just silently drop the user provided level. Instead with this code -// it'll logged as: -// -// {"level": "info", "fields.level": 1, "msg": "hello", "time": "..."} -// -// It's not exported because it's still using Data in an opinionated way. It's to -// avoid code duplication between the two default formatters. -func prefixFieldClashes(data logrus.Fields) { - if t, ok := data["time"]; ok { - data["fields.time"] = t - } - - if m, ok := data["msg"]; ok { - data["fields.msg"] = m - } - - if l, ok := data["level"]; ok { - data["fields.level"] = l - } -} diff --git a/logger/create.go b/logger/create.go new file mode 100644 index 00000000..58f7d603 --- /dev/null +++ b/logger/create.go @@ -0,0 +1,150 @@ +package logger + +import ( + "fmt" + "os" + "strings" + "time" + + "github.com/alecthomas/units" +) + +// Option is to encaspulate actions that will be called by Parse and run later to build an Options struct +type Option func(*Options) error + +// Options is use to set logging configuration data +type Options struct { + logFileDirectory string + maxFileSize units.Base2Bytes + maxFileCount uint + terminalOutputDisabled bool + supportedFileLevels []Level + supportedTerminalLevels []Level +} + +// DisableTerminal stops terminal output for the logger +func DisableTerminal(disable bool) Option { + return func(c *Options) error { + c.terminalOutputDisabled = disable + return nil + } +} + +// File sets a custom file to log events +func File(path string, size units.Base2Bytes, count uint) Option { + return func(c *Options) error { + c.logFileDirectory = path + c.maxFileSize = size + c.maxFileCount = count + return nil + } +} + +// DefaultFile configures the log options will the defaults +func DefaultFile(directoryPath string) Option { + return func(c *Options) error { + size, err := units.ParseBase2Bytes("1MB") + if err != nil { + return err + } + + c.logFileDirectory = directoryPath + c.maxFileSize = size + c.maxFileCount = 5 + return nil + } +} + +// SupportedFileLevels sets the supported logging levels for the log file +func SupportedFileLevels(supported []Level) Option { + return func(c *Options) error { + c.supportedFileLevels = supported + return nil + } +} + +// SupportedTerminalevels sets the supported logging levels for the terminal output +func SupportedTerminalevels(supported []Level) Option { + return func(c *Options) error { + c.supportedTerminalLevels = supported + return nil + } +} + +// LogLevelString sets the supported logging levels from a command line flag +func LogLevelString(level string) Option { + return func(c *Options) error { + supported, err := ParseLevelString(level) + if err != nil { + return err + } + c.supportedFileLevels = supported + c.supportedTerminalLevels = supported + return nil + } +} + +// Parse builds the Options struct so the caller knows what actions should be run +func Parse(opts ...Option) (*Options, error) { + options := &Options{} + for _, opt := range opts { + if err := opt(options); err != nil { + return nil, err + } + } + return options, nil +} + +// New setups a new logger based on the options. +// The default behavior is to write to standard out +func New(opts ...Option) (Service, error) { + config, err := Parse(opts...) + + if err != nil { + return nil, err + } + + l := NewOutputWriter(SharedWriteManager) + if config.logFileDirectory != "" { + l.Add(NewFileRollingWriter(config.logFileDirectory, + "cloudflared", + int64(config.maxFileSize), + config.maxFileCount), + NewDefaultFormatter(time.RFC3339Nano), config.supportedFileLevels...) + } + + if !config.terminalOutputDisabled { + if len(config.supportedTerminalLevels) == 0 { + l.Add(os.Stdout, NewTerminalFormatter(""), InfoLevel) + l.Add(os.Stderr, NewTerminalFormatter(""), ErrorLevel, FatalLevel) + } else { + errLevels := []Level{} + outLevels := []Level{} + for _, level := range config.supportedTerminalLevels { + if level == ErrorLevel || level == FatalLevel { + errLevels = append(errLevels, level) + } else { + outLevels = append(outLevels, level) + } + } + l.Add(os.Stdout, NewTerminalFormatter(""), outLevels...) + l.Add(os.Stderr, NewTerminalFormatter(""), errLevels...) + } + } + return l, nil +} + +// ParseLevelString returns the expected log levels based on the cmd flag +func ParseLevelString(lvl string) ([]Level, error) { + switch strings.ToLower(lvl) { + case "fatal": + return []Level{FatalLevel}, nil + case "error": + return []Level{FatalLevel, ErrorLevel}, nil + case "info": + return []Level{FatalLevel, ErrorLevel, InfoLevel}, nil + case "debug": + return []Level{FatalLevel, ErrorLevel, InfoLevel, DebugLevel}, nil + } + return []Level{}, fmt.Errorf("not a valid log level: %q", lvl) +} diff --git a/logger/file_writer.go b/logger/file_writer.go new file mode 100644 index 00000000..123c0b9e --- /dev/null +++ b/logger/file_writer.go @@ -0,0 +1,105 @@ +package logger + +import ( + "fmt" + "os" + "path/filepath" +) + +// FileRollingWriter maintains a set of log files numbered in order +// to keep a subset of log data to ensure it doesn't grow pass defined limits +type FileRollingWriter struct { + baseFileName string + directory string + maxFileSize int64 + maxFileCount uint + fileHandle *os.File +} + +// NewFileRollingWriter creates a new rolling file writer. +// directory is the working directory for the files +// baseFileName is the log file name. This writer appends .log to the name for the file name +// maxFileSize is the size in bytes of how large each file can be. Not a hard limit, general limit based after each write +// maxFileCount is the number of rolled files to keep. +func NewFileRollingWriter(directory, baseFileName string, maxFileSize int64, maxFileCount uint) *FileRollingWriter { + return &FileRollingWriter{ + directory: directory, + baseFileName: baseFileName, + maxFileSize: maxFileSize, + maxFileCount: maxFileCount, + } +} + +// Write is an implementation of io.writer the rolls the file once it reaches its max size +// It is expected the caller to Write is doing so in a thread safe manner (as WriteManager does). +func (w *FileRollingWriter) Write(p []byte) (n int, err error) { + logFile := buildPath(w.directory, w.baseFileName) + if w.fileHandle == nil { + h, err := os.OpenFile(logFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0664) + if err != nil { + return 0, err + } + w.fileHandle = h + } + + // get size for rolling check + info, err := w.fileHandle.Stat() + if err != nil { + // failed to stat the file. Close the file handle and attempt to open a new handle on the next write + w.Close() + w.fileHandle = nil + return 0, err + } + + // write to the file + written, err := w.fileHandle.Write(p) + + // check if the file needs to be rolled + if err == nil && info.Size()+int64(written) > w.maxFileSize { + // close the file handle than do the renaming. A new one will be opened on the next write + w.Close() + w.rename(logFile, 1) + } + + return written, err +} + +// Close closes the file handle if it is open +func (w *FileRollingWriter) Close() { + if w.fileHandle != nil { + w.fileHandle.Close() + w.fileHandle = nil + } +} + +// rename is how the files are rolled. It works recursively to move the base log file to the rolled ones +// e.g. cloudflared.log -> cloudflared-1.log, +// but if cloudflared-1.log already exists, it is renamed to cloudflared-2.log, +// then the other files move in to their postion +func (w *FileRollingWriter) rename(sourcePath string, index uint) { + destinationPath := buildPath(w.directory, fmt.Sprintf("%s-%d", w.baseFileName, index)) + + // rolled to the max amount of files allowed on disk + if index >= w.maxFileCount { + os.Remove(destinationPath) + } + + // if the rolled path already exist, rename it to cloudflared-2.log, then do this one. + // recursive call since the oldest one needs to be renamed, before the newer ones can be moved + if exists(destinationPath) { + w.rename(destinationPath, index+1) + } + + os.Rename(sourcePath, destinationPath) +} + +func buildPath(directory, fileName string) string { + return filepath.Join(directory, fileName+".log") +} + +func exists(filePath string) bool { + if _, err := os.Stat(filePath); os.IsNotExist(err) { + return false + } + return true +} diff --git a/logger/file_writer_test.go b/logger/file_writer_test.go new file mode 100644 index 00000000..5ceae609 --- /dev/null +++ b/logger/file_writer_test.go @@ -0,0 +1,56 @@ +package logger + +import ( + "fmt" + "io/ioutil" + "os" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestFileWrite(t *testing.T) { + fileName := "test_file" + fileLog := fileName + ".log" + testData := []byte(string("hello Dalton, how are you doing?")) + defer func() { + os.Remove(fileLog) + }() + + w := NewFileRollingWriter("", fileName, 1000, 2) + defer w.Close() + + l, err := w.Write(testData) + + assert.NoError(t, err) + assert.Equal(t, l, len(testData), "expected write length and data length to match") + + d, err := ioutil.ReadFile(fileLog) + assert.FileExists(t, fileLog, "file doesn't exist at expected path") + assert.Equal(t, d, testData, "expected data in file to match test data") +} + +func TestRolling(t *testing.T) { + fileName := "test_file" + firstFile := fileName + ".log" + secondFile := fileName + "-1.log" + thirdFile := fileName + "-2.log" + + defer func() { + os.Remove(firstFile) + os.Remove(secondFile) + os.Remove(thirdFile) + }() + + w := NewFileRollingWriter("", fileName, 1000, 2) + defer w.Close() + + for i := 99; i >= 1; i-- { + testData := []byte(fmt.Sprintf("%d bottles of beer on the wall...", i)) + w.Write(testData) + } + assert.FileExists(t, firstFile, "first file doesn't exist as expected") + assert.FileExists(t, secondFile, "second file doesn't exist as expected") + assert.FileExists(t, thirdFile, "third file doesn't exist as expected") + assert.False(t, exists(fileName+"-3.log"), "limited to two files and there is more") +} diff --git a/logger/formatter.go b/logger/formatter.go new file mode 100644 index 00000000..bbcdbdff --- /dev/null +++ b/logger/formatter.go @@ -0,0 +1,91 @@ +package logger + +import ( + "fmt" + "time" + + "github.com/acmacalister/skittles" +) + +// Level of logging +type Level int + +const ( + // InfoLevel is for standard log messages + InfoLevel Level = iota + + // DebugLevel is for messages that are intended for purposes debugging only + DebugLevel + + // ErrorLevel is for error message to indicte something has gone wrong + ErrorLevel + + // FatalLevel is for error message that log and kill the program with an os.exit(1) + FatalLevel +) + +// Formatter is the base interface for formatting logging messages before writing them out +type Formatter interface { + Timestamp(Level, time.Time) string // format the timestamp string + Content(Level, string) string // format content string (color for terminal, etc) +} + +// DefaultFormatter writes a simple structure timestamp and the message per log line +type DefaultFormatter struct { + format string +} + +// NewDefaultFormatter creates the standard log formatter +// format is the time format to use for timestamp formatting +func NewDefaultFormatter(format string) Formatter { + return &DefaultFormatter{ + format: format, + } +} + +// Timestamp formats a log line timestamp with a brackets around them +func (f *DefaultFormatter) Timestamp(l Level, d time.Time) string { + if f.format == "" { + return "" + } + return fmt.Sprintf("[%s]: ", d.Format(f.format)) +} + +// Content just writes the log line straight to the sources +func (f *DefaultFormatter) Content(l Level, c string) string { + return c +} + +// TerminalFormatter is setup for colored output +type TerminalFormatter struct { + format string +} + +// NewTerminalFormatter creates a Terminal formatter for colored output +// format is the time format to use for timestamp formatting +func NewTerminalFormatter(format string) Formatter { + return &TerminalFormatter{ + format: format, + } +} + +// Timestamp returns the log level with a matching color to the log type +func (f *TerminalFormatter) Timestamp(l Level, d time.Time) string { + t := "" + switch l { + case InfoLevel: + t = skittles.Cyan("[INFO] ") + case ErrorLevel: + t = skittles.Red("[ERROR] ") + case DebugLevel: + t = skittles.Yellow("[DEBUG] ") + case FatalLevel: + t = skittles.Red("[FATAL] ") + } + return t +} + +// Content just writes the log line straight to the sources +func (f *TerminalFormatter) Content(l Level, c string) string { + return c +} diff --git a/logger/manager.go b/logger/manager.go new file mode 100644 index 00000000..f044c181 --- /dev/null +++ b/logger/manager.go @@ -0,0 +1,59 @@ +package logger + +import "sync" + +// SharedWriteManager is a package level variable to allows multiple loggers to use the same write manager. +// This is useful when multiple loggers will write to the same file to ensure they don't clobber each other. +var SharedWriteManager = NewWriteManager() + +type writeData struct { + writeFunc func([]byte) + data []byte +} + +// WriteManager is a logging service that handles managing multiple writing streams +type WriteManager struct { + shutdown chan struct{} + writeChan chan writeData + writers map[string]Service + wg sync.WaitGroup +} + +// NewWriteManager creates a write manager that implements OutputManager +func NewWriteManager() OutputManager { + m := &WriteManager{ + shutdown: make(chan struct{}), + writeChan: make(chan writeData, 1000), + } + + go m.run() + return m +} + +// Append adds a message to the writer runloop +func (m *WriteManager) Append(data []byte, callback func([]byte)) { + m.wg.Add(1) + m.writeChan <- writeData{data: data, writeFunc: callback} +} + +// Shutdown stops the sync manager service +func (m *WriteManager) Shutdown() { + m.wg.Wait() + close(m.shutdown) + close(m.writeChan) +} + +// run is the main runloop that schedules log messages +func (m *WriteManager) run() { + for { + select { + case event, ok := <-m.writeChan: + if ok { + event.writeFunc(event.data) + m.wg.Done() + } + case <-m.shutdown: + return + } + } +} diff --git a/logger/manager_test.go b/logger/manager_test.go new file mode 100644 index 00000000..5cf9e7ba --- /dev/null +++ b/logger/manager_test.go @@ -0,0 +1,18 @@ +package logger + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestWriteManger(t *testing.T) { + testData := []byte(string("hello Austin, how are you doing?")) + waitChan := make(chan []byte) + m := NewWriteManager() + m.Append(testData, func(b []byte) { + waitChan <- b + }) + resp := <-waitChan + assert.Equal(t, testData, resp) +} diff --git a/logger/mock_manager.go b/logger/mock_manager.go new file mode 100644 index 00000000..ea585577 --- /dev/null +++ b/logger/mock_manager.go @@ -0,0 +1,18 @@ +package logger + +// MockWriteManager does nothing and is provided for testing purposes +type MockWriteManager struct { +} + +// NewMockWriteManager creates an OutputManager that does nothing for testing purposes +func NewMockWriteManager() OutputManager { + return &MockWriteManager{} +} + +// Append is a mock stub +func (m *MockWriteManager) Append(data []byte, callback func([]byte)) { +} + +// Shutdown is a mock stub +func (m *MockWriteManager) Shutdown() { +} diff --git a/logger/output.go b/logger/output.go new file mode 100644 index 00000000..cdc7d195 --- /dev/null +++ b/logger/output.go @@ -0,0 +1,132 @@ +package logger + +import ( + "fmt" + "io" + "os" + "time" +) + +// provided for testing +var osExit = os.Exit + +// OutputManager is used to sync data of Output +type OutputManager interface { + Append([]byte, func([]byte)) + Shutdown() +} + +// Service is the logging service that is either a group or single log writer +type Service interface { + Error(message string) + Info(message string) + Debug(message string) + Fatal(message string) + + Errorf(format string, args ...interface{}) + Infof(format string, args ...interface{}) + Debugf(format string, args ...interface{}) + Fatalf(format string, args ...interface{}) +} + +type sourceGroup struct { + writer io.Writer + formatter Formatter + levelsSupported []Level +} + +// OutputWriter is the standard logging implementation +type OutputWriter struct { + groups []sourceGroup + syncWriter OutputManager +} + +// NewOutputWriter create a new logger +func NewOutputWriter(syncWriter OutputManager) *OutputWriter { + return &OutputWriter{ + syncWriter: syncWriter, + groups: make([]sourceGroup, 0), + } +} + +// Add a writer and formatter to output to +func (s *OutputWriter) Add(writer io.Writer, formatter Formatter, levels ...Level) { + s.groups = append(s.groups, sourceGroup{writer: writer, formatter: formatter, levelsSupported: levels}) +} + +// Error writes an error to the logging sources +func (s *OutputWriter) Error(message string) { + s.output(ErrorLevel, message) +} + +// Info writes an info string to the logging sources +func (s *OutputWriter) Info(message string) { + s.output(InfoLevel, message) +} + +// Debug writes a debug string to the logging sources +func (s *OutputWriter) Debug(message string) { + s.output(DebugLevel, message) +} + +// Fatal writes a error string to the logging sources and runs does an os.exit() +func (s *OutputWriter) Fatal(message string) { + s.output(FatalLevel, message) + s.syncWriter.Shutdown() // waits for the pending logging to finish + osExit(1) +} + +// Errorf writes a formatted error to the logging sources +func (s *OutputWriter) Errorf(format string, args ...interface{}) { + s.output(ErrorLevel, fmt.Sprintf(format, args...)) +} + +// Infof writes a formatted info statement to the logging sources +func (s *OutputWriter) Infof(format string, args ...interface{}) { + s.output(InfoLevel, fmt.Sprintf(format, args...)) +} + +// Debugf writes a formatted debug statement to the logging sources +func (s *OutputWriter) Debugf(format string, args ...interface{}) { + s.output(DebugLevel, fmt.Sprintf(format, args...)) +} + +// Fatalf writes a writes a formatted error statement and runs does an os.exit() +func (s *OutputWriter) Fatalf(format string, args ...interface{}) { + s.output(FatalLevel, fmt.Sprintf(format, args...)) + s.syncWriter.Shutdown() // waits for the pending logging to finish + osExit(1) +} + +// output does the actual write to the sync manager +func (s *OutputWriter) output(l Level, content string) { + for _, group := range s.groups { + if isSupported(group, l) { + logLine := fmt.Sprintf("%s%s\n", group.formatter.Timestamp(l, time.Now()), + group.formatter.Content(l, content)) + s.append(group, []byte(logLine)) + } + } +} + +func (s *OutputWriter) append(group sourceGroup, logLine []byte) { + s.syncWriter.Append(logLine, func(b []byte) { + group.writer.Write(b) + }) +} + +// isSupported checks if the log level is supported +func isSupported(group sourceGroup, l Level) bool { + for _, level := range group.levelsSupported { + if l == level { + return true + } + } + return false +} + +// Write implements io.Writer to support SetOutput of the log package +func (s *OutputWriter) Write(p []byte) (n int, err error) { + s.Info(string(p)) + return len(p), nil +} diff --git a/logger/output_test.go b/logger/output_test.go new file mode 100644 index 00000000..02b68dcb --- /dev/null +++ b/logger/output_test.go @@ -0,0 +1,104 @@ +package logger + +import ( + "bufio" + "bytes" + "fmt" + "strings" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestLogLevel(t *testing.T) { + timeFormat := "2006-01-02" + f := NewDefaultFormatter(timeFormat) + m := NewWriteManager() + + var testBuffer bytes.Buffer + logger := NewOutputWriter(m) + logger.Add(&testBuffer, f, InfoLevel, DebugLevel) + + testTime := f.Timestamp(InfoLevel, time.Now()) + + testInfo := "hello Dalton, how are you doing?" + logger.Info(testInfo) + + tesErr := "hello Austin, how did it break today?" + logger.Error(tesErr) + + testDebug := "hello Bill, who are you?" + logger.Debug(testDebug) + + m.Shutdown() + + lines := strings.Split(testBuffer.String(), "\n") + assert.Len(t, lines, 3, "only expected two strings in the buffer") + + infoLine := lines[0] + debugLine := lines[1] + + compareInfo := fmt.Sprintf("%s%s", testTime, testInfo) + assert.Equal(t, compareInfo, infoLine, "expect the strings to match") + + compareDebug := fmt.Sprintf("%s%s", testTime, testDebug) + assert.Equal(t, compareDebug, debugLine, "expect the strings to match") +} + +func TestOutputWrite(t *testing.T) { + timeFormat := "2006-01-02" + f := NewDefaultFormatter(timeFormat) + m := NewWriteManager() + + var testBuffer bytes.Buffer + logger := NewOutputWriter(m) + logger.Add(&testBuffer, f, InfoLevel) + + testData := "hello Bob Bork, how are you doing?" + logger.Info(testData) + testTime := f.Timestamp(InfoLevel, time.Now()) + + m.Shutdown() + + scanner := bufio.NewScanner(&testBuffer) + scanner.Scan() + line := scanner.Text() + assert.NoError(t, scanner.Err()) + + compareLine := fmt.Sprintf("%s%s", testTime, testData) + assert.Equal(t, compareLine, line, "expect the strings to match") +} + +func TestFatalWrite(t *testing.T) { + timeFormat := "2006-01-02" + f := NewDefaultFormatter(timeFormat) + m := NewWriteManager() + + var testBuffer bytes.Buffer + logger := NewOutputWriter(m) + logger.Add(&testBuffer, f, FatalLevel) + + oldOsExit := osExit + defer func() { osExit = oldOsExit }() + + var got int + myExit := func(code int) { + got = code + } + + osExit = myExit + + testData := "so long y'all" + logger.Fatal(testData) + testTime := f.Timestamp(FatalLevel, time.Now()) + + scanner := bufio.NewScanner(&testBuffer) + scanner.Scan() + line := scanner.Text() + assert.NoError(t, scanner.Err()) + + compareLine := fmt.Sprintf("%s%s", testTime, testData) + assert.Equal(t, compareLine, line, "expect the strings to match") + assert.Equal(t, got, 1, "exit code should be one for a fatal log") +} diff --git a/metrics/metrics.go b/metrics/metrics.go index 89510d07..c28d6013 100644 --- a/metrics/metrics.go +++ b/metrics/metrics.go @@ -12,9 +12,9 @@ import ( "golang.org/x/net/trace" + "github.com/cloudflare/cloudflared/logger" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" - "github.com/sirupsen/logrus" ) const ( @@ -22,7 +22,7 @@ const ( startupTime = time.Millisecond * 500 ) -func ServeMetrics(l net.Listener, shutdownC <-chan struct{}, logger *logrus.Logger) (err error) { +func ServeMetrics(l net.Listener, shutdownC <-chan struct{}, logger logger.Service) (err error) { var wg sync.WaitGroup // Metrics port is privileged, so no need for further access control trace.AuthRequest = func(*http.Request) (bool, bool) { return true, true } @@ -43,7 +43,7 @@ func ServeMetrics(l net.Listener, shutdownC <-chan struct{}, logger *logrus.Logg defer wg.Done() err = server.Serve(l) }() - logger.WithField("addr", fmt.Sprintf("%v/metrics", l.Addr())).Info("Starting metrics server") + logger.Infof("Starting metrics server on %s", fmt.Sprintf("%v/metrics", l.Addr())) // server.Serve will hang if server.Shutdown is called before the server is // fully started up. So add artificial delay. time.Sleep(startupTime) @@ -58,7 +58,7 @@ func ServeMetrics(l net.Listener, shutdownC <-chan struct{}, logger *logrus.Logg logger.Info("Metrics server stopped") return nil } - logger.WithError(err).Error("Metrics server quit with error") + logger.Errorf("Metrics server quit with error: %s", err) return err } diff --git a/origin/supervisor.go b/origin/supervisor.go index cd21b57f..b3c680f9 100644 --- a/origin/supervisor.go +++ b/origin/supervisor.go @@ -9,12 +9,12 @@ import ( "time" "github.com/google/uuid" - "github.com/sirupsen/logrus" "github.com/cloudflare/cloudflared/buffer" "github.com/cloudflare/cloudflared/connection" "github.com/cloudflare/cloudflared/edgediscovery" "github.com/cloudflare/cloudflared/h2mux" + "github.com/cloudflare/cloudflared/logger" "github.com/cloudflare/cloudflared/signal" tunnelpogs "github.com/cloudflare/cloudflared/tunnelrpc/pogs" ) @@ -56,7 +56,7 @@ type Supervisor struct { nextConnectedIndex int nextConnectedSignal chan struct{} - logger *logrus.Entry + logger logger.Service jwtLock sync.RWMutex jwt []byte @@ -99,7 +99,7 @@ func NewSupervisor(config *TunnelConfig, u uuid.UUID) (*Supervisor, error) { edgeIPs: edgeIPs, tunnelErrors: make(chan tunnelError), tunnelsConnecting: map[int]chan struct{}{}, - logger: config.Logger.WithField("subsystem", "supervisor"), + logger: config.Logger, connDigest: make(map[uint8][]byte), bufferPool: buffer.NewPool(512 * 1024), }, nil @@ -123,7 +123,7 @@ func (s *Supervisor) Run(ctx context.Context, connectedSignal *signal.Signal, re if timer, err := s.refreshAuth(ctx, refreshAuthBackoff, s.authenticate); err == nil { refreshAuthBackoffTimer = timer } else { - logger.WithError(err).Errorf("initial refreshAuth failed, retrying in %v", refreshAuthRetryDuration) + logger.Errorf("supervisor: initial refreshAuth failed, retrying in %v: %s", refreshAuthRetryDuration, err) refreshAuthBackoffTimer = time.After(refreshAuthRetryDuration) } } @@ -142,7 +142,7 @@ func (s *Supervisor) Run(ctx context.Context, connectedSignal *signal.Signal, re case tunnelError := <-s.tunnelErrors: tunnelsActive-- if tunnelError.err != nil { - logger.WithError(tunnelError.err).Warn("Tunnel disconnected due to error") + logger.Infof("supervisor: Tunnel disconnected due to error: %s", tunnelError.err) tunnelsWaiting = append(tunnelsWaiting, tunnelError.index) s.waitForNextTunnel(tunnelError.index) @@ -165,7 +165,7 @@ func (s *Supervisor) Run(ctx context.Context, connectedSignal *signal.Signal, re case <-refreshAuthBackoffTimer: newTimer, err := s.refreshAuth(ctx, refreshAuthBackoff, s.authenticate) if err != nil { - logger.WithError(err).Error("Authentication failed") + logger.Errorf("supervisor: Authentication failed: %s", err) // Permanent failure. Leave the `select` without setting the // channel to be non-null, so we'll never hit this case of the `select` again. continue @@ -182,9 +182,9 @@ func (s *Supervisor) Run(ctx context.Context, connectedSignal *signal.Signal, re s.lastResolve = time.Now() s.resolverC = nil if result.err == nil { - logger.Debug("Service discovery refresh complete") + logger.Debug("supervisor: Service discovery refresh complete") } else { - logger.WithError(result.err).Error("Service discovery error") + logger.Errorf("supervisor: Service discovery error: %s", result.err) } } } @@ -197,7 +197,7 @@ func (s *Supervisor) initialize(ctx context.Context, connectedSignal *signal.Sig s.lastResolve = time.Now() availableAddrs := int(s.edgeIPs.AvailableAddrs()) if s.config.HAConnections > availableAddrs { - logger.Warnf("You requested %d HA connections but I can give you at most %d.", s.config.HAConnections, availableAddrs) + logger.Infof("You requested %d HA connections but I can give you at most %d.", s.config.HAConnections, availableAddrs) s.config.HAConnections = availableAddrs } @@ -355,12 +355,12 @@ func (s *Supervisor) refreshAuth( backoff *BackoffHandler, authenticate func(ctx context.Context, numPreviousAttempts int) (tunnelpogs.AuthOutcome, error), ) (retryTimer <-chan time.Time, err error) { - logger := s.config.Logger.WithField("subsystem", subsystemRefreshAuth) + logger := s.config.Logger authOutcome, err := authenticate(ctx, backoff.Retries()) if err != nil { s.config.Metrics.authFail.WithLabelValues(err.Error()).Inc() if duration, ok := backoff.GetBackoffDuration(ctx); ok { - logger.WithError(err).Warnf("Retrying in %v", duration) + logger.Debugf("refresh_auth: Retrying in %v: %s", duration, err) return backoff.BackoffTimer(), nil } return nil, err @@ -376,13 +376,13 @@ func (s *Supervisor) refreshAuth( case tunnelpogs.AuthUnknown: duration := outcome.RefreshAfter() s.config.Metrics.authFail.WithLabelValues(outcome.Error()).Inc() - logger.WithError(outcome).Warnf("Retrying in %v", duration) + logger.Debugf("refresh_auth: Retrying in %v: %s", duration, outcome) return timeAfter(duration), nil case tunnelpogs.AuthFail: s.config.Metrics.authFail.WithLabelValues(outcome.Error()).Inc() return nil, outcome default: - err := fmt.Errorf("Unexpected outcome type %T", authOutcome) + err := fmt.Errorf("refresh_auth: Unexpected outcome type %T", authOutcome) s.config.Metrics.authFail.WithLabelValues(err.Error()).Inc() return nil, err } @@ -405,7 +405,6 @@ func (s *Supervisor) authenticate(ctx context.Context, numPreviousAttempts int) return nil // noop }) muxerConfig := s.config.muxerConfig(handler) - muxerConfig.Logger = muxerConfig.Logger.WithField("subsystem", subsystemRefreshAuth) muxer, err := h2mux.Handshake(edgeConn, edgeConn, muxerConfig, s.config.Metrics.activeStreams) if err != nil { return nil, err @@ -417,7 +416,7 @@ func (s *Supervisor) authenticate(ctx context.Context, numPreviousAttempts int) <-muxer.Shutdown() }() - tunnelServer, err := connection.NewRPCClient(ctx, muxer, s.logger.WithField("subsystem", subsystemRefreshAuth), openStreamTimeout) + tunnelServer, err := connection.NewRPCClient(ctx, muxer, s.logger, openStreamTimeout) if err != nil { return nil, err } diff --git a/origin/supervisor_test.go b/origin/supervisor_test.go index 5f031221..21eeec60 100644 --- a/origin/supervisor_test.go +++ b/origin/supervisor_test.go @@ -9,13 +9,13 @@ import ( "github.com/google/uuid" "github.com/prometheus/client_golang/prometheus" - "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" + "github.com/cloudflare/cloudflared/logger" tunnelpogs "github.com/cloudflare/cloudflared/tunnelrpc/pogs" ) -func testConfig(logger *logrus.Logger) *TunnelConfig { +func testConfig(logger logger.Service) *TunnelConfig { metrics := TunnelMetrics{} metrics.authSuccess = prometheus.NewCounter( @@ -40,8 +40,7 @@ func testConfig(logger *logrus.Logger) *TunnelConfig { } func TestRefreshAuthBackoff(t *testing.T) { - logger := logrus.New() - logger.Level = logrus.ErrorLevel + logger := logger.NewOutputWriter(logger.NewMockWriteManager()) var wait time.Duration timeAfter = func(d time.Duration) <-chan time.Time { @@ -85,8 +84,7 @@ func TestRefreshAuthBackoff(t *testing.T) { } func TestRefreshAuthSuccess(t *testing.T) { - logger := logrus.New() - logger.Level = logrus.ErrorLevel + logger := logger.NewOutputWriter(logger.NewMockWriteManager()) var wait time.Duration timeAfter = func(d time.Duration) <-chan time.Time { @@ -114,8 +112,7 @@ func TestRefreshAuthSuccess(t *testing.T) { } func TestRefreshAuthUnknown(t *testing.T) { - logger := logrus.New() - logger.Level = logrus.ErrorLevel + logger := logger.NewOutputWriter(logger.NewMockWriteManager()) var wait time.Duration timeAfter = func(d time.Duration) <-chan time.Time { @@ -143,8 +140,7 @@ func TestRefreshAuthUnknown(t *testing.T) { } func TestRefreshAuthFail(t *testing.T) { - logger := logrus.New() - logger.Level = logrus.ErrorLevel + logger := logger.NewOutputWriter(logger.NewMockWriteManager()) s, err := NewSupervisor(testConfig(logger), uuid.New()) if !assert.NoError(t, err) { diff --git a/origin/tunnel.go b/origin/tunnel.go index 42db204b..a68dedc0 100644 --- a/origin/tunnel.go +++ b/origin/tunnel.go @@ -17,13 +17,13 @@ import ( "github.com/google/uuid" "github.com/pkg/errors" "github.com/prometheus/client_golang/prometheus" - log "github.com/sirupsen/logrus" "golang.org/x/sync/errgroup" "github.com/cloudflare/cloudflared/buffer" "github.com/cloudflare/cloudflared/cmd/cloudflared/buildinfo" "github.com/cloudflare/cloudflared/connection" "github.com/cloudflare/cloudflared/h2mux" + "github.com/cloudflare/cloudflared/logger" "github.com/cloudflare/cloudflared/signal" "github.com/cloudflare/cloudflared/streamhandler" "github.com/cloudflare/cloudflared/tunnelrpc" @@ -68,7 +68,8 @@ type TunnelConfig struct { IsAutoupdated bool IsFreeTunnel bool LBPool string - Logger *log.Logger + Logger logger.Service + TransportLogger logger.Service MaxHeartbeats uint64 Metrics *TunnelMetrics MetricsUpdateFreq time.Duration @@ -79,7 +80,6 @@ type TunnelConfig struct { RunFromTerminal bool Tags []tunnelpogs.Tag TlsConfig *tls.Config - TransportLogger *log.Logger UseDeclarativeTunnel bool WSGI bool // OriginUrl may not be used if a user specifies a unix socket. @@ -144,7 +144,7 @@ func (c *TunnelConfig) muxerConfig(handler h2mux.MuxedStreamHandler) h2mux.Muxer IsClient: true, HeartbeatInterval: c.HeartbeatInterval, MaxHeartbeats: c.MaxHeartbeats, - Logger: c.TransportLogger.WithFields(log.Fields{}), + Logger: c.TransportLogger, CompressionQuality: h2mux.CompressionSetting(c.CompressionQuality), } } @@ -197,7 +197,6 @@ func ServeTunnelLoop(ctx context.Context, bufferPool *buffer.Pool, reconnectCh chan ReconnectSignal, ) error { - connectionLogger := config.Logger.WithField("connectionID", connectionID) config.Metrics.incrementHaConnections() defer config.Metrics.decrementHaConnections() backoff := BackoffHandler{MaxRetries: config.Retries} @@ -214,7 +213,7 @@ func ServeTunnelLoop(ctx context.Context, ctx, credentialManager, config, - connectionLogger, + config.Logger, addr, connectionID, connectedFuse, &backoff, @@ -224,7 +223,7 @@ func ServeTunnelLoop(ctx context.Context, ) if recoverable { if duration, ok := backoff.GetBackoffDuration(ctx); ok { - connectionLogger.Infof("Retrying in %s seconds", duration) + config.Logger.Infof("Retrying in %s seconds: connectionID: %d", duration, connectionID) backoff.Backoff(ctx) continue } @@ -237,7 +236,7 @@ func ServeTunnel( ctx context.Context, credentialManager ReconnectTunnelCredentialManager, config *TunnelConfig, - logger *log.Entry, + logger logger.Service, addr *net.TCPAddr, connectionID uint8, connectedFuse *h2mux.BooleanFuse, @@ -267,14 +266,13 @@ func ServeTunnel( // Returns error from parsing the origin URL or handshake errors handler, originLocalIP, err := NewTunnelHandler(ctx, config, addr, connectionID, bufferPool) if err != nil { - errLog := logger.WithError(err) switch err.(type) { case connection.DialError: - errLog.Error("Unable to dial edge") + logger.Errorf("Unable to dial edge: %s connectionID: %d", err, connectionID) case h2mux.MuxerHandshakeError: - errLog.Error("Handshake failed with edge server") + logger.Errorf("Handshake failed with edge server: %s connectionID: %d", err, connectionID) default: - errLog.Error("Tunnel creation failure") + logger.Errorf("Tunnel creation failure: %s connectionID: %d", err, connectionID) return err, false } return err, true @@ -307,10 +305,10 @@ func ServeTunnel( } // log errors and proceed to RegisterTunnel if tokenErr != nil { - logger.WithError(tokenErr).Error("Couldn't get reconnect token") + logger.Errorf("Couldn't get reconnect token: %s", tokenErr) } if eventDigestErr != nil { - logger.WithError(eventDigestErr).Error("Couldn't get event digest") + logger.Errorf("Couldn't get event digest: %s", eventDigestErr) } } return RegisterTunnel(serveCtx, credentialManager, handler.muxer, config, logger, connectionID, originLocalIP, u) @@ -365,7 +363,7 @@ func ServeTunnel( logger.Info("Already connected to this server, selecting a different one") return err, true case serverRegisterTunnelError: - logger.WithError(castedErr.cause).Error("Register tunnel error from server side") + logger.Errorf("Register tunnel error from server side: %s", castedErr.cause) // Don't send registration error return from server to Sentry. They are // logged on server side if incidents := config.IncidentLookup.ActiveIncidents(); len(incidents) > 0 { @@ -373,17 +371,17 @@ func ServeTunnel( } return castedErr.cause, !castedErr.permanent case clientRegisterTunnelError: - logger.WithError(castedErr.cause).Error("Register tunnel error on client side") + logger.Errorf("Register tunnel error on client side: %s", castedErr.cause) return err, true case muxerShutdownError: logger.Info("Muxer shutdown") return err, true case *ReconnectSignal: - logger.Warnf("Restarting due to reconnect signal in %d seconds", castedErr.Delay) + logger.Infof("Restarting due to reconnect signal in %d seconds", castedErr.Delay) castedErr.DelayBeforeReconnect() return err, true default: - logger.WithError(err).Error("Serve tunnel error") + logger.Errorf("Serve tunnel error: %s", err) return err, true } } @@ -395,13 +393,13 @@ func RegisterTunnel( credentialManager ReconnectTunnelCredentialManager, muxer *h2mux.Muxer, config *TunnelConfig, - logger *log.Entry, + logger logger.Service, connectionID uint8, originLocalIP string, uuid uuid.UUID, ) error { config.TransportLogger.Debug("initiating RPC stream to register") - tunnelServer, err := connection.NewRPCClient(ctx, muxer, config.TransportLogger.WithField("subsystem", "rpc-register"), openStreamTimeout) + tunnelServer, err := connection.NewRPCClient(ctx, muxer, config.TransportLogger, openStreamTimeout) if err != nil { // RPC stream open error return newClientRegisterTunnelError(err, config.Metrics.rpcFail, register) @@ -432,14 +430,14 @@ func ReconnectTunnel( eventDigest, connDigest []byte, muxer *h2mux.Muxer, config *TunnelConfig, - logger *log.Entry, + logger logger.Service, connectionID uint8, originLocalIP string, uuid uuid.UUID, credentialManager ReconnectTunnelCredentialManager, ) error { config.TransportLogger.Debug("initiating RPC stream to reconnect") - tunnelServer, err := connection.NewRPCClient(ctx, muxer, config.TransportLogger.WithField("subsystem", "rpc-reconnect"), openStreamTimeout) + tunnelServer, err := connection.NewRPCClient(ctx, muxer, config.TransportLogger, openStreamTimeout) if err != nil { // RPC stream open error return newClientRegisterTunnelError(err, config.Metrics.rpcFail, reconnect) @@ -467,7 +465,7 @@ func ReconnectTunnel( func processRegistrationSuccess( config *TunnelConfig, - logger *log.Entry, + logger logger.Service, connectionID uint8, registration *tunnelpogs.TunnelRegistration, name registerRPCName, @@ -486,10 +484,10 @@ func processRegistrationSuccess( if isTrialTunnel := config.Hostname == ""; isTrialTunnel { if url, err := url.Parse(registration.Url); err == nil { for _, line := range asciiBox(trialZoneMsg(url.String()), 2) { - logger.Infoln(line) + logger.Info(line) } } else { - logger.Errorln("Failed to connect tunnel, please try again.") + logger.Error("Failed to connect tunnel, please try again.") return fmt.Errorf("empty URL in response from Cloudflare edge") } } @@ -514,10 +512,10 @@ func processRegisterTunnelError(err tunnelpogs.TunnelRegistrationError, metrics } } -func UnregisterTunnel(muxer *h2mux.Muxer, gracePeriod time.Duration, logger *log.Logger) error { +func UnregisterTunnel(muxer *h2mux.Muxer, gracePeriod time.Duration, logger logger.Service) error { logger.Debug("initiating RPC stream to unregister") ctx := context.Background() - tunnelServer, err := connection.NewRPCClient(ctx, muxer, logger.WithField("subsystem", "rpc-unregister"), openStreamTimeout) + tunnelServer, err := connection.NewRPCClient(ctx, muxer, logger, openStreamTimeout) if err != nil { // RPC stream open error return err @@ -532,16 +530,16 @@ func LogServerInfo( promise tunnelrpc.ServerInfo_Promise, connectionID uint8, metrics *TunnelMetrics, - logger *log.Entry, + logger logger.Service, ) { serverInfoMessage, err := promise.Struct() if err != nil { - logger.WithError(err).Warn("Failed to retrieve server information") + logger.Errorf("Failed to retrieve server information: %s", err) return } serverInfo, err := tunnelpogs.UnmarshalServerInfo(serverInfoMessage) if err != nil { - logger.WithError(err).Warn("Failed to retrieve server information") + logger.Errorf("Failed to retrieve server information: %s", err) return } logger.Infof("Connected to %s", serverInfo.LocationName) @@ -558,7 +556,7 @@ type TunnelHandler struct { metrics *TunnelMetrics // connectionID is only used by metrics, and prometheus requires labels to be string connectionID string - logger *log.Logger + logger logger.Service noChunkedEncoding bool bufferPool *buffer.Pool @@ -736,7 +734,7 @@ func (h *TunnelHandler) isEventStream(response *http.Response) bool { } func (h *TunnelHandler) writeErrorResponse(stream *h2mux.MuxedStream, err error) { - h.logger.WithError(err).Error("HTTP request error") + h.logger.Errorf("HTTP request error: %s", err) stream.WriteHeaders([]h2mux.Header{ {Name: ":status", Value: "502"}, h2mux.CreateResponseMetaHeader(h2mux.ResponseMetaHeaderField, h2mux.ResponseSourceCloudflared), @@ -746,41 +744,39 @@ func (h *TunnelHandler) writeErrorResponse(stream *h2mux.MuxedStream, err error) } func (h *TunnelHandler) logRequest(req *http.Request, cfRay string, lbProbe bool) { - logger := log.NewEntry(h.logger) + logger := h.logger if cfRay != "" { - logger = logger.WithField("CF-RAY", cfRay) - logger.Debugf("%s %s %s", req.Method, req.URL, req.Proto) + logger.Debugf("CF-RAY: %s %s %s %s", cfRay, req.Method, req.URL, req.Proto) } else if lbProbe { - logger.Debugf("Load Balancer health check %s %s %s", req.Method, req.URL, req.Proto) + logger.Debugf("CF-RAY: %s Load Balancer health check %s %s %s", cfRay, req.Method, req.URL, req.Proto) } else { - logger.Warnf("All requests should have a CF-RAY header. Please open a support ticket with Cloudflare. %s %s %s ", req.Method, req.URL, req.Proto) + logger.Infof("CF-RAY: %s All requests should have a CF-RAY header. Please open a support ticket with Cloudflare. %s %s %s ", cfRay, req.Method, req.URL, req.Proto) } - logger.Debugf("Request Headers %+v", req.Header) + logger.Debugf("CF-RAY: %s Request Headers %+v", cfRay, req.Header) if contentLen := req.ContentLength; contentLen == -1 { - logger.Debugf("Request Content length unknown") + logger.Debugf("CF-RAY: %s Request Content length unknown", cfRay) } else { - logger.Debugf("Request content length %d", contentLen) + logger.Debugf("CF-RAY: %s Request content length %d", cfRay, contentLen) } } func (h *TunnelHandler) logResponseOk(r *http.Response, cfRay string, lbProbe bool) { h.metrics.incrementResponses(h.connectionID, "200") - logger := log.NewEntry(h.logger) + logger := h.logger if cfRay != "" { - logger = logger.WithField("CF-RAY", cfRay) - logger.Debugf("%s", r.Status) + logger.Debugf("CF-RAY: %s %s", cfRay, r.Status) } else if lbProbe { logger.Debugf("Response to Load Balancer health check %s", r.Status) } else { logger.Infof("%s", r.Status) } - logger.Debugf("Response Headers %+v", r.Header) + logger.Debugf("CF-RAY: %s Response Headers %+v", cfRay, r.Header) if contentLen := r.ContentLength; contentLen == -1 { - logger.Debugf("Response content length unknown") + logger.Debugf("CF-RAY: %s Response content length unknown", cfRay) } else { - logger.Debugf("Response content length %d", contentLen) + logger.Debugf("CF-RAY: %s Response content length %d", cfRay, contentLen) } } diff --git a/originservice/originservice.go b/originservice/originservice.go index 6f363c30..97fa69ef 100644 --- a/originservice/originservice.go +++ b/originservice/originservice.go @@ -15,7 +15,7 @@ import ( "github.com/cloudflare/cloudflared/buffer" "github.com/cloudflare/cloudflared/h2mux" "github.com/cloudflare/cloudflared/hello" - "github.com/cloudflare/cloudflared/log" + "github.com/cloudflare/cloudflared/logger" "github.com/cloudflare/cloudflared/websocket" "github.com/pkg/errors" ) @@ -101,14 +101,14 @@ type WebsocketService struct { shutdownC chan struct{} } -func NewWebSocketService(tlsConfig *tls.Config, url *url.URL) (OriginService, error) { +func NewWebSocketService(tlsConfig *tls.Config, url *url.URL, logger logger.Service) (OriginService, error) { listener, err := net.Listen("tcp", "127.0.0.1:") if err != nil { return nil, errors.Wrap(err, "cannot start Websocket Proxy Server") } shutdownC := make(chan struct{}) go func() { - websocket.StartProxyServer(log.CreateLogger(), listener, url.String(), shutdownC, websocket.DefaultStreamHandler) + websocket.StartProxyServer(logger, listener, url.String(), shutdownC, websocket.DefaultStreamHandler) }() return &WebsocketService{ tlsConfig: tlsConfig, @@ -157,14 +157,14 @@ type HelloWorldService struct { bufferPool *buffer.Pool } -func NewHelloWorldService(transport http.RoundTripper) (OriginService, error) { +func NewHelloWorldService(transport http.RoundTripper, logger logger.Service) (OriginService, error) { listener, err := hello.CreateTLSListener("127.0.0.1:") if err != nil { return nil, errors.Wrap(err, "cannot start Hello World Server") } shutdownC := make(chan struct{}) go func() { - hello.StartHelloWorldServer(log.CreateLogger(), listener, shutdownC) + hello.StartHelloWorldServer(logger, listener, shutdownC) }() return &HelloWorldService{ client: transport, diff --git a/sshlog/empty_manager.go b/sshlog/empty_manager.go index bd9f3137..e95825e6 100644 --- a/sshlog/empty_manager.go +++ b/sshlog/empty_manager.go @@ -3,7 +3,7 @@ package sshlog import ( "io" - "github.com/sirupsen/logrus" + "github.com/cloudflare/cloudflared/logger" ) //empty manager implements the Manager but does nothing (for testing and to disable logging unless the logs are set) @@ -18,11 +18,11 @@ func NewEmptyManager() Manager { return &emptyManager{} } -func (m *emptyManager) NewLogger(name string, logger *logrus.Logger) (io.WriteCloser, error) { +func (m *emptyManager) NewLogger(name string, logger logger.Service) (io.WriteCloser, error) { return &emptyWriteCloser{}, nil } -func (m *emptyManager) NewSessionLogger(name string, logger *logrus.Logger) (io.WriteCloser, error) { +func (m *emptyManager) NewSessionLogger(name string, logger logger.Service) (io.WriteCloser, error) { return &emptyWriteCloser{}, nil } diff --git a/sshlog/logger.go b/sshlog/logger.go index 96742c22..b62f87b3 100644 --- a/sshlog/logger.go +++ b/sshlog/logger.go @@ -9,7 +9,7 @@ import ( "sync" "time" - "github.com/sirupsen/logrus" + "github.com/cloudflare/cloudflared/logger" ) const ( @@ -24,7 +24,7 @@ type Logger struct { filename string file *os.File writeBuffer *bufio.Writer - logger *logrus.Logger + logger logger.Service flushInterval time.Duration maxFileSize int64 done chan struct{} @@ -35,10 +35,10 @@ type Logger struct { // drained and closed when the caller is finished, so instances should call // Close when finished with this Logger instance. Writes will be flushed to disk // every second (fsync). filename is the name of the logfile to be created. The -// logger variable is a logrus that will log all i/o, filesystem error etc, that +// logger variable is a logger service that will log all i/o, filesystem error etc, that // that shouldn't end execution of the logger, but are useful to report to the // caller. -func NewLogger(filename string, logger *logrus.Logger, flushInterval time.Duration, maxFileSize int64) (*Logger, error) { +func NewLogger(filename string, logger logger.Service, flushInterval time.Duration, maxFileSize int64) (*Logger, error) { if logger == nil { return nil, errors.New("logger can't be nil") } @@ -87,7 +87,7 @@ func (l *Logger) writer() { select { case <-ticker.C: if err := l.write(); err != nil { - l.logger.Errorln(err) + l.logger.Errorf("%s", err) } case <-l.done: return diff --git a/sshlog/logger_test.go b/sshlog/logger_test.go index 4e08a504..061b9e33 100644 --- a/sshlog/logger_test.go +++ b/sshlog/logger_test.go @@ -8,20 +8,21 @@ import ( "testing" "time" - "github.com/sirupsen/logrus" + "github.com/cloudflare/cloudflared/logger" ) const logFileName = "test-logger.log" func createLogger(t *testing.T) *Logger { os.Remove(logFileName) - l := logrus.New() + l := logger.NewOutputWriter(logger.NewMockWriteManager()) logger, err := NewLogger(logFileName, l, time.Millisecond, 1024) if err != nil { t.Fatal("couldn't create the logger!", err) } return logger } + // AUTH-2115 TODO: fix this test //func TestWrite(t *testing.T) { // testStr := "hi" diff --git a/sshlog/manager.go b/sshlog/manager.go index 3178eb03..c3045287 100644 --- a/sshlog/manager.go +++ b/sshlog/manager.go @@ -5,13 +5,13 @@ import ( "path/filepath" "time" - "github.com/sirupsen/logrus" + "github.com/cloudflare/cloudflared/logger" ) // Manager be managing logs bruh type Manager interface { - NewLogger(string, *logrus.Logger) (io.WriteCloser, error) - NewSessionLogger(string, *logrus.Logger) (io.WriteCloser, error) + NewLogger(string, logger.Service) (io.WriteCloser, error) + NewSessionLogger(string, logger.Service) (io.WriteCloser, error) } type manager struct { @@ -25,10 +25,10 @@ func New(baseDirectory string) Manager { } } -func (m *manager) NewLogger(name string, logger *logrus.Logger) (io.WriteCloser, error) { +func (m *manager) NewLogger(name string, logger logger.Service) (io.WriteCloser, error) { return NewLogger(filepath.Join(m.baseDirectory, name), logger, time.Second, defaultFileSizeLimit) } -func (m *manager) NewSessionLogger(name string, logger *logrus.Logger) (io.WriteCloser, error) { +func (m *manager) NewSessionLogger(name string, logger logger.Service) (io.WriteCloser, error) { return NewSessionLogger(filepath.Join(m.baseDirectory, name), logger, time.Second, defaultFileSizeLimit) } diff --git a/sshlog/session_logger.go b/sshlog/session_logger.go index 19a6d3bc..1f4ab262 100644 --- a/sshlog/session_logger.go +++ b/sshlog/session_logger.go @@ -3,7 +3,7 @@ package sshlog import ( "time" - "github.com/sirupsen/logrus" + "github.com/cloudflare/cloudflared/logger" capnp "zombiezen.com/go/capnproto2" "zombiezen.com/go/capnproto2/pogs" ) @@ -20,7 +20,7 @@ type sessionLogData struct { } // NewSessionLogger creates a new session logger by encapsulating a Logger object and writing capnp encoded messages to it -func NewSessionLogger(filename string, logger *logrus.Logger, flushInterval time.Duration, maxFileSize int64) (*SessionLogger, error) { +func NewSessionLogger(filename string, logger logger.Service, flushInterval time.Duration, maxFileSize int64) (*SessionLogger, error) { l, err := NewLogger(filename, logger, flushInterval, maxFileSize) if err != nil { return nil, err diff --git a/sshlog/session_logger_test.go b/sshlog/session_logger_test.go index ccda3fe7..35d0aecb 100644 --- a/sshlog/session_logger_test.go +++ b/sshlog/session_logger_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - "github.com/sirupsen/logrus" + "github.com/cloudflare/cloudflared/logger" capnp "zombiezen.com/go/capnproto2" ) @@ -13,7 +13,7 @@ const sessionLogFileName = "test-session-logger.log" func createSessionLogger(t *testing.T) *SessionLogger { os.Remove(sessionLogFileName) - l := logrus.New() + l := logger.NewOutputWriter(logger.NewMockWriteManager()) logger, err := NewSessionLogger(sessionLogFileName, l, time.Millisecond, 1024) if err != nil { t.Fatal("couldn't create the logger!", err) diff --git a/sshserver/host_keys.go b/sshserver/host_keys.go index fcdf3582..8c16d013 100644 --- a/sshserver/host_keys.go +++ b/sshserver/host_keys.go @@ -19,8 +19,8 @@ import ( ) const ( - rsaFilename = "ssh_host_rsa_key" - ecdsaFilename = "ssh_host_ecdsa_key" + rsaFilename = "ssh_host_rsa_key" + ecdsaFilename = "ssh_host_ecdsa_key" ) var defaultHostKeyDir = filepath.Join(".cloudflared", "host_keys") @@ -68,7 +68,7 @@ func (s *SSHProxy) configureRSAKey(basePath string) error { return err } - s.logger.Debug("Created new RSA SSH host key: ", keyPath) + s.logger.Debugf("Created new RSA SSH host key: %s", keyPath) } if err := s.SetOption(ssh.HostKeyFile(keyPath)); err != nil { return errors.Wrap(err, "Could not set SSH RSA host key") @@ -98,7 +98,7 @@ func (s *SSHProxy) configureECDSAKey(basePath string) error { return err } - s.logger.Debug("Created new ECDSA SSH host key: ", keyPath) + s.logger.Debugf("Created new ECDSA SSH host key: %s", keyPath) } if err := s.SetOption(ssh.HostKeyFile(keyPath)); err != nil { return errors.Wrap(err, "Could not set SSH ECDSA host key") diff --git a/sshserver/sshserver_unix.go b/sshserver/sshserver_unix.go index 8a5b9b2d..98f86e5c 100644 --- a/sshserver/sshserver_unix.go +++ b/sshserver/sshserver_unix.go @@ -15,12 +15,12 @@ import ( "strings" "time" + "github.com/cloudflare/cloudflared/logger" "github.com/cloudflare/cloudflared/sshgen" "github.com/cloudflare/cloudflared/sshlog" "github.com/gliderlabs/ssh" "github.com/google/uuid" "github.com/pkg/errors" - "github.com/sirupsen/logrus" gossh "golang.org/x/crypto/ssh" ) @@ -66,7 +66,7 @@ func (c sshConn) Close() error { type SSHProxy struct { ssh.Server hostname string - logger *logrus.Logger + logger logger.Service shutdownC chan struct{} caCert ssh.PublicKey logManager sshlog.Manager @@ -78,7 +78,7 @@ type SSHPreamble struct { } // New creates a new SSHProxy and configures its host keys and authentication by the data provided -func New(logManager sshlog.Manager, logger *logrus.Logger, version, localAddress, hostname, hostKeyDir string, shutdownC chan struct{}, idleTimeout, maxTimeout time.Duration) (*SSHProxy, error) { +func New(logManager sshlog.Manager, logger logger.Service, version, localAddress, hostname, hostKeyDir string, shutdownC chan struct{}, idleTimeout, maxTimeout time.Duration) (*SSHProxy, error) { sshProxy := SSHProxy{ hostname: hostname, logger: logger, @@ -112,7 +112,7 @@ func (s *SSHProxy) Start() error { go func() { <-s.shutdownC if err := s.Close(); err != nil { - s.logger.WithError(err).Error("Cannot close SSH server") + s.logger.Errorf("Cannot close SSH server: %s", err) } }() @@ -141,9 +141,9 @@ func (s *SSHProxy) connCallback(ctx ssh.Context, conn net.Conn) net.Conn { preamble, err := s.readPreamble(conn) if err != nil { if netErr, ok := err.(net.Error); ok && netErr.Timeout() { - s.logger.Warn("Could not establish session. Client likely does not have --destination set and is using old-style ssh config") + s.logger.Info("Could not establish session. Client likely does not have --destination set and is using old-style ssh config") } else if err != io.EOF { - s.logger.WithError(err).Error("failed to read SSH preamble") + s.logger.Errorf("failed to read SSH preamble: %s", err) } return nil } @@ -151,7 +151,7 @@ func (s *SSHProxy) connCallback(ctx ssh.Context, conn net.Conn) net.Conn { logger, sessionID, err := s.auditLogger() if err != nil { - s.logger.WithError(err).Error("failed to configure logger") + s.logger.Errorf("failed to configure logger: %s", err) return nil } ctx.SetValue(sshContextEventLogger, logger) @@ -175,14 +175,14 @@ func (s *SSHProxy) channelHandler(srv *ssh.Server, conn *gossh.ServerConn, newCh msg := fmt.Sprintf("channel type %s is not supported", newChan.ChannelType()) s.logger.Info(msg) if err := newChan.Reject(gossh.UnknownChannelType, msg); err != nil { - s.logger.WithError(err).Error("Error rejecting SSH channel") + s.logger.Errorf("Error rejecting SSH channel: %s", err) } return } localChan, localChanReqs, err := newChan.Accept() if err != nil { - s.logger.WithError(err).Error("Failed to accept session channel") + s.logger.Errorf("Failed to accept session channel: %s", err) return } defer localChan.Close() @@ -196,7 +196,7 @@ func (s *SSHProxy) channelHandler(srv *ssh.Server, conn *gossh.ServerConn, newCh remoteChan, remoteChanReqs, err := client.OpenChannel(newChan.ChannelType(), newChan.ExtraData()) if err != nil { - s.logger.WithError(err).Error("Failed to open remote channel") + s.logger.Errorf("Failed to open remote channel: %s", err) return } @@ -211,13 +211,13 @@ func (s *SSHProxy) proxyChannel(localChan, remoteChan gossh.Channel, localChanRe done := make(chan struct{}, 2) go func() { if _, err := io.Copy(localChan, remoteChan); err != nil { - s.logger.WithError(err).Error("remote to local copy error") + s.logger.Errorf("remote to local copy error: %s", err) } done <- struct{}{} }() go func() { if _, err := io.Copy(remoteChan, localChan); err != nil { - s.logger.WithError(err).Error("local to remote copy error") + s.logger.Errorf("local to remote copy error: %s", err) } done <- struct{}{} }() @@ -227,12 +227,12 @@ func (s *SSHProxy) proxyChannel(localChan, remoteChan gossh.Channel, localChanRe localStderr := localChan.Stderr() go func() { if _, err := io.Copy(remoteStderr, localStderr); err != nil { - s.logger.WithError(err).Error("stderr local to remote copy error") + s.logger.Errorf("stderr local to remote copy error: %s", err) } }() go func() { if _, err := io.Copy(localStderr, remoteStderr); err != nil { - s.logger.WithError(err).Error("stderr remote to local copy error") + s.logger.Errorf("stderr remote to local copy error: %s", err) } }() @@ -247,7 +247,7 @@ func (s *SSHProxy) proxyChannel(localChan, remoteChan gossh.Channel, localChanRe return } if err := s.forwardChannelRequest(remoteChan, req); err != nil { - s.logger.WithError(err).Error("Failed to forward request") + s.logger.Errorf("Failed to forward request: %s", err) return } @@ -258,7 +258,7 @@ func (s *SSHProxy) proxyChannel(localChan, remoteChan gossh.Channel, localChanRe return } if err := s.forwardChannelRequest(localChan, req); err != nil { - s.logger.WithError(err).Error("Failed to forward request") + s.logger.Errorf("Failed to forward request: %s", err) return } case <-done: @@ -278,7 +278,7 @@ func (s *SSHProxy) readPreamble(conn net.Conn) (*SSHPreamble, error) { } defer func() { if err := conn.SetReadDeadline(time.Time{}); err != nil { - s.logger.WithError(err).Error("Failed to unset conn read deadline") + s.logger.Errorf("Failed to unset conn read deadline: %s", err) } }() @@ -341,7 +341,7 @@ func (s *SSHProxy) dialDestination(ctx ssh.Context) (*gossh.Client, error) { signer, err := s.genSSHSigner(preamble.JWT) if err != nil { - s.logger.WithError(err).Error("Failed to generate signed short lived cert") + s.logger.Errorf("Failed to generate signed short lived cert: %s", err) return nil, err } s.logger.Debugf("Short lived certificate for %s connecting to %s:\n\n%s", ctx.User(), preamble.Destination, gossh.MarshalAuthorizedKey(signer.PublicKey())) @@ -356,7 +356,7 @@ func (s *SSHProxy) dialDestination(ctx ssh.Context) (*gossh.Client, error) { client, err := gossh.Dial("tcp", preamble.Destination, clientConfig) if err != nil { - s.logger.WithError(err).Info("Failed to connect to destination SSH server") + s.logger.Errorf("Failed to connect to destination SSH server: %s", err) return nil, err } return client, nil @@ -421,7 +421,7 @@ func (s *SSHProxy) logChannelRequest(req *gossh.Request, conn *gossh.ServerConn, case "exec": var payload struct{ Value string } if err := gossh.Unmarshal(req.Payload, &payload); err != nil { - s.logger.WithError(err).Errorf("Failed to unmarshal channel request payload: %s:%s", req.Type, req.Payload) + s.logger.Errorf("Failed to unmarshal channel request payload: %s:%s with error: %s", req.Type, req.Payload, err) } event = payload.Value @@ -481,11 +481,11 @@ func (s *SSHProxy) logAuditEvent(conn *gossh.ServerConn, event, eventType string } data, err := json.Marshal(&ae) if err != nil { - s.logger.WithError(err).Error("Failed to marshal audit event. malformed audit object") + s.logger.Errorf("Failed to marshal audit event. malformed audit object: %s", err) return } line := string(data) + "\n" if _, err := writer.Write([]byte(line)); err != nil { - s.logger.WithError(err).Error("Failed to write audit event.") + s.logger.Errorf("Failed to write audit event: %s", err) } } diff --git a/sshserver/sshserver_windows.go b/sshserver/sshserver_windows.go index f87c582b..7b338417 100644 --- a/sshserver/sshserver_windows.go +++ b/sshserver/sshserver_windows.go @@ -7,8 +7,8 @@ import ( "time" + "github.com/cloudflare/cloudflared/logger" "github.com/cloudflare/cloudflared/sshlog" - "github.com/sirupsen/logrus" ) const SSHPreambleLength = 2 @@ -20,7 +20,7 @@ type SSHPreamble struct { JWT string } -func New(_ sshlog.Manager, _ *logrus.Logger, _, _, _, _ string, _ chan struct{}, _, _ time.Duration) (*SSHServer, error) { +func New(_ sshlog.Manager, _ logger.Service, _, _, _, _ string, _ chan struct{}, _, _ time.Duration) (*SSHServer, error) { return nil, errors.New("cloudflared ssh server is not supported on windows") } diff --git a/streamhandler/stream_handler.go b/streamhandler/stream_handler.go index 66763bcd..2b40b0a1 100644 --- a/streamhandler/stream_handler.go +++ b/streamhandler/stream_handler.go @@ -7,11 +7,11 @@ import ( "strconv" "github.com/cloudflare/cloudflared/h2mux" + "github.com/cloudflare/cloudflared/logger" "github.com/cloudflare/cloudflared/tunnelhostnamemapper" "github.com/cloudflare/cloudflared/tunnelrpc" "github.com/cloudflare/cloudflared/tunnelrpc/pogs" "github.com/pkg/errors" - "github.com/sirupsen/logrus" "zombiezen.com/go/capnproto2/rpc" ) @@ -45,19 +45,19 @@ type StreamHandler struct { useConfigResultChan <-chan *pogs.UseConfigurationResult // originMapper maps tunnel hostname to origin service tunnelHostnameMapper *tunnelhostnamemapper.TunnelHostnameMapper - logger *logrus.Entry + logger logger.Service } // NewStreamHandler creates a new StreamHandler func NewStreamHandler(newConfigChan chan<- *pogs.ClientConfig, useConfigResultChan <-chan *pogs.UseConfigurationResult, - logger *logrus.Logger, + logger logger.Service, ) *StreamHandler { return &StreamHandler{ newConfigChan: newConfigChan, useConfigResultChan: useConfigResultChan, tunnelHostnameMapper: tunnelhostnamemapper.NewTunnelHostnameMapper(), - logger: logger.WithField("subsystem", "streamHandler"), + logger: logger, } } @@ -66,14 +66,14 @@ func (s *StreamHandler) UseConfiguration(ctx context.Context, config *pogs.Clien select { case <-ctx.Done(): err := fmt.Errorf("Timeout while sending new config to Supervisor") - s.logger.Error(err) + s.logger.Errorf("streamHandler: %s", err) return nil, err case s.newConfigChan <- config: } select { case <-ctx.Done(): err := fmt.Errorf("Timeout applying new configuration") - s.logger.Error(err) + s.logger.Errorf("streamHandler: %s", err) return nil, err case result := <-s.useConfigResultChan: return result, nil @@ -93,9 +93,9 @@ func (s *StreamHandler) UpdateConfig(newConfig []*pogs.ReverseProxyConfig) (fail toAdd := s.tunnelHostnameMapper.ToAdd(newConfig) for _, tunnelConfig := range toAdd { tunnelHostname := tunnelConfig.TunnelHostname - originSerice, err := tunnelConfig.OriginConfig.Service() + originSerice, err := tunnelConfig.OriginConfig.Service(s.logger) if err != nil { - s.logger.WithField("tunnelHostname", tunnelHostname).WithError(err).Error("Invalid origin service config") + s.logger.Errorf("streamHandler: tunnelHostname: %s Invalid origin service config: %s", tunnelHostname, err) failedConfigs = append(failedConfigs, &pogs.FailedConfig{ Config: tunnelConfig, Reason: tunnelConfig.FailReason(err), @@ -103,7 +103,7 @@ func (s *StreamHandler) UpdateConfig(newConfig []*pogs.ReverseProxyConfig) (fail continue } s.tunnelHostnameMapper.Add(tunnelConfig.TunnelHostname, originSerice) - s.logger.WithField("tunnelHostname", tunnelHostname).Infof("New origin service config: %v", originSerice.Summary()) + s.logger.Infof("streamHandler: tunnelHostname: %s New origin service config: %v", tunnelHostname, originSerice.Summary()) } return } @@ -114,7 +114,7 @@ func (s *StreamHandler) ServeStream(stream *h2mux.MuxedStream) error { return s.serveRPC(stream) } if err := s.serveRequest(stream); err != nil { - s.logger.Error(err) + s.logger.Errorf("streamHandler: %s", err) return err } return nil @@ -123,11 +123,10 @@ func (s *StreamHandler) ServeStream(stream *h2mux.MuxedStream) error { func (s *StreamHandler) serveRPC(stream *h2mux.MuxedStream) error { stream.WriteHeaders([]h2mux.Header{{Name: ":status", Value: "200"}}) main := pogs.ClientService_ServerToClient(s) - rpcLogger := s.logger.WithField("subsystem", "clientserver-rpc") rpcConn := rpc.NewConn( - tunnelrpc.NewTransportLogger(rpcLogger, rpc.StreamTransport(stream)), + tunnelrpc.NewTransportLogger(s.logger, rpc.StreamTransport(stream)), rpc.MainInterface(main.Client), - tunnelrpc.ConnLog(s.logger.WithField("subsystem", "clientserver-rpc-transport")), + tunnelrpc.ConnLog(s.logger), ) return rpcConn.Wait() } @@ -151,8 +150,8 @@ func (s *StreamHandler) serveRequest(stream *h2mux.MuxedStream) error { return errors.Wrap(err, "cannot create request") } - logger := s.requestLogger(req, tunnelHostname) - logger.Debugf("Request Headers %+v", req.Header) + cfRay := s.logRequest(req, tunnelHostname) + s.logger.Debugf("streamHandler: tunnelHostname: %s CF-RAY: %s Request Headers %+v", tunnelHostname, cfRay, req.Header) resp, err := originService.Proxy(stream, req) if err != nil { @@ -160,23 +159,22 @@ func (s *StreamHandler) serveRequest(stream *h2mux.MuxedStream) error { return errors.Wrap(err, "cannot proxy request") } - logger.WithField("status", resp.Status).Debugf("Response Headers %+v", resp.Header) + s.logger.Debugf("streamHandler: tunnelHostname: %s CF-RAY: %s status: %s Response Headers %+v", tunnelHostname, cfRay, resp.Status, resp.Header) return nil } -func (s *StreamHandler) requestLogger(req *http.Request, tunnelHostname h2mux.TunnelHostname) *logrus.Entry { +func (s *StreamHandler) logRequest(req *http.Request, tunnelHostname h2mux.TunnelHostname) string { cfRay := FindCfRayHeader(req) lbProbe := IsLBProbeRequest(req) - logger := s.logger.WithField("tunnelHostname", tunnelHostname) + logger := s.logger if cfRay != "" { - logger = logger.WithField("CF-RAY", cfRay) - logger.Debugf("%s %s %s", req.Method, req.URL, req.Proto) + logger.Debugf("streamHandler: tunnelHostname: %s CF-RAY: %s %s %s %s", tunnelHostname, cfRay, req.Method, req.URL, req.Proto) } else if lbProbe { - logger.Debugf("Load Balancer health check %s %s %s", req.Method, req.URL, req.Proto) + logger.Debugf("streamHandler: tunnelHostname: %s CF-RAY: %s Load Balancer health check %s %s %s", tunnelHostname, cfRay, req.Method, req.URL, req.Proto) } else { - logger.Warnf("Requests %v does not have CF-RAY header. Please open a support ticket with Cloudflare.", req) + logger.Infof("streamHandler: tunnelHostname: %s CF-RAY: %s Requests %v does not have CF-RAY header. Please open a support ticket with Cloudflare.", tunnelHostname, cfRay, req) } - return logger + return cfRay } func (s *StreamHandler) writeErrorStatus(stream *h2mux.MuxedStream, status *httpErrorStatus) { diff --git a/streamhandler/stream_handler_test.go b/streamhandler/stream_handler_test.go index 721f5a6c..628524b3 100644 --- a/streamhandler/stream_handler_test.go +++ b/streamhandler/stream_handler_test.go @@ -12,9 +12,9 @@ import ( "time" "github.com/cloudflare/cloudflared/h2mux" + "github.com/cloudflare/cloudflared/logger" "github.com/cloudflare/cloudflared/tunnelrpc/pogs" "github.com/pkg/errors" - "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" "golang.org/x/sync/errgroup" ) @@ -39,9 +39,10 @@ var ( ) func TestServeRequest(t *testing.T) { + l := logger.NewOutputWriter(logger.NewMockWriteManager()) configChan := make(chan *pogs.ClientConfig) useConfigResultChan := make(chan *pogs.UseConfigurationResult) - streamHandler := NewStreamHandler(configChan, useConfigResultChan, logrus.New()) + streamHandler := NewStreamHandler(configChan, useConfigResultChan, l) message := []byte("Hello cloudflared") httpServer := httptest.NewServer(&mockHTTPHandler{message}) @@ -72,8 +73,9 @@ func TestServeRequest(t *testing.T) { func createStreamHandler() *StreamHandler { configChan := make(chan *pogs.ClientConfig) useConfigResultChan := make(chan *pogs.UseConfigurationResult) + l := logger.NewOutputWriter(logger.NewMockWriteManager()) - return NewStreamHandler(configChan, useConfigResultChan, logrus.New()) + return NewStreamHandler(configChan, useConfigResultChan, l) } func createRequestMuxPair(t *testing.T, streamHandler *StreamHandler) *DefaultMuxerPair { @@ -185,7 +187,7 @@ func NewDefaultMuxerPair(t *testing.T, h h2mux.MuxedStreamHandler) *DefaultMuxer Handler: h, IsClient: true, Name: "origin", - Logger: logrus.NewEntry(logrus.New()), + Logger: logger.NewOutputWriter(logger.NewMockWriteManager()), DefaultWindowSize: (1 << 8) - 1, MaxWindowSize: (1 << 15) - 1, StreamWriteBufferMaxLen: 1024, @@ -195,7 +197,7 @@ func NewDefaultMuxerPair(t *testing.T, h h2mux.MuxedStreamHandler) *DefaultMuxer Timeout: testHandshakeTimeout, IsClient: false, Name: "edge", - Logger: logrus.NewEntry(logrus.New()), + Logger: logger.NewOutputWriter(logger.NewMockWriteManager()), DefaultWindowSize: (1 << 8) - 1, MaxWindowSize: (1 << 15) - 1, StreamWriteBufferMaxLen: 1024, diff --git a/supervisor/supervisor.go b/supervisor/supervisor.go index ca9764b1..8dee085d 100644 --- a/supervisor/supervisor.go +++ b/supervisor/supervisor.go @@ -16,10 +16,10 @@ import ( "github.com/cloudflare/cloudflared/connection" "github.com/cloudflare/cloudflared/edgediscovery" "github.com/cloudflare/cloudflared/h2mux" + "github.com/cloudflare/cloudflared/logger" "github.com/cloudflare/cloudflared/streamhandler" "github.com/cloudflare/cloudflared/tunnelrpc/pogs" "github.com/prometheus/client_golang/prometheus" - "github.com/sirupsen/logrus" ) type Supervisor struct { @@ -30,7 +30,7 @@ type Supervisor struct { newConfigChan <-chan *pogs.ClientConfig useConfigResultChan chan<- *pogs.UseConfigurationResult state *state - logger *logrus.Entry + logger logger.Service metrics metrics } @@ -62,7 +62,7 @@ func NewSupervisor( cloudflaredConfig *connection.CloudflaredConfig, autoupdater *updater.AutoUpdater, supportAutoupdate bool, - logger *logrus.Logger, + logger logger.Service, ) (*Supervisor, error) { newConfigChan := make(chan *pogs.ClientConfig) useConfigResultChan := make(chan *pogs.UseConfigurationResult) @@ -93,7 +93,7 @@ func NewSupervisor( newConfigChan: newConfigChan, useConfigResultChan: useConfigResultChan, state: newState(defaultClientConfig), - logger: logger.WithField("subsystem", "supervisor"), + logger: logger, metrics: newMetrics(), }, nil } @@ -120,7 +120,7 @@ func (s *Supervisor) Run(ctx context.Context) error { } err := errGroup.Wait() - s.logger.Warnf("Supervisor terminated, reason: %v", err) + s.logger.Errorf("Supervisor terminated, reason: %v", err) return err } diff --git a/tlsconfig/certreloader.go b/tlsconfig/certreloader.go index f8e1fdca..a8fb7499 100644 --- a/tlsconfig/certreloader.go +++ b/tlsconfig/certreloader.go @@ -8,9 +8,9 @@ import ( "runtime" "sync" + "github.com/cloudflare/cloudflared/logger" "github.com/getsentry/raven-go" "github.com/pkg/errors" - "github.com/sirupsen/logrus" "gopkg.in/urfave/cli.v2" ) @@ -65,7 +65,7 @@ func (cr *CertReloader) LoadCert() error { return nil } -func LoadOriginCA(c *cli.Context, logger *logrus.Logger) (*x509.CertPool, error) { +func LoadOriginCA(c *cli.Context, logger logger.Service) (*x509.CertPool, error) { var originCustomCAPool []byte originCAPoolFilename := c.String(OriginCAPoolFlag) @@ -151,7 +151,7 @@ func CreateTunnelConfig(c *cli.Context) (*tls.Config, error) { return tlsConfig, nil } -func loadOriginCertPool(originCAPoolPEM []byte, logger *logrus.Logger) (*x509.CertPool, error) { +func loadOriginCertPool(originCAPoolPEM []byte, logger logger.Service) (*x509.CertPool, error) { // Get the global pool certPool, err := loadGlobalCertPool(logger) if err != nil { @@ -161,19 +161,19 @@ func loadOriginCertPool(originCAPoolPEM []byte, logger *logrus.Logger) (*x509.Ce // Then, add any custom origin CA pool the user may have passed if originCAPoolPEM != nil { if !certPool.AppendCertsFromPEM(originCAPoolPEM) { - logger.Warn("could not append the provided origin CA to the cloudflared certificate pool") + logger.Info("could not append the provided origin CA to the cloudflared certificate pool") } } return certPool, nil } -func loadGlobalCertPool(logger *logrus.Logger) (*x509.CertPool, error) { +func loadGlobalCertPool(logger logger.Service) (*x509.CertPool, error) { // First, obtain the system certificate pool certPool, err := x509.SystemCertPool() if err != nil { if runtime.GOOS != "windows" { // See https://github.com/golang/go/issues/16736 - logger.WithError(err).Warn("error obtaining the system certificates") + logger.Infof("error obtaining the system certificates: %s", err) } certPool = x509.NewCertPool() } diff --git a/tunneldns/https_upstream.go b/tunneldns/https_upstream.go index 8494fd5f..b4c756fa 100644 --- a/tunneldns/https_upstream.go +++ b/tunneldns/https_upstream.go @@ -11,9 +11,9 @@ import ( "net/url" "time" + "github.com/cloudflare/cloudflared/logger" "github.com/miekg/dns" "github.com/pkg/errors" - log "github.com/sirupsen/logrus" "golang.org/x/net/http2" ) @@ -26,15 +26,16 @@ type UpstreamHTTPS struct { client *http.Client endpoint *url.URL bootstraps []string + logger logger.Service } // NewUpstreamHTTPS creates a new DNS over HTTPS upstream from endpoint -func NewUpstreamHTTPS(endpoint string, bootstraps []string) (Upstream, error) { +func NewUpstreamHTTPS(endpoint string, bootstraps []string, logger logger.Service) (Upstream, error) { u, err := url.Parse(endpoint) if err != nil { return nil, err } - return &UpstreamHTTPS{client: configureClient(u.Hostname()), endpoint: u, bootstraps: bootstraps}, nil + return &UpstreamHTTPS{client: configureClient(u.Hostname()), endpoint: u, bootstraps: bootstraps, logger: logger}, nil } // Exchange provides an implementation for the Upstream interface @@ -48,12 +49,12 @@ func (u *UpstreamHTTPS) Exchange(ctx context.Context, query *dns.Msg) (*dns.Msg, for _, bootstrap := range u.bootstraps { endpoint, client, err := configureBootstrap(bootstrap) if err != nil { - log.WithError(err).Errorf("failed to configure boostrap upstream %s", bootstrap) + u.logger.Errorf("failed to configure boostrap upstream %s: %s", bootstrap, err) continue } - msg, err := exchange(queryBuf, query.Id, endpoint, client) + msg, err := exchange(queryBuf, query.Id, endpoint, client, u.logger) if err != nil { - log.WithError(err).Errorf("failed to connect to a boostrap upstream %s", bootstrap) + u.logger.Errorf("failed to connect to a boostrap upstream %s: %s", bootstrap, err) continue } return msg, nil @@ -61,10 +62,10 @@ func (u *UpstreamHTTPS) Exchange(ctx context.Context, query *dns.Msg) (*dns.Msg, return nil, fmt.Errorf("failed to reach any bootstrap upstream: %v", u.bootstraps) } - return exchange(queryBuf, query.Id, u.endpoint, u.client) + return exchange(queryBuf, query.Id, u.endpoint, u.client, u.logger) } -func exchange(msg []byte, queryID uint16, endpoint *url.URL, client *http.Client) (*dns.Msg, error) { +func exchange(msg []byte, queryID uint16, endpoint *url.URL, client *http.Client, logger logger.Service) (*dns.Msg, error) { // No content negotiation for now, use DNS wire format buf, backendErr := exchangeWireformat(msg, endpoint, client) if backendErr == nil { @@ -77,7 +78,7 @@ func exchange(msg []byte, queryID uint16, endpoint *url.URL, client *http.Client return response, nil } - log.WithError(backendErr).Errorf("failed to connect to an HTTPS backend %q", endpoint) + logger.Errorf("failed to connect to an HTTPS backend %q: %s", endpoint, backendErr) return nil, backendErr } diff --git a/tunneldns/tunnel.go b/tunneldns/tunnel.go index 1cbe1136..e85d0a26 100644 --- a/tunneldns/tunnel.go +++ b/tunneldns/tunnel.go @@ -8,7 +8,8 @@ import ( "sync" "syscall" - "github.com/cloudflare/cloudflared/log" + "github.com/cloudflare/cloudflared/cmd/cloudflared/config" + "github.com/cloudflare/cloudflared/logger" "github.com/cloudflare/cloudflared/metrics" "github.com/coredns/coredns/core/dnsserver" @@ -18,26 +19,31 @@ import ( "gopkg.in/urfave/cli.v2" ) -var logger = log.CreateLogger() - // Listener is an adapter between CoreDNS server and Warp runnable type Listener struct { server *dnsserver.Server wg sync.WaitGroup + logger logger.Service } // Run implements a foreground runner func Run(c *cli.Context) error { + logDirectory, logLevel := config.FindLogSettings() + logger, err := logger.New(logger.DefaultFile(logDirectory), logger.LogLevelString(logLevel)) + if err != nil { + return errors.Wrap(err, "error setting up logger") + } + metricsListener, err := net.Listen("tcp", c.String("metrics")) if err != nil { - logger.WithError(err).Fatal("Failed to open the metrics listener") + logger.Fatalf("Failed to open the metrics listener: %s", err) } go metrics.ServeMetrics(metricsListener, nil, logger) - listener, err := CreateListener(c.String("address"), uint16(c.Uint("port")), c.StringSlice("upstream"), c.StringSlice("bootstrap")) + listener, err := CreateListener(c.String("address"), uint16(c.Uint("port")), c.StringSlice("upstream"), c.StringSlice("bootstrap"), logger) if err != nil { - logger.WithError(err).Errorf("Failed to create the listeners") + logger.Errorf("Failed to create the listeners: %s", err) return err } @@ -45,7 +51,7 @@ func Run(c *cli.Context) error { readySignal := make(chan struct{}) err = listener.Start(readySignal) if err != nil { - logger.WithError(err).Errorf("Failed to start the listeners") + logger.Errorf("Failed to start the listeners: %s", err) return listener.Stop() } <-readySignal @@ -59,7 +65,7 @@ func Run(c *cli.Context) error { // Shut down server err = listener.Stop() if err != nil { - logger.WithError(err).Errorf("failed to stop") + logger.Errorf("failed to stop: %s", err) } return err } @@ -80,7 +86,7 @@ func createConfig(address string, port uint16, p plugin.Handler) *dnsserver.Conf // Start blocks for serving requests func (l *Listener) Start(readySignal chan struct{}) error { defer close(readySignal) - logger.WithField("addr", l.server.Address()).Infof("Starting DNS over HTTPS proxy server") + l.logger.Infof("Starting DNS over HTTPS proxy server on: %s", l.server.Address()) // Start UDP listener if udp, err := l.server.ListenPacket(); err == nil { @@ -117,12 +123,12 @@ func (l *Listener) Stop() error { } // CreateListener configures the server and bound sockets -func CreateListener(address string, port uint16, upstreams []string, bootstraps []string) (*Listener, error) { +func CreateListener(address string, port uint16, upstreams []string, bootstraps []string, logger logger.Service) (*Listener, error) { // Build the list of upstreams upstreamList := make([]Upstream, 0) for _, url := range upstreams { - logger.WithField("url", url).Infof("Adding DNS upstream") - upstream, err := NewUpstreamHTTPS(url, bootstraps) + logger.Infof("Adding DNS upstream - url: %s", url) + upstream, err := NewUpstreamHTTPS(url, bootstraps, logger) if err != nil { return nil, errors.Wrap(err, "failed to create HTTPS upstream") } @@ -144,5 +150,5 @@ func CreateListener(address string, port uint16, upstreams []string, bootstraps return nil, err } - return &Listener{server: server}, nil + return &Listener{server: server, logger: logger}, nil } diff --git a/tunnelrpc/log.go b/tunnelrpc/log.go index 848012b8..262c0a72 100644 --- a/tunnelrpc/log.go +++ b/tunnelrpc/log.go @@ -3,14 +3,14 @@ package tunnelrpc import ( "context" - log "github.com/sirupsen/logrus" + "github.com/cloudflare/cloudflared/logger" "golang.org/x/net/trace" "zombiezen.com/go/capnproto2/rpc" ) // ConnLogger wraps a logrus *log.Entry for a connection. type ConnLogger struct { - Entry *log.Entry + Entry logger.Service } func (c ConnLogger) Infof(ctx context.Context, format string, args ...interface{}) { @@ -21,7 +21,7 @@ func (c ConnLogger) Errorf(ctx context.Context, format string, args ...interface c.Entry.Errorf(format, args...) } -func ConnLog(log *log.Entry) rpc.ConnOption { +func ConnLog(log logger.Service) rpc.ConnOption { return rpc.ConnLog(ConnLogger{log}) } diff --git a/tunnelrpc/logtransport.go b/tunnelrpc/logtransport.go index 80fdc6a1..1d290ef4 100644 --- a/tunnelrpc/logtransport.go +++ b/tunnelrpc/logtransport.go @@ -5,7 +5,7 @@ import ( "bytes" "context" - log "github.com/sirupsen/logrus" + "github.com/cloudflare/cloudflared/logger" "zombiezen.com/go/capnproto2/encoding/text" "zombiezen.com/go/capnproto2/rpc" rpccapnp "zombiezen.com/go/capnproto2/std/capnp/rpc" @@ -13,28 +13,28 @@ import ( type transport struct { rpc.Transport - l *log.Entry + l logger.Service } -// New creates a new logger that proxies messages to and from t and +// NewTransportLogger creates a new logger that proxies messages to and from t and // logs them to l. If l is nil, then the log package's default // logger is used. -func NewTransportLogger(l *log.Entry, t rpc.Transport) rpc.Transport { +func NewTransportLogger(l logger.Service, t rpc.Transport) rpc.Transport { return &transport{Transport: t, l: l} } func (t *transport) SendMessage(ctx context.Context, msg rpccapnp.Message) error { - t.l.Debugf("tx %s", formatMsg(msg)) + t.l.Debugf("rpcconnect: tx %s", formatMsg(msg)) return t.Transport.SendMessage(ctx, msg) } func (t *transport) RecvMessage(ctx context.Context) (rpccapnp.Message, error) { msg, err := t.Transport.RecvMessage(ctx) if err != nil { - t.l.WithError(err).Debug("rx error") + t.l.Debugf("rpcconnect: rx error: %s", err) return msg, err } - t.l.Debugf("rx %s", formatMsg(msg)) + t.l.Debugf("rpcconnect: rx %s", formatMsg(msg)) return msg, nil } diff --git a/tunnelrpc/pogs/config.go b/tunnelrpc/pogs/config.go index a15caca3..6ef537ae 100644 --- a/tunnelrpc/pogs/config.go +++ b/tunnelrpc/pogs/config.go @@ -11,6 +11,7 @@ import ( "time" "github.com/cloudflare/cloudflared/h2mux" + "github.com/cloudflare/cloudflared/logger" "github.com/cloudflare/cloudflared/originservice" "github.com/cloudflare/cloudflared/tlsconfig" "github.com/cloudflare/cloudflared/tunnelrpc" @@ -171,7 +172,7 @@ func (_ *ReverseProxyConfig) isFallibleConfig() {} //go-sumtype:decl OriginConfig type OriginConfig interface { // Service returns a OriginService used to proxy to the origin - Service() (originservice.OriginService, error) + Service(logger.Service) (originservice.OriginService, error) // go-sumtype requires at least one unexported method, otherwise it will complain that interface is not sealed isOriginConfig() } @@ -191,7 +192,7 @@ type HTTPOriginConfig struct { ChunkedEncoding bool } -func (hc *HTTPOriginConfig) Service() (originservice.OriginService, error) { +func (hc *HTTPOriginConfig) Service(logger logger.Service) (originservice.OriginService, error) { rootCAs, err := tlsconfig.LoadCustomOriginCA(hc.OriginCAPool) if err != nil { return nil, err @@ -239,7 +240,7 @@ type WebSocketOriginConfig struct { OriginServerName string } -func (wsc *WebSocketOriginConfig) Service() (originservice.OriginService, error) { +func (wsc *WebSocketOriginConfig) Service(logger logger.Service) (originservice.OriginService, error) { rootCAs, err := tlsconfig.LoadCustomOriginCA(wsc.OriginCAPool) if err != nil { return nil, err @@ -254,14 +255,14 @@ func (wsc *WebSocketOriginConfig) Service() (originservice.OriginService, error) if err != nil { return nil, errors.Wrapf(err, "%s is not a valid URL", wsc.URLString) } - return originservice.NewWebSocketService(tlsConfig, url) + return originservice.NewWebSocketService(tlsConfig, url, logger) } func (*WebSocketOriginConfig) isOriginConfig() {} type HelloWorldOriginConfig struct{} -func (*HelloWorldOriginConfig) Service() (originservice.OriginService, error) { +func (*HelloWorldOriginConfig) Service(logger logger.Service) (originservice.OriginService, error) { helloCert, err := tlsconfig.GetHelloCertificateX509() if err != nil { return nil, errors.Wrap(err, "Cannot get Hello World server certificate") @@ -282,7 +283,7 @@ func (*HelloWorldOriginConfig) Service() (originservice.OriginService, error) { TLSHandshakeTimeout: 10 * time.Second, ExpectContinueTimeout: 1 * time.Second, } - return originservice.NewHelloWorldService(transport) + return originservice.NewHelloWorldService(transport, logger) } func (*HelloWorldOriginConfig) isOriginConfig() {} diff --git a/tunnelrpc/pogs/config_test.go b/tunnelrpc/pogs/config_test.go index 38ba5976..199a3d47 100644 --- a/tunnelrpc/pogs/config_test.go +++ b/tunnelrpc/pogs/config_test.go @@ -8,6 +8,7 @@ import ( "github.com/stretchr/testify/assert" + "github.com/cloudflare/cloudflared/logger" "github.com/cloudflare/cloudflared/tunnelrpc" capnp "zombiezen.com/go/capnproto2" ) @@ -241,9 +242,10 @@ func TestOriginConfigInvalidURL(t *testing.T) { URLString: "127.0.0.1:36192", }, } + logger := logger.NewOutputWriter(logger.NewMockWriteManager()) for _, config := range invalidConfigs { - service, err := config.Service() + service, err := config.Service(logger) assert.Error(t, err) assert.Nil(t, service) } diff --git a/tunnelstore/client.go b/tunnelstore/client.go index fcd5f92c..fbddee8a 100644 --- a/tunnelstore/client.go +++ b/tunnelstore/client.go @@ -9,8 +9,8 @@ import ( "strings" "time" + "github.com/cloudflare/cloudflared/logger" "github.com/pkg/errors" - "github.com/sirupsen/logrus" ) const ( @@ -42,11 +42,12 @@ type RESTClient struct { baseURL string authToken string client http.Client + logger logger.Service } var _ Client = (*RESTClient)(nil) -func NewRESTClient(baseURL string, accountTag string, authToken string) *RESTClient { +func NewRESTClient(baseURL string, accountTag string, authToken string, logger logger.Service) *RESTClient { if strings.HasSuffix(baseURL, "/") { baseURL = baseURL[:len(baseURL)-1] } @@ -61,6 +62,7 @@ func NewRESTClient(baseURL string, accountTag string, authToken string) *RESTCli }, Timeout: defaultTimeout, }, + logger: logger, } } @@ -146,7 +148,7 @@ func (r *RESTClient) resolve(target string) string { func (r *RESTClient) sendRequest(method string, target string, body io.Reader) (*http.Response, error) { url := r.resolve(target) - logrus.Debugf("%s %s", method, url) + r.logger.Debugf("%s %s", method, url) req, err := http.NewRequest(method, url, body) if err != nil { return nil, errors.Wrapf(err, "can't create %s request", method) @@ -180,5 +182,3 @@ func statusCodeToError(op string, resp *http.Response) error { return errors.Errorf("API call to %s tunnel failed with status %d: %s", op, resp.StatusCode, http.StatusText(resp.StatusCode)) } - - diff --git a/vendor/github.com/mattn/go-colorable/LICENSE b/vendor/github.com/acmacalister/skittles/LICENSE similarity index 96% rename from vendor/github.com/mattn/go-colorable/LICENSE rename to vendor/github.com/acmacalister/skittles/LICENSE index 91b5cef3..e8ff4073 100644 --- a/vendor/github.com/mattn/go-colorable/LICENSE +++ b/vendor/github.com/acmacalister/skittles/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2016 Yasuhiro Matsumoto +Copyright (c) 2016 Austin Cherry Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/vendor/github.com/acmacalister/skittles/README.md b/vendor/github.com/acmacalister/skittles/README.md new file mode 100644 index 00000000..013ef2f9 --- /dev/null +++ b/vendor/github.com/acmacalister/skittles/README.md @@ -0,0 +1,40 @@ +Skittles +======== + +Miminal package for terminal colors/ANSI escape code. + +![alt tag](https://raw.githubusercontent.com/acmacalister/skittles/master/pictures/terminal-colors.png) + +## Install + +`go get github.com/acmacalister/skittles` + +`import "github.com/acmacalister/skittles"` + +## Example + +```go +package main + +import ( + "fmt" + "github.com/acmacalister/skittles" +) + +func main() { + fmt.Println(skittles.Red("Red's my favorite color")) +} +``` + +## Supported Platforms + +Only tested on OS X terminal app, but I would expect it to work with any unix based terminal. + +## Docs + +* [GoDoc](http://godoc.org/github.com/acmacalister/skittles) + +## Help + +* [Github](https://github.com/acmacalister) +* [Twitter](http://twitter.com/acmacalister) diff --git a/vendor/github.com/acmacalister/skittles/skittles.go b/vendor/github.com/acmacalister/skittles/skittles.go new file mode 100644 index 00000000..aa61627c --- /dev/null +++ b/vendor/github.com/acmacalister/skittles/skittles.go @@ -0,0 +1,153 @@ +// Miminal package for terminal colors/ANSI escape code. +// Check out the source here https://github.com/acmacalister/skittles. +// Also see the example directory for another example on how to use skittles. +// +// package main +// +// import ( +// "fmt" +// "github.com/acmacalister/skittles" +// ) +// +// func main() { +// fmt.Println(skittles.Red("Red's my favorite color")) +// } +package skittles + +import ( + "fmt" + "strings" +) + +// source: http://www.termsys.demon.co.uk/vtansi.htm +// source: http://ascii-table.com/ansi-escape-sequences.php + +const ( + nofmt = "0" + bold = "1" + underline = "4" + blink = "5" + inverse = "7" // attributes end at 7 + black = "30" // colors start at 30 + red = "31" + green = "32" + yellow = "33" + blue = "34" + magenta = "35" + cyan = "36" + white = "37" // colors end at 37 + blackBackground = "40" // background colors start at 40 + redBackground = "41" + greenBackground = "42" + yellowBackground = "43" + blueBackground = "44" + magentaBackground = "45" + cyanBackground = "46" + whiteBackground = "47" +) + +// makeFunction returns a function that formats some text with the provided list +// of ANSI escape codes. +func makeFunction(attributes []string) func(interface{}) string { + return func(text interface{}) string { + return fmt.Sprintf("\033[%sm%s\033[0m", strings.Join(attributes, ";"), text) + } +} + +var ( + // Reset resets all formatting. + Reset = makeFunction([]string{nofmt}) + // Bold makes terminal text bold and doesn't add any color. + Bold = makeFunction([]string{bold}) + // Underline makes terminal text underlined and doesn't add any color. + Underline = makeFunction([]string{underline}) + // Blink makes terminal text blink and doesn't add any color. + Blink = makeFunction([]string{blink}) + // Inverse inverts terminal text and doesn't add any color. + Inverse = makeFunction([]string{inverse}) + + // Black makes terminal text black. + Black = makeFunction([]string{black}) + // Red makes terminal text red. + Red = makeFunction([]string{red}) + // Green makes terminal text green. + Green = makeFunction([]string{green}) + // Yellow makes terminal text yellow. + Yellow = makeFunction([]string{yellow}) + // Blue makes terminal text blue. + Blue = makeFunction([]string{blue}) + // Magenta makes terminal text magenta. + Magenta = makeFunction([]string{magenta}) + // Cyan makes terminal text cyan. + Cyan = makeFunction([]string{cyan}) + // White makes terminal text white. + White = makeFunction([]string{white}) + + // BoldBlack makes terminal text bold and black. + BoldBlack = makeFunction([]string{black, bold}) + // BoldRed makes terminal text bold and red. + BoldRed = makeFunction([]string{red, bold}) + // BoldGreen makes terminal text bold and green. + BoldGreen = makeFunction([]string{green, bold}) + // BoldYellow makes terminal text bold and yellow. + BoldYellow = makeFunction([]string{yellow, bold}) + // BoldBlue makes terminal text bold and blue. + BoldBlue = makeFunction([]string{blue, bold}) + // BoldMagenta makes terminal text bold and magenta. + BoldMagenta = makeFunction([]string{magenta, bold}) + // BoldCyan makes terminal text bold and cyan. + BoldCyan = makeFunction([]string{cyan, bold}) + // BoldWhite makes terminal text bold and white. + BoldWhite = makeFunction([]string{white, bold}) + + // BlinkBlack makes terminal text blink and black. + BlinkBlack = makeFunction([]string{black, blink}) + // BlinkRed makes terminal text blink and red. + BlinkRed = makeFunction([]string{red, blink}) + // BlinkGreen makes terminal text blink and green. + BlinkGreen = makeFunction([]string{green, blink}) + // BlinkYellow makes terminal text blink and yellow. + BlinkYellow = makeFunction([]string{yellow, blink}) + // BlinkBlue makes terminal text blink and blue. + BlinkBlue = makeFunction([]string{blue, blink}) + // BlinkMagenta makes terminal text blink and magenta. + BlinkMagenta = makeFunction([]string{magenta, blink}) + // BlinkCyan makes terminal text blink and cyan. + BlinkCyan = makeFunction([]string{cyan, blink}) + // BlinkWhite makes terminal text blink and white. + BlinkWhite = makeFunction([]string{white, blink}) + + // UnderlineBlack makes terminal text underlined and black. + UnderlineBlack = makeFunction([]string{black, underline}) + // UnderlineRed makes terminal text underlined and red. + UnderlineRed = makeFunction([]string{red, underline}) + // UnderlineGreen makes terminal text underlined and green. + UnderlineGreen = makeFunction([]string{green, underline}) + // UnderlineYellow makes terminal text underlined and yellow. + UnderlineYellow = makeFunction([]string{yellow, underline}) + // UnderlineBlue makes terminal text underlined and blue. + UnderlineBlue = makeFunction([]string{blue, underline}) + // UnderlineMagenta makes terminal text underlined and magenta. + UnderlineMagenta = makeFunction([]string{magenta, underline}) + // UnderlineCyan makes terminal text underlined and cyan. + UnderlineCyan = makeFunction([]string{cyan, underline}) + // UnderlineWhite makes terminal text underlined and white. + UnderlineWhite = makeFunction([]string{white, underline}) + + // InverseBlack makes terminal text inverted and black. + InverseBlack = makeFunction([]string{black, inverse}) + // InverseRed makes terminal text inverted and red. + InverseRed = makeFunction([]string{red, inverse}) + // InverseGreen makes terminal text inverted and green. + InverseGreen = makeFunction([]string{green, inverse}) + // InverseYellow makes terminal text inverted and yellow. + InverseYellow = makeFunction([]string{yellow, inverse}) + // InverseBlue makes terminal text inverted and blue. + InverseBlue = makeFunction([]string{blue, inverse}) + // InverseMagenta makes terminal text inverted and magenta. + InverseMagenta = makeFunction([]string{magenta, inverse}) + // InverseCyan makes terminal text inverted and cyan. + InverseCyan = makeFunction([]string{cyan, inverse}) + // InverseWhite makes terminal text inverted and white. + InverseWhite = makeFunction([]string{white, inverse}) +) diff --git a/vendor/github.com/rifflock/lfshook/LICENSE b/vendor/github.com/alecthomas/units/COPYING similarity index 52% rename from vendor/github.com/rifflock/lfshook/LICENSE rename to vendor/github.com/alecthomas/units/COPYING index eb9d3214..2993ec08 100644 --- a/vendor/github.com/rifflock/lfshook/LICENSE +++ b/vendor/github.com/alecthomas/units/COPYING @@ -1,21 +1,19 @@ -The MIT License (MIT) +Copyright (C) 2014 Alec Thomas -Copyright (c) 2015 Michael Riffle +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/alecthomas/units/README.md b/vendor/github.com/alecthomas/units/README.md new file mode 100644 index 00000000..bee884e3 --- /dev/null +++ b/vendor/github.com/alecthomas/units/README.md @@ -0,0 +1,11 @@ +# Units - Helpful unit multipliers and functions for Go + +The goal of this package is to have functionality similar to the [time](http://golang.org/pkg/time/) package. + +It allows for code like this: + +```go +n, err := ParseBase2Bytes("1KB") +// n == 1024 +n = units.Mebibyte * 512 +``` diff --git a/vendor/github.com/alecthomas/units/bytes.go b/vendor/github.com/alecthomas/units/bytes.go new file mode 100644 index 00000000..eaadeb80 --- /dev/null +++ b/vendor/github.com/alecthomas/units/bytes.go @@ -0,0 +1,83 @@ +package units + +// Base2Bytes is the old non-SI power-of-2 byte scale (1024 bytes in a kilobyte, +// etc.). +type Base2Bytes int64 + +// Base-2 byte units. +const ( + Kibibyte Base2Bytes = 1024 + KiB = Kibibyte + Mebibyte = Kibibyte * 1024 + MiB = Mebibyte + Gibibyte = Mebibyte * 1024 + GiB = Gibibyte + Tebibyte = Gibibyte * 1024 + TiB = Tebibyte + Pebibyte = Tebibyte * 1024 + PiB = Pebibyte + Exbibyte = Pebibyte * 1024 + EiB = Exbibyte +) + +var ( + bytesUnitMap = MakeUnitMap("iB", "B", 1024) + oldBytesUnitMap = MakeUnitMap("B", "B", 1024) +) + +// ParseBase2Bytes supports both iB and B in base-2 multipliers. That is, KB +// and KiB are both 1024. +func ParseBase2Bytes(s string) (Base2Bytes, error) { + n, err := ParseUnit(s, bytesUnitMap) + if err != nil { + n, err = ParseUnit(s, oldBytesUnitMap) + } + return Base2Bytes(n), err +} + +func (b Base2Bytes) String() string { + return ToString(int64(b), 1024, "iB", "B") +} + +var ( + metricBytesUnitMap = MakeUnitMap("B", "B", 1000) +) + +// MetricBytes are SI byte units (1000 bytes in a kilobyte). +type MetricBytes SI + +// SI base-10 byte units. +const ( + Kilobyte MetricBytes = 1000 + KB = Kilobyte + Megabyte = Kilobyte * 1000 + MB = Megabyte + Gigabyte = Megabyte * 1000 + GB = Gigabyte + Terabyte = Gigabyte * 1000 + TB = Terabyte + Petabyte = Terabyte * 1000 + PB = Petabyte + Exabyte = Petabyte * 1000 + EB = Exabyte +) + +// ParseMetricBytes parses base-10 metric byte units. That is, KB is 1000 bytes. +func ParseMetricBytes(s string) (MetricBytes, error) { + n, err := ParseUnit(s, metricBytesUnitMap) + return MetricBytes(n), err +} + +func (m MetricBytes) String() string { + return ToString(int64(m), 1000, "B", "B") +} + +// ParseStrictBytes supports both iB and B suffixes for base 2 and metric, +// respectively. That is, KiB represents 1024 and KB represents 1000. +func ParseStrictBytes(s string) (int64, error) { + n, err := ParseUnit(s, bytesUnitMap) + if err != nil { + n, err = ParseUnit(s, metricBytesUnitMap) + } + return int64(n), err +} diff --git a/vendor/github.com/alecthomas/units/doc.go b/vendor/github.com/alecthomas/units/doc.go new file mode 100644 index 00000000..156ae386 --- /dev/null +++ b/vendor/github.com/alecthomas/units/doc.go @@ -0,0 +1,13 @@ +// Package units provides helpful unit multipliers and functions for Go. +// +// The goal of this package is to have functionality similar to the time [1] package. +// +// +// [1] http://golang.org/pkg/time/ +// +// It allows for code like this: +// +// n, err := ParseBase2Bytes("1KB") +// // n == 1024 +// n = units.Mebibyte * 512 +package units diff --git a/vendor/github.com/alecthomas/units/go.mod b/vendor/github.com/alecthomas/units/go.mod new file mode 100644 index 00000000..f5721732 --- /dev/null +++ b/vendor/github.com/alecthomas/units/go.mod @@ -0,0 +1 @@ +module github.com/alecthomas/units diff --git a/vendor/github.com/alecthomas/units/si.go b/vendor/github.com/alecthomas/units/si.go new file mode 100644 index 00000000..8234a9d5 --- /dev/null +++ b/vendor/github.com/alecthomas/units/si.go @@ -0,0 +1,26 @@ +package units + +// SI units. +type SI int64 + +// SI unit multiples. +const ( + Kilo SI = 1000 + Mega = Kilo * 1000 + Giga = Mega * 1000 + Tera = Giga * 1000 + Peta = Tera * 1000 + Exa = Peta * 1000 +) + +func MakeUnitMap(suffix, shortSuffix string, scale int64) map[string]float64 { + return map[string]float64{ + shortSuffix: 1, + "K" + suffix: float64(scale), + "M" + suffix: float64(scale * scale), + "G" + suffix: float64(scale * scale * scale), + "T" + suffix: float64(scale * scale * scale * scale), + "P" + suffix: float64(scale * scale * scale * scale * scale), + "E" + suffix: float64(scale * scale * scale * scale * scale * scale), + } +} diff --git a/vendor/github.com/alecthomas/units/util.go b/vendor/github.com/alecthomas/units/util.go new file mode 100644 index 00000000..6527e92d --- /dev/null +++ b/vendor/github.com/alecthomas/units/util.go @@ -0,0 +1,138 @@ +package units + +import ( + "errors" + "fmt" + "strings" +) + +var ( + siUnits = []string{"", "K", "M", "G", "T", "P", "E"} +) + +func ToString(n int64, scale int64, suffix, baseSuffix string) string { + mn := len(siUnits) + out := make([]string, mn) + for i, m := range siUnits { + if n%scale != 0 || i == 0 && n == 0 { + s := suffix + if i == 0 { + s = baseSuffix + } + out[mn-1-i] = fmt.Sprintf("%d%s%s", n%scale, m, s) + } + n /= scale + if n == 0 { + break + } + } + return strings.Join(out, "") +} + +// Below code ripped straight from http://golang.org/src/pkg/time/format.go?s=33392:33438#L1123 +var errLeadingInt = errors.New("units: bad [0-9]*") // never printed + +// leadingInt consumes the leading [0-9]* from s. +func leadingInt(s string) (x int64, rem string, err error) { + i := 0 + for ; i < len(s); i++ { + c := s[i] + if c < '0' || c > '9' { + break + } + if x >= (1<<63-10)/10 { + // overflow + return 0, "", errLeadingInt + } + x = x*10 + int64(c) - '0' + } + return x, s[i:], nil +} + +func ParseUnit(s string, unitMap map[string]float64) (int64, error) { + // [-+]?([0-9]*(\.[0-9]*)?[a-z]+)+ + orig := s + f := float64(0) + neg := false + + // Consume [-+]? + if s != "" { + c := s[0] + if c == '-' || c == '+' { + neg = c == '-' + s = s[1:] + } + } + // Special case: if all that is left is "0", this is zero. + if s == "0" { + return 0, nil + } + if s == "" { + return 0, errors.New("units: invalid " + orig) + } + for s != "" { + g := float64(0) // this element of the sequence + + var x int64 + var err error + + // The next character must be [0-9.] + if !(s[0] == '.' || ('0' <= s[0] && s[0] <= '9')) { + return 0, errors.New("units: invalid " + orig) + } + // Consume [0-9]* + pl := len(s) + x, s, err = leadingInt(s) + if err != nil { + return 0, errors.New("units: invalid " + orig) + } + g = float64(x) + pre := pl != len(s) // whether we consumed anything before a period + + // Consume (\.[0-9]*)? + post := false + if s != "" && s[0] == '.' { + s = s[1:] + pl := len(s) + x, s, err = leadingInt(s) + if err != nil { + return 0, errors.New("units: invalid " + orig) + } + scale := 1.0 + for n := pl - len(s); n > 0; n-- { + scale *= 10 + } + g += float64(x) / scale + post = pl != len(s) + } + if !pre && !post { + // no digits (e.g. ".s" or "-.s") + return 0, errors.New("units: invalid " + orig) + } + + // Consume unit. + i := 0 + for ; i < len(s); i++ { + c := s[i] + if c == '.' || ('0' <= c && c <= '9') { + break + } + } + u := s[:i] + s = s[i:] + unit, ok := unitMap[u] + if !ok { + return 0, errors.New("units: unknown unit " + u + " in " + orig) + } + + f += g * unit + } + + if neg { + f = -f + } + if f < float64(-1<<63) || f > float64(1<<63-1) { + return 0, errors.New("units: overflow parsing unit") + } + return int64(f), nil +} diff --git a/vendor/github.com/cloudflare/cfssl/LICENSE b/vendor/github.com/cloudflare/cfssl/LICENSE deleted file mode 100644 index bc5841fa..00000000 --- a/vendor/github.com/cloudflare/cfssl/LICENSE +++ /dev/null @@ -1,24 +0,0 @@ -Copyright (c) 2014 CloudFlare Inc. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -Redistributions of source code must retain the above copyright notice, -this list of conditions and the following disclaimer. - -Redistributions in binary form must reproduce the above copyright notice, -this list of conditions and the following disclaimer in the documentation -and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED -TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/cloudflare/cfssl/log/log.go b/vendor/github.com/cloudflare/cfssl/log/log.go deleted file mode 100644 index 1ec06f10..00000000 --- a/vendor/github.com/cloudflare/cfssl/log/log.go +++ /dev/null @@ -1,98 +0,0 @@ -// Package log implements a wrapper around the Go standard library's -// logging package. Clients should set the current log level; only -// messages below that level will actually be logged. For example, if -// Level is set to LevelWarning, only log messages at the Warning, -// Error, and Critical levels will be logged. -package log - -import ( - "fmt" - golog "log" -) - -// The following constants represent logging levels in increasing levels of seriousness. -const ( - LevelDebug = iota - LevelInfo - LevelWarning - LevelError - LevelCritical -) - -var levelPrefix = [...]string{ - LevelDebug: "[DEBUG] ", - LevelInfo: "[INFO] ", - LevelWarning: "[WARNING] ", - LevelError: "[ERROR] ", - LevelCritical: "[CRITICAL] ", -} - -// Level stores the current logging level. -var Level = LevelDebug - -func outputf(l int, format string, v []interface{}) { - if l >= Level { - golog.Printf(fmt.Sprint(levelPrefix[l], format), v...) - } -} - -func output(l int, v []interface{}) { - if l >= Level { - golog.Print(levelPrefix[l], fmt.Sprint(v...)) - } -} - -// Criticalf logs a formatted message at the "critical" level. The -// arguments are handled in the same manner as fmt.Printf. -func Criticalf(format string, v ...interface{}) { - outputf(LevelCritical, format, v) -} - -// Critical logs its arguments at the "critical" level. -func Critical(v ...interface{}) { - output(LevelCritical, v) -} - -// Errorf logs a formatted message at the "error" level. The arguments -// are handled in the same manner as fmt.Printf. -func Errorf(format string, v ...interface{}) { - outputf(LevelError, format, v) -} - -// Error logs its arguments at the "error" level. -func Error(v ...interface{}) { - output(LevelError, v) -} - -// Warningf logs a formatted message at the "warning" level. The -// arguments are handled in the same manner as fmt.Printf. -func Warningf(format string, v ...interface{}) { - outputf(LevelWarning, format, v) -} - -// Warning logs its arguments at the "warning" level. -func Warning(v ...interface{}) { - output(LevelWarning, v) -} - -// Infof logs a formatted message at the "info" level. The arguments -// are handled in the same manner as fmt.Printf. -func Infof(format string, v ...interface{}) { - outputf(LevelInfo, format, v) -} - -// Info logs its arguments at the "info" level. -func Info(v ...interface{}) { - output(LevelInfo, v) -} - -// Debugf logs a formatted message at the "debug" level. The arguments -// are handled in the same manner as fmt.Printf. -func Debugf(format string, v ...interface{}) { - outputf(LevelDebug, format, v) -} - -// Debug logs its arguments at the "debug" level. -func Debug(v ...interface{}) { - output(LevelDebug, v) -} diff --git a/vendor/github.com/cloudflare/cfssl/revoke/revoke.go b/vendor/github.com/cloudflare/cfssl/revoke/revoke.go deleted file mode 100644 index 1cb3a19c..00000000 --- a/vendor/github.com/cloudflare/cfssl/revoke/revoke.go +++ /dev/null @@ -1,146 +0,0 @@ -// Package revoke provides functionality for checking the validity of -// a cert. Specifically, the temporal validity of the certificate is -// checked first, then any CRL in the cert is checked. OCSP is not -// supported at this time. -package revoke - -import ( - "crypto/x509" - "crypto/x509/pkix" - "errors" - "io/ioutil" - "net/http" - neturl "net/url" - "time" - - "github.com/cloudflare/cfssl/log" -) - -// HardFail determines whether the failure to check the revocation -// status of a certificate (i.e. due to network failure) causes -// verification to fail (a hard failure). -var HardFail = false - -// TODO (kyle): figure out a good mechanism for OCSP; this requires -// presenting both the certificate and the issuer, and we don't have a -// good way at this time of getting the issuer. - -// CRLSet associates a PKIX certificate list with the URL the CRL is -// fetched from. -var CRLSet = map[string]*pkix.CertificateList{} - -// We can't handle LDAP certificates, so this checks to see if the -// URL string points to an LDAP resource so that we can ignore it. -func ldapURL(url string) bool { - u, err := neturl.Parse(url) - if err != nil { - log.Warningf("invalid url %s: %v", url, err) - return false - } - if u.Scheme == "ldap" { - return true - } - return false -} - -// revCheck should check the certificate for any revocations. It -// returns a pair of booleans: the first indicates whether the certificate -// is revoked, the second indicates whether the revocations were -// successfully checked.. This leads to the following combinations: -// -// false, false: an error was encountered while checking revocations. -// -// false, true: the certificate was checked successfully and -// it is not revoked. -// -// true, true: the certificate was checked successfully and -// it is revoked. -func revCheck(cert *x509.Certificate) (revoked, ok bool) { - for _, url := range cert.CRLDistributionPoints { - if ldapURL(url) { - log.Infof("skipping LDAP CRL: %s", url) - continue - } - - if revoked, ok := certIsRevokedCRL(cert, url); !ok { - log.Warning("error checking revocation via CRL") - if HardFail { - return true, false - } - return false, false - } else if revoked { - log.Info("certificate is revoked via CRL") - return true, true - } - } - - return false, true -} - -// fetchCRL fetches and parses a CRL. -func fetchCRL(url string) (*pkix.CertificateList, error) { - resp, err := http.Get(url) - if err != nil { - return nil, err - } else if resp.StatusCode >= 300 { - return nil, errors.New("failed to retrieve CRL") - } - - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return nil, err - } - resp.Body.Close() - - return x509.ParseCRL(body) -} - -// check a cert against a specific CRL. Returns the same bool pair -// as revCheck. -func certIsRevokedCRL(cert *x509.Certificate, url string) (revoked, ok bool) { - crl, ok := CRLSet[url] - if ok && crl == nil { - ok = false - delete(CRLSet, url) - } - - var shouldFetchCRL = true - if ok { - if !crl.HasExpired(time.Now()) { - shouldFetchCRL = false - } - } - - if shouldFetchCRL { - var err error - crl, err = fetchCRL(url) - if err != nil { - log.Warningf("failed to fetch CRL: %v", err) - return false, false - } - CRLSet[url] = crl - } - - for _, revoked := range crl.TBSCertList.RevokedCertificates { - if cert.SerialNumber.Cmp(revoked.SerialNumber) == 0 { - log.Info("Serial number match: intermediate is revoked.") - return true, true - } - } - - return false, true -} - -// VerifyCertificate ensures that the certificate passed in hasn't -// expired and checks the CRL for the server. -func VerifyCertificate(cert *x509.Certificate) (revoked, ok bool) { - if !time.Now().Before(cert.NotAfter) { - log.Infof("Certificate expired %s\n", cert.NotAfter) - return true, true - } else if !time.Now().After(cert.NotBefore) { - log.Infof("Certificate isn't valid until %s\n", cert.NotBefore) - return true, true - } - - return revCheck(cert) -} diff --git a/vendor/github.com/konsorten/go-windows-terminal-sequences/LICENSE b/vendor/github.com/konsorten/go-windows-terminal-sequences/LICENSE deleted file mode 100644 index 14127cd8..00000000 --- a/vendor/github.com/konsorten/go-windows-terminal-sequences/LICENSE +++ /dev/null @@ -1,9 +0,0 @@ -(The MIT License) - -Copyright (c) 2017 marvin + konsorten GmbH (open-source@konsorten.de) - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/konsorten/go-windows-terminal-sequences/README.md b/vendor/github.com/konsorten/go-windows-terminal-sequences/README.md deleted file mode 100644 index 195333e5..00000000 --- a/vendor/github.com/konsorten/go-windows-terminal-sequences/README.md +++ /dev/null @@ -1,41 +0,0 @@ -# Windows Terminal Sequences - -This library allow for enabling Windows terminal color support for Go. - -See [Console Virtual Terminal Sequences](https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences) for details. - -## Usage - -```go -import ( - "syscall" - - sequences "github.com/konsorten/go-windows-terminal-sequences" -) - -func main() { - sequences.EnableVirtualTerminalProcessing(syscall.Stdout, true) -} - -``` - -## Authors - -The tool is sponsored by the [marvin + konsorten GmbH](http://www.konsorten.de). - -We thank all the authors who provided code to this library: - -* Felix Kollmann -* Nicolas Perraut - -## License - -(The MIT License) - -Copyright (c) 2018 marvin + konsorten GmbH (open-source@konsorten.de) - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/konsorten/go-windows-terminal-sequences/go.mod b/vendor/github.com/konsorten/go-windows-terminal-sequences/go.mod deleted file mode 100644 index 716c6131..00000000 --- a/vendor/github.com/konsorten/go-windows-terminal-sequences/go.mod +++ /dev/null @@ -1 +0,0 @@ -module github.com/konsorten/go-windows-terminal-sequences diff --git a/vendor/github.com/konsorten/go-windows-terminal-sequences/sequences.go b/vendor/github.com/konsorten/go-windows-terminal-sequences/sequences.go deleted file mode 100644 index ef18d8f9..00000000 --- a/vendor/github.com/konsorten/go-windows-terminal-sequences/sequences.go +++ /dev/null @@ -1,36 +0,0 @@ -// +build windows - -package sequences - -import ( - "syscall" - "unsafe" -) - -var ( - kernel32Dll *syscall.LazyDLL = syscall.NewLazyDLL("Kernel32.dll") - setConsoleMode *syscall.LazyProc = kernel32Dll.NewProc("SetConsoleMode") -) - -func EnableVirtualTerminalProcessing(stream syscall.Handle, enable bool) error { - const ENABLE_VIRTUAL_TERMINAL_PROCESSING uint32 = 0x4 - - var mode uint32 - err := syscall.GetConsoleMode(syscall.Stdout, &mode) - if err != nil { - return err - } - - if enable { - mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING - } else { - mode &^= ENABLE_VIRTUAL_TERMINAL_PROCESSING - } - - ret, _, err := setConsoleMode.Call(uintptr(unsafe.Pointer(stream)), uintptr(mode)) - if ret == 0 { - return err - } - - return nil -} diff --git a/vendor/github.com/konsorten/go-windows-terminal-sequences/sequences_dummy.go b/vendor/github.com/konsorten/go-windows-terminal-sequences/sequences_dummy.go deleted file mode 100644 index df61a6f2..00000000 --- a/vendor/github.com/konsorten/go-windows-terminal-sequences/sequences_dummy.go +++ /dev/null @@ -1,11 +0,0 @@ -// +build linux darwin - -package sequences - -import ( - "fmt" -) - -func EnableVirtualTerminalProcessing(stream uintptr, enable bool) error { - return fmt.Errorf("windows only package") -} diff --git a/vendor/github.com/mattn/go-colorable/.travis.yml b/vendor/github.com/mattn/go-colorable/.travis.yml deleted file mode 100644 index 98db8f06..00000000 --- a/vendor/github.com/mattn/go-colorable/.travis.yml +++ /dev/null @@ -1,9 +0,0 @@ -language: go -go: - - tip - -before_install: - - go get github.com/mattn/goveralls - - go get golang.org/x/tools/cmd/cover -script: - - $HOME/gopath/bin/goveralls -repotoken xnXqRGwgW3SXIguzxf90ZSK1GPYZPaGrw diff --git a/vendor/github.com/mattn/go-colorable/README.md b/vendor/github.com/mattn/go-colorable/README.md deleted file mode 100644 index 56729a92..00000000 --- a/vendor/github.com/mattn/go-colorable/README.md +++ /dev/null @@ -1,48 +0,0 @@ -# go-colorable - -[![Godoc Reference](https://godoc.org/github.com/mattn/go-colorable?status.svg)](http://godoc.org/github.com/mattn/go-colorable) -[![Build Status](https://travis-ci.org/mattn/go-colorable.svg?branch=master)](https://travis-ci.org/mattn/go-colorable) -[![Coverage Status](https://coveralls.io/repos/github/mattn/go-colorable/badge.svg?branch=master)](https://coveralls.io/github/mattn/go-colorable?branch=master) -[![Go Report Card](https://goreportcard.com/badge/mattn/go-colorable)](https://goreportcard.com/report/mattn/go-colorable) - -Colorable writer for windows. - -For example, most of logger packages doesn't show colors on windows. (I know we can do it with ansicon. But I don't want.) -This package is possible to handle escape sequence for ansi color on windows. - -## Too Bad! - -![](https://raw.githubusercontent.com/mattn/go-colorable/gh-pages/bad.png) - - -## So Good! - -![](https://raw.githubusercontent.com/mattn/go-colorable/gh-pages/good.png) - -## Usage - -```go -logrus.SetFormatter(&logrus.TextFormatter{ForceColors: true}) -logrus.SetOutput(colorable.NewColorableStdout()) - -logrus.Info("succeeded") -logrus.Warn("not correct") -logrus.Error("something error") -logrus.Fatal("panic") -``` - -You can compile above code on non-windows OSs. - -## Installation - -``` -$ go get github.com/mattn/go-colorable -``` - -# License - -MIT - -# Author - -Yasuhiro Matsumoto (a.k.a mattn) diff --git a/vendor/github.com/mattn/go-colorable/colorable_appengine.go b/vendor/github.com/mattn/go-colorable/colorable_appengine.go deleted file mode 100644 index 0b0aef83..00000000 --- a/vendor/github.com/mattn/go-colorable/colorable_appengine.go +++ /dev/null @@ -1,29 +0,0 @@ -// +build appengine - -package colorable - -import ( - "io" - "os" - - _ "github.com/mattn/go-isatty" -) - -// NewColorable returns new instance of Writer which handles escape sequence. -func NewColorable(file *os.File) io.Writer { - if file == nil { - panic("nil passed instead of *os.File to NewColorable()") - } - - return file -} - -// NewColorableStdout returns new instance of Writer which handles escape sequence for stdout. -func NewColorableStdout() io.Writer { - return os.Stdout -} - -// NewColorableStderr returns new instance of Writer which handles escape sequence for stderr. -func NewColorableStderr() io.Writer { - return os.Stderr -} diff --git a/vendor/github.com/mattn/go-colorable/colorable_others.go b/vendor/github.com/mattn/go-colorable/colorable_others.go deleted file mode 100644 index 3fb771dc..00000000 --- a/vendor/github.com/mattn/go-colorable/colorable_others.go +++ /dev/null @@ -1,30 +0,0 @@ -// +build !windows -// +build !appengine - -package colorable - -import ( - "io" - "os" - - _ "github.com/mattn/go-isatty" -) - -// NewColorable returns new instance of Writer which handles escape sequence. -func NewColorable(file *os.File) io.Writer { - if file == nil { - panic("nil passed instead of *os.File to NewColorable()") - } - - return file -} - -// NewColorableStdout returns new instance of Writer which handles escape sequence for stdout. -func NewColorableStdout() io.Writer { - return os.Stdout -} - -// NewColorableStderr returns new instance of Writer which handles escape sequence for stderr. -func NewColorableStderr() io.Writer { - return os.Stderr -} diff --git a/vendor/github.com/mattn/go-colorable/colorable_windows.go b/vendor/github.com/mattn/go-colorable/colorable_windows.go deleted file mode 100644 index 1bd628f2..00000000 --- a/vendor/github.com/mattn/go-colorable/colorable_windows.go +++ /dev/null @@ -1,1005 +0,0 @@ -// +build windows -// +build !appengine - -package colorable - -import ( - "bytes" - "io" - "math" - "os" - "strconv" - "strings" - "syscall" - "unsafe" - - "github.com/mattn/go-isatty" -) - -const ( - foregroundBlue = 0x1 - foregroundGreen = 0x2 - foregroundRed = 0x4 - foregroundIntensity = 0x8 - foregroundMask = (foregroundRed | foregroundBlue | foregroundGreen | foregroundIntensity) - backgroundBlue = 0x10 - backgroundGreen = 0x20 - backgroundRed = 0x40 - backgroundIntensity = 0x80 - backgroundMask = (backgroundRed | backgroundBlue | backgroundGreen | backgroundIntensity) -) - -const ( - genericRead = 0x80000000 - genericWrite = 0x40000000 -) - -const ( - consoleTextmodeBuffer = 0x1 -) - -type wchar uint16 -type short int16 -type dword uint32 -type word uint16 - -type coord struct { - x short - y short -} - -type smallRect struct { - left short - top short - right short - bottom short -} - -type consoleScreenBufferInfo struct { - size coord - cursorPosition coord - attributes word - window smallRect - maximumWindowSize coord -} - -type consoleCursorInfo struct { - size dword - visible int32 -} - -var ( - kernel32 = syscall.NewLazyDLL("kernel32.dll") - procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo") - procSetConsoleTextAttribute = kernel32.NewProc("SetConsoleTextAttribute") - procSetConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition") - procFillConsoleOutputCharacter = kernel32.NewProc("FillConsoleOutputCharacterW") - procFillConsoleOutputAttribute = kernel32.NewProc("FillConsoleOutputAttribute") - procGetConsoleCursorInfo = kernel32.NewProc("GetConsoleCursorInfo") - procSetConsoleCursorInfo = kernel32.NewProc("SetConsoleCursorInfo") - procSetConsoleTitle = kernel32.NewProc("SetConsoleTitleW") - procCreateConsoleScreenBuffer = kernel32.NewProc("CreateConsoleScreenBuffer") -) - -// Writer provides colorable Writer to the console -type Writer struct { - out io.Writer - handle syscall.Handle - althandle syscall.Handle - oldattr word - oldpos coord - rest bytes.Buffer -} - -// NewColorable returns new instance of Writer which handles escape sequence from File. -func NewColorable(file *os.File) io.Writer { - if file == nil { - panic("nil passed instead of *os.File to NewColorable()") - } - - if isatty.IsTerminal(file.Fd()) { - var csbi consoleScreenBufferInfo - handle := syscall.Handle(file.Fd()) - procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) - return &Writer{out: file, handle: handle, oldattr: csbi.attributes, oldpos: coord{0, 0}} - } - return file -} - -// NewColorableStdout returns new instance of Writer which handles escape sequence for stdout. -func NewColorableStdout() io.Writer { - return NewColorable(os.Stdout) -} - -// NewColorableStderr returns new instance of Writer which handles escape sequence for stderr. -func NewColorableStderr() io.Writer { - return NewColorable(os.Stderr) -} - -var color256 = map[int]int{ - 0: 0x000000, - 1: 0x800000, - 2: 0x008000, - 3: 0x808000, - 4: 0x000080, - 5: 0x800080, - 6: 0x008080, - 7: 0xc0c0c0, - 8: 0x808080, - 9: 0xff0000, - 10: 0x00ff00, - 11: 0xffff00, - 12: 0x0000ff, - 13: 0xff00ff, - 14: 0x00ffff, - 15: 0xffffff, - 16: 0x000000, - 17: 0x00005f, - 18: 0x000087, - 19: 0x0000af, - 20: 0x0000d7, - 21: 0x0000ff, - 22: 0x005f00, - 23: 0x005f5f, - 24: 0x005f87, - 25: 0x005faf, - 26: 0x005fd7, - 27: 0x005fff, - 28: 0x008700, - 29: 0x00875f, - 30: 0x008787, - 31: 0x0087af, - 32: 0x0087d7, - 33: 0x0087ff, - 34: 0x00af00, - 35: 0x00af5f, - 36: 0x00af87, - 37: 0x00afaf, - 38: 0x00afd7, - 39: 0x00afff, - 40: 0x00d700, - 41: 0x00d75f, - 42: 0x00d787, - 43: 0x00d7af, - 44: 0x00d7d7, - 45: 0x00d7ff, - 46: 0x00ff00, - 47: 0x00ff5f, - 48: 0x00ff87, - 49: 0x00ffaf, - 50: 0x00ffd7, - 51: 0x00ffff, - 52: 0x5f0000, - 53: 0x5f005f, - 54: 0x5f0087, - 55: 0x5f00af, - 56: 0x5f00d7, - 57: 0x5f00ff, - 58: 0x5f5f00, - 59: 0x5f5f5f, - 60: 0x5f5f87, - 61: 0x5f5faf, - 62: 0x5f5fd7, - 63: 0x5f5fff, - 64: 0x5f8700, - 65: 0x5f875f, - 66: 0x5f8787, - 67: 0x5f87af, - 68: 0x5f87d7, - 69: 0x5f87ff, - 70: 0x5faf00, - 71: 0x5faf5f, - 72: 0x5faf87, - 73: 0x5fafaf, - 74: 0x5fafd7, - 75: 0x5fafff, - 76: 0x5fd700, - 77: 0x5fd75f, - 78: 0x5fd787, - 79: 0x5fd7af, - 80: 0x5fd7d7, - 81: 0x5fd7ff, - 82: 0x5fff00, - 83: 0x5fff5f, - 84: 0x5fff87, - 85: 0x5fffaf, - 86: 0x5fffd7, - 87: 0x5fffff, - 88: 0x870000, - 89: 0x87005f, - 90: 0x870087, - 91: 0x8700af, - 92: 0x8700d7, - 93: 0x8700ff, - 94: 0x875f00, - 95: 0x875f5f, - 96: 0x875f87, - 97: 0x875faf, - 98: 0x875fd7, - 99: 0x875fff, - 100: 0x878700, - 101: 0x87875f, - 102: 0x878787, - 103: 0x8787af, - 104: 0x8787d7, - 105: 0x8787ff, - 106: 0x87af00, - 107: 0x87af5f, - 108: 0x87af87, - 109: 0x87afaf, - 110: 0x87afd7, - 111: 0x87afff, - 112: 0x87d700, - 113: 0x87d75f, - 114: 0x87d787, - 115: 0x87d7af, - 116: 0x87d7d7, - 117: 0x87d7ff, - 118: 0x87ff00, - 119: 0x87ff5f, - 120: 0x87ff87, - 121: 0x87ffaf, - 122: 0x87ffd7, - 123: 0x87ffff, - 124: 0xaf0000, - 125: 0xaf005f, - 126: 0xaf0087, - 127: 0xaf00af, - 128: 0xaf00d7, - 129: 0xaf00ff, - 130: 0xaf5f00, - 131: 0xaf5f5f, - 132: 0xaf5f87, - 133: 0xaf5faf, - 134: 0xaf5fd7, - 135: 0xaf5fff, - 136: 0xaf8700, - 137: 0xaf875f, - 138: 0xaf8787, - 139: 0xaf87af, - 140: 0xaf87d7, - 141: 0xaf87ff, - 142: 0xafaf00, - 143: 0xafaf5f, - 144: 0xafaf87, - 145: 0xafafaf, - 146: 0xafafd7, - 147: 0xafafff, - 148: 0xafd700, - 149: 0xafd75f, - 150: 0xafd787, - 151: 0xafd7af, - 152: 0xafd7d7, - 153: 0xafd7ff, - 154: 0xafff00, - 155: 0xafff5f, - 156: 0xafff87, - 157: 0xafffaf, - 158: 0xafffd7, - 159: 0xafffff, - 160: 0xd70000, - 161: 0xd7005f, - 162: 0xd70087, - 163: 0xd700af, - 164: 0xd700d7, - 165: 0xd700ff, - 166: 0xd75f00, - 167: 0xd75f5f, - 168: 0xd75f87, - 169: 0xd75faf, - 170: 0xd75fd7, - 171: 0xd75fff, - 172: 0xd78700, - 173: 0xd7875f, - 174: 0xd78787, - 175: 0xd787af, - 176: 0xd787d7, - 177: 0xd787ff, - 178: 0xd7af00, - 179: 0xd7af5f, - 180: 0xd7af87, - 181: 0xd7afaf, - 182: 0xd7afd7, - 183: 0xd7afff, - 184: 0xd7d700, - 185: 0xd7d75f, - 186: 0xd7d787, - 187: 0xd7d7af, - 188: 0xd7d7d7, - 189: 0xd7d7ff, - 190: 0xd7ff00, - 191: 0xd7ff5f, - 192: 0xd7ff87, - 193: 0xd7ffaf, - 194: 0xd7ffd7, - 195: 0xd7ffff, - 196: 0xff0000, - 197: 0xff005f, - 198: 0xff0087, - 199: 0xff00af, - 200: 0xff00d7, - 201: 0xff00ff, - 202: 0xff5f00, - 203: 0xff5f5f, - 204: 0xff5f87, - 205: 0xff5faf, - 206: 0xff5fd7, - 207: 0xff5fff, - 208: 0xff8700, - 209: 0xff875f, - 210: 0xff8787, - 211: 0xff87af, - 212: 0xff87d7, - 213: 0xff87ff, - 214: 0xffaf00, - 215: 0xffaf5f, - 216: 0xffaf87, - 217: 0xffafaf, - 218: 0xffafd7, - 219: 0xffafff, - 220: 0xffd700, - 221: 0xffd75f, - 222: 0xffd787, - 223: 0xffd7af, - 224: 0xffd7d7, - 225: 0xffd7ff, - 226: 0xffff00, - 227: 0xffff5f, - 228: 0xffff87, - 229: 0xffffaf, - 230: 0xffffd7, - 231: 0xffffff, - 232: 0x080808, - 233: 0x121212, - 234: 0x1c1c1c, - 235: 0x262626, - 236: 0x303030, - 237: 0x3a3a3a, - 238: 0x444444, - 239: 0x4e4e4e, - 240: 0x585858, - 241: 0x626262, - 242: 0x6c6c6c, - 243: 0x767676, - 244: 0x808080, - 245: 0x8a8a8a, - 246: 0x949494, - 247: 0x9e9e9e, - 248: 0xa8a8a8, - 249: 0xb2b2b2, - 250: 0xbcbcbc, - 251: 0xc6c6c6, - 252: 0xd0d0d0, - 253: 0xdadada, - 254: 0xe4e4e4, - 255: 0xeeeeee, -} - -// `\033]0;TITLESTR\007` -func doTitleSequence(er *bytes.Reader) error { - var c byte - var err error - - c, err = er.ReadByte() - if err != nil { - return err - } - if c != '0' && c != '2' { - return nil - } - c, err = er.ReadByte() - if err != nil { - return err - } - if c != ';' { - return nil - } - title := make([]byte, 0, 80) - for { - c, err = er.ReadByte() - if err != nil { - return err - } - if c == 0x07 || c == '\n' { - break - } - title = append(title, c) - } - if len(title) > 0 { - title8, err := syscall.UTF16PtrFromString(string(title)) - if err == nil { - procSetConsoleTitle.Call(uintptr(unsafe.Pointer(title8))) - } - } - return nil -} - -// returns Atoi(s) unless s == "" in which case it returns def -func atoiWithDefault(s string, def int) (int, error) { - if s == "" { - return def, nil - } - return strconv.Atoi(s) -} - -// Write writes data on console -func (w *Writer) Write(data []byte) (n int, err error) { - var csbi consoleScreenBufferInfo - procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) - - handle := w.handle - - var er *bytes.Reader - if w.rest.Len() > 0 { - var rest bytes.Buffer - w.rest.WriteTo(&rest) - w.rest.Reset() - rest.Write(data) - er = bytes.NewReader(rest.Bytes()) - } else { - er = bytes.NewReader(data) - } - var bw [1]byte -loop: - for { - c1, err := er.ReadByte() - if err != nil { - break loop - } - if c1 != 0x1b { - bw[0] = c1 - w.out.Write(bw[:]) - continue - } - c2, err := er.ReadByte() - if err != nil { - break loop - } - - switch c2 { - case '>': - continue - case ']': - w.rest.WriteByte(c1) - w.rest.WriteByte(c2) - er.WriteTo(&w.rest) - if bytes.IndexByte(w.rest.Bytes(), 0x07) == -1 { - break loop - } - er = bytes.NewReader(w.rest.Bytes()[2:]) - err := doTitleSequence(er) - if err != nil { - break loop - } - w.rest.Reset() - continue - // https://github.com/mattn/go-colorable/issues/27 - case '7': - procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) - w.oldpos = csbi.cursorPosition - continue - case '8': - procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&w.oldpos))) - continue - case 0x5b: - // execute part after switch - default: - continue - } - - w.rest.WriteByte(c1) - w.rest.WriteByte(c2) - er.WriteTo(&w.rest) - - var buf bytes.Buffer - var m byte - for i, c := range w.rest.Bytes()[2:] { - if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '@' { - m = c - er = bytes.NewReader(w.rest.Bytes()[2+i+1:]) - w.rest.Reset() - break - } - buf.Write([]byte(string(c))) - } - if m == 0 { - break loop - } - - switch m { - case 'A': - n, err = atoiWithDefault(buf.String(), 1) - if err != nil { - continue - } - procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) - csbi.cursorPosition.y -= short(n) - procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) - case 'B': - n, err = atoiWithDefault(buf.String(), 1) - if err != nil { - continue - } - procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) - csbi.cursorPosition.y += short(n) - procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) - case 'C': - n, err = atoiWithDefault(buf.String(), 1) - if err != nil { - continue - } - procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) - csbi.cursorPosition.x += short(n) - procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) - case 'D': - n, err = atoiWithDefault(buf.String(), 1) - if err != nil { - continue - } - procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) - csbi.cursorPosition.x -= short(n) - if csbi.cursorPosition.x < 0 { - csbi.cursorPosition.x = 0 - } - procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) - case 'E': - n, err = strconv.Atoi(buf.String()) - if err != nil { - continue - } - procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) - csbi.cursorPosition.x = 0 - csbi.cursorPosition.y += short(n) - procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) - case 'F': - n, err = strconv.Atoi(buf.String()) - if err != nil { - continue - } - procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) - csbi.cursorPosition.x = 0 - csbi.cursorPosition.y -= short(n) - procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) - case 'G': - n, err = strconv.Atoi(buf.String()) - if err != nil { - continue - } - if n < 1 { - n = 1 - } - procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) - csbi.cursorPosition.x = short(n - 1) - procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) - case 'H', 'f': - procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) - if buf.Len() > 0 { - token := strings.Split(buf.String(), ";") - switch len(token) { - case 1: - n1, err := strconv.Atoi(token[0]) - if err != nil { - continue - } - csbi.cursorPosition.y = short(n1 - 1) - case 2: - n1, err := strconv.Atoi(token[0]) - if err != nil { - continue - } - n2, err := strconv.Atoi(token[1]) - if err != nil { - continue - } - csbi.cursorPosition.x = short(n2 - 1) - csbi.cursorPosition.y = short(n1 - 1) - } - } else { - csbi.cursorPosition.y = 0 - } - procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) - case 'J': - n := 0 - if buf.Len() > 0 { - n, err = strconv.Atoi(buf.String()) - if err != nil { - continue - } - } - var count, written dword - var cursor coord - procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) - switch n { - case 0: - cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y} - count = dword(csbi.size.x) - dword(csbi.cursorPosition.x) + dword(csbi.size.y-csbi.cursorPosition.y)*dword(csbi.size.x) - case 1: - cursor = coord{x: csbi.window.left, y: csbi.window.top} - count = dword(csbi.size.x) - dword(csbi.cursorPosition.x) + dword(csbi.window.top-csbi.cursorPosition.y)*dword(csbi.size.x) - case 2: - cursor = coord{x: csbi.window.left, y: csbi.window.top} - count = dword(csbi.size.x) - dword(csbi.cursorPosition.x) + dword(csbi.size.y-csbi.cursorPosition.y)*dword(csbi.size.x) - } - procFillConsoleOutputCharacter.Call(uintptr(handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) - procFillConsoleOutputAttribute.Call(uintptr(handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) - case 'K': - n := 0 - if buf.Len() > 0 { - n, err = strconv.Atoi(buf.String()) - if err != nil { - continue - } - } - procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) - var cursor coord - var count, written dword - switch n { - case 0: - cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y} - count = dword(csbi.size.x - csbi.cursorPosition.x) - case 1: - cursor = coord{x: csbi.window.left, y: csbi.cursorPosition.y} - count = dword(csbi.size.x - csbi.cursorPosition.x) - case 2: - cursor = coord{x: csbi.window.left, y: csbi.cursorPosition.y} - count = dword(csbi.size.x) - } - procFillConsoleOutputCharacter.Call(uintptr(handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) - procFillConsoleOutputAttribute.Call(uintptr(handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) - case 'X': - n := 0 - if buf.Len() > 0 { - n, err = strconv.Atoi(buf.String()) - if err != nil { - continue - } - } - procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) - var cursor coord - var written dword - cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y} - procFillConsoleOutputCharacter.Call(uintptr(handle), uintptr(' '), uintptr(n), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) - procFillConsoleOutputAttribute.Call(uintptr(handle), uintptr(csbi.attributes), uintptr(n), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) - case 'm': - procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) - attr := csbi.attributes - cs := buf.String() - if cs == "" { - procSetConsoleTextAttribute.Call(uintptr(handle), uintptr(w.oldattr)) - continue - } - token := strings.Split(cs, ";") - for i := 0; i < len(token); i++ { - ns := token[i] - if n, err = strconv.Atoi(ns); err == nil { - switch { - case n == 0 || n == 100: - attr = w.oldattr - case 1 <= n && n <= 5: - attr |= foregroundIntensity - case n == 7: - attr = ((attr & foregroundMask) << 4) | ((attr & backgroundMask) >> 4) - case n == 22 || n == 25: - attr |= foregroundIntensity - case n == 27: - attr = ((attr & foregroundMask) << 4) | ((attr & backgroundMask) >> 4) - case 30 <= n && n <= 37: - attr &= backgroundMask - if (n-30)&1 != 0 { - attr |= foregroundRed - } - if (n-30)&2 != 0 { - attr |= foregroundGreen - } - if (n-30)&4 != 0 { - attr |= foregroundBlue - } - case n == 38: // set foreground color. - if i < len(token)-2 && (token[i+1] == "5" || token[i+1] == "05") { - if n256, err := strconv.Atoi(token[i+2]); err == nil { - if n256foreAttr == nil { - n256setup() - } - attr &= backgroundMask - attr |= n256foreAttr[n256] - i += 2 - } - } else if len(token) == 5 && token[i+1] == "2" { - var r, g, b int - r, _ = strconv.Atoi(token[i+2]) - g, _ = strconv.Atoi(token[i+3]) - b, _ = strconv.Atoi(token[i+4]) - i += 4 - if r > 127 { - attr |= foregroundRed - } - if g > 127 { - attr |= foregroundGreen - } - if b > 127 { - attr |= foregroundBlue - } - } else { - attr = attr & (w.oldattr & backgroundMask) - } - case n == 39: // reset foreground color. - attr &= backgroundMask - attr |= w.oldattr & foregroundMask - case 40 <= n && n <= 47: - attr &= foregroundMask - if (n-40)&1 != 0 { - attr |= backgroundRed - } - if (n-40)&2 != 0 { - attr |= backgroundGreen - } - if (n-40)&4 != 0 { - attr |= backgroundBlue - } - case n == 48: // set background color. - if i < len(token)-2 && token[i+1] == "5" { - if n256, err := strconv.Atoi(token[i+2]); err == nil { - if n256backAttr == nil { - n256setup() - } - attr &= foregroundMask - attr |= n256backAttr[n256] - i += 2 - } - } else if len(token) == 5 && token[i+1] == "2" { - var r, g, b int - r, _ = strconv.Atoi(token[i+2]) - g, _ = strconv.Atoi(token[i+3]) - b, _ = strconv.Atoi(token[i+4]) - i += 4 - if r > 127 { - attr |= backgroundRed - } - if g > 127 { - attr |= backgroundGreen - } - if b > 127 { - attr |= backgroundBlue - } - } else { - attr = attr & (w.oldattr & foregroundMask) - } - case n == 49: // reset foreground color. - attr &= foregroundMask - attr |= w.oldattr & backgroundMask - case 90 <= n && n <= 97: - attr = (attr & backgroundMask) - attr |= foregroundIntensity - if (n-90)&1 != 0 { - attr |= foregroundRed - } - if (n-90)&2 != 0 { - attr |= foregroundGreen - } - if (n-90)&4 != 0 { - attr |= foregroundBlue - } - case 100 <= n && n <= 107: - attr = (attr & foregroundMask) - attr |= backgroundIntensity - if (n-100)&1 != 0 { - attr |= backgroundRed - } - if (n-100)&2 != 0 { - attr |= backgroundGreen - } - if (n-100)&4 != 0 { - attr |= backgroundBlue - } - } - procSetConsoleTextAttribute.Call(uintptr(handle), uintptr(attr)) - } - } - case 'h': - var ci consoleCursorInfo - cs := buf.String() - if cs == "5>" { - procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) - ci.visible = 0 - procSetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) - } else if cs == "?25" { - procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) - ci.visible = 1 - procSetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) - } else if cs == "?1049" { - if w.althandle == 0 { - h, _, _ := procCreateConsoleScreenBuffer.Call(uintptr(genericRead|genericWrite), 0, 0, uintptr(consoleTextmodeBuffer), 0, 0) - w.althandle = syscall.Handle(h) - if w.althandle != 0 { - handle = w.althandle - } - } - } - case 'l': - var ci consoleCursorInfo - cs := buf.String() - if cs == "5>" { - procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) - ci.visible = 1 - procSetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) - } else if cs == "?25" { - procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) - ci.visible = 0 - procSetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) - } else if cs == "?1049" { - if w.althandle != 0 { - syscall.CloseHandle(w.althandle) - w.althandle = 0 - handle = w.handle - } - } - case 's': - procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) - w.oldpos = csbi.cursorPosition - case 'u': - procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&w.oldpos))) - } - } - - return len(data), nil -} - -type consoleColor struct { - rgb int - red bool - green bool - blue bool - intensity bool -} - -func (c consoleColor) foregroundAttr() (attr word) { - if c.red { - attr |= foregroundRed - } - if c.green { - attr |= foregroundGreen - } - if c.blue { - attr |= foregroundBlue - } - if c.intensity { - attr |= foregroundIntensity - } - return -} - -func (c consoleColor) backgroundAttr() (attr word) { - if c.red { - attr |= backgroundRed - } - if c.green { - attr |= backgroundGreen - } - if c.blue { - attr |= backgroundBlue - } - if c.intensity { - attr |= backgroundIntensity - } - return -} - -var color16 = []consoleColor{ - {0x000000, false, false, false, false}, - {0x000080, false, false, true, false}, - {0x008000, false, true, false, false}, - {0x008080, false, true, true, false}, - {0x800000, true, false, false, false}, - {0x800080, true, false, true, false}, - {0x808000, true, true, false, false}, - {0xc0c0c0, true, true, true, false}, - {0x808080, false, false, false, true}, - {0x0000ff, false, false, true, true}, - {0x00ff00, false, true, false, true}, - {0x00ffff, false, true, true, true}, - {0xff0000, true, false, false, true}, - {0xff00ff, true, false, true, true}, - {0xffff00, true, true, false, true}, - {0xffffff, true, true, true, true}, -} - -type hsv struct { - h, s, v float32 -} - -func (a hsv) dist(b hsv) float32 { - dh := a.h - b.h - switch { - case dh > 0.5: - dh = 1 - dh - case dh < -0.5: - dh = -1 - dh - } - ds := a.s - b.s - dv := a.v - b.v - return float32(math.Sqrt(float64(dh*dh + ds*ds + dv*dv))) -} - -func toHSV(rgb int) hsv { - r, g, b := float32((rgb&0xFF0000)>>16)/256.0, - float32((rgb&0x00FF00)>>8)/256.0, - float32(rgb&0x0000FF)/256.0 - min, max := minmax3f(r, g, b) - h := max - min - if h > 0 { - if max == r { - h = (g - b) / h - if h < 0 { - h += 6 - } - } else if max == g { - h = 2 + (b-r)/h - } else { - h = 4 + (r-g)/h - } - } - h /= 6.0 - s := max - min - if max != 0 { - s /= max - } - v := max - return hsv{h: h, s: s, v: v} -} - -type hsvTable []hsv - -func toHSVTable(rgbTable []consoleColor) hsvTable { - t := make(hsvTable, len(rgbTable)) - for i, c := range rgbTable { - t[i] = toHSV(c.rgb) - } - return t -} - -func (t hsvTable) find(rgb int) consoleColor { - hsv := toHSV(rgb) - n := 7 - l := float32(5.0) - for i, p := range t { - d := hsv.dist(p) - if d < l { - l, n = d, i - } - } - return color16[n] -} - -func minmax3f(a, b, c float32) (min, max float32) { - if a < b { - if b < c { - return a, c - } else if a < c { - return a, b - } else { - return c, b - } - } else { - if a < c { - return b, c - } else if b < c { - return b, a - } else { - return c, a - } - } -} - -var n256foreAttr []word -var n256backAttr []word - -func n256setup() { - n256foreAttr = make([]word, 256) - n256backAttr = make([]word, 256) - t := toHSVTable(color16) - for i, rgb := range color256 { - c := t.find(rgb) - n256foreAttr[i] = c.foregroundAttr() - n256backAttr[i] = c.backgroundAttr() - } -} diff --git a/vendor/github.com/mattn/go-colorable/go.mod b/vendor/github.com/mattn/go-colorable/go.mod deleted file mode 100644 index ef3ca9d4..00000000 --- a/vendor/github.com/mattn/go-colorable/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/mattn/go-colorable - -require github.com/mattn/go-isatty v0.0.8 diff --git a/vendor/github.com/mattn/go-colorable/go.sum b/vendor/github.com/mattn/go-colorable/go.sum deleted file mode 100644 index 2c12960e..00000000 --- a/vendor/github.com/mattn/go-colorable/go.sum +++ /dev/null @@ -1,4 +0,0 @@ -github.com/mattn/go-isatty v0.0.5 h1:tHXDdz1cpzGaovsTB+TVB8q90WEokoVmfMqoVcrLUgw= -github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= diff --git a/vendor/github.com/mattn/go-colorable/noncolorable.go b/vendor/github.com/mattn/go-colorable/noncolorable.go deleted file mode 100644 index 95f2c6be..00000000 --- a/vendor/github.com/mattn/go-colorable/noncolorable.go +++ /dev/null @@ -1,55 +0,0 @@ -package colorable - -import ( - "bytes" - "io" -) - -// NonColorable holds writer but removes escape sequence. -type NonColorable struct { - out io.Writer -} - -// NewNonColorable returns new instance of Writer which removes escape sequence from Writer. -func NewNonColorable(w io.Writer) io.Writer { - return &NonColorable{out: w} -} - -// Write writes data on console -func (w *NonColorable) Write(data []byte) (n int, err error) { - er := bytes.NewReader(data) - var bw [1]byte -loop: - for { - c1, err := er.ReadByte() - if err != nil { - break loop - } - if c1 != 0x1b { - bw[0] = c1 - w.out.Write(bw[:]) - continue - } - c2, err := er.ReadByte() - if err != nil { - break loop - } - if c2 != 0x5b { - continue - } - - var buf bytes.Buffer - for { - c, err := er.ReadByte() - if err != nil { - break loop - } - if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '@' { - break - } - buf.Write([]byte(string(c))) - } - } - - return len(data), nil -} diff --git a/vendor/github.com/mattn/go-isatty/.travis.yml b/vendor/github.com/mattn/go-isatty/.travis.yml deleted file mode 100644 index 5597e026..00000000 --- a/vendor/github.com/mattn/go-isatty/.travis.yml +++ /dev/null @@ -1,13 +0,0 @@ -language: go -go: - - tip - -os: - - linux - - osx - -before_install: - - go get github.com/mattn/goveralls - - go get golang.org/x/tools/cmd/cover -script: - - $HOME/gopath/bin/goveralls -repotoken 3gHdORO5k5ziZcWMBxnd9LrMZaJs8m9x5 diff --git a/vendor/github.com/mattn/go-isatty/LICENSE b/vendor/github.com/mattn/go-isatty/LICENSE deleted file mode 100644 index 65dc692b..00000000 --- a/vendor/github.com/mattn/go-isatty/LICENSE +++ /dev/null @@ -1,9 +0,0 @@ -Copyright (c) Yasuhiro MATSUMOTO - -MIT License (Expat) - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/mattn/go-isatty/README.md b/vendor/github.com/mattn/go-isatty/README.md deleted file mode 100644 index 1e69004b..00000000 --- a/vendor/github.com/mattn/go-isatty/README.md +++ /dev/null @@ -1,50 +0,0 @@ -# go-isatty - -[![Godoc Reference](https://godoc.org/github.com/mattn/go-isatty?status.svg)](http://godoc.org/github.com/mattn/go-isatty) -[![Build Status](https://travis-ci.org/mattn/go-isatty.svg?branch=master)](https://travis-ci.org/mattn/go-isatty) -[![Coverage Status](https://coveralls.io/repos/github/mattn/go-isatty/badge.svg?branch=master)](https://coveralls.io/github/mattn/go-isatty?branch=master) -[![Go Report Card](https://goreportcard.com/badge/mattn/go-isatty)](https://goreportcard.com/report/mattn/go-isatty) - -isatty for golang - -## Usage - -```go -package main - -import ( - "fmt" - "github.com/mattn/go-isatty" - "os" -) - -func main() { - if isatty.IsTerminal(os.Stdout.Fd()) { - fmt.Println("Is Terminal") - } else if isatty.IsCygwinTerminal(os.Stdout.Fd()) { - fmt.Println("Is Cygwin/MSYS2 Terminal") - } else { - fmt.Println("Is Not Terminal") - } -} -``` - -## Installation - -``` -$ go get github.com/mattn/go-isatty -``` - -## License - -MIT - -## Author - -Yasuhiro Matsumoto (a.k.a mattn) - -## Thanks - -* k-takata: base idea for IsCygwinTerminal - - https://github.com/k-takata/go-iscygpty diff --git a/vendor/github.com/mattn/go-isatty/doc.go b/vendor/github.com/mattn/go-isatty/doc.go deleted file mode 100644 index 17d4f90e..00000000 --- a/vendor/github.com/mattn/go-isatty/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// Package isatty implements interface to isatty -package isatty diff --git a/vendor/github.com/mattn/go-isatty/go.mod b/vendor/github.com/mattn/go-isatty/go.mod deleted file mode 100644 index a8ddf404..00000000 --- a/vendor/github.com/mattn/go-isatty/go.mod +++ /dev/null @@ -1,5 +0,0 @@ -module github.com/mattn/go-isatty - -require golang.org/x/sys v0.0.0-20191008105621-543471e840be - -go 1.14 diff --git a/vendor/github.com/mattn/go-isatty/go.sum b/vendor/github.com/mattn/go-isatty/go.sum deleted file mode 100644 index c141fc53..00000000 --- a/vendor/github.com/mattn/go-isatty/go.sum +++ /dev/null @@ -1,4 +0,0 @@ -golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO9UxmJRx8K0gsfABByQ= -golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191008105621-543471e840be h1:QAcqgptGM8IQBC9K/RC4o+O9YmqEm0diQn9QmZw/0mU= -golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/vendor/github.com/mattn/go-isatty/isatty_android.go b/vendor/github.com/mattn/go-isatty/isatty_android.go deleted file mode 100644 index d3567cb5..00000000 --- a/vendor/github.com/mattn/go-isatty/isatty_android.go +++ /dev/null @@ -1,23 +0,0 @@ -// +build android - -package isatty - -import ( - "syscall" - "unsafe" -) - -const ioctlReadTermios = syscall.TCGETS - -// IsTerminal return true if the file descriptor is terminal. -func IsTerminal(fd uintptr) bool { - var termios syscall.Termios - _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0) - return err == 0 -} - -// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2 -// terminal. This is also always false on this environment. -func IsCygwinTerminal(fd uintptr) bool { - return false -} diff --git a/vendor/github.com/mattn/go-isatty/isatty_bsd.go b/vendor/github.com/mattn/go-isatty/isatty_bsd.go deleted file mode 100644 index 07e93039..00000000 --- a/vendor/github.com/mattn/go-isatty/isatty_bsd.go +++ /dev/null @@ -1,24 +0,0 @@ -// +build darwin freebsd openbsd netbsd dragonfly -// +build !appengine - -package isatty - -import ( - "syscall" - "unsafe" -) - -const ioctlReadTermios = syscall.TIOCGETA - -// IsTerminal return true if the file descriptor is terminal. -func IsTerminal(fd uintptr) bool { - var termios syscall.Termios - _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0) - return err == 0 -} - -// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2 -// terminal. This is also always false on this environment. -func IsCygwinTerminal(fd uintptr) bool { - return false -} diff --git a/vendor/github.com/mattn/go-isatty/isatty_others.go b/vendor/github.com/mattn/go-isatty/isatty_others.go deleted file mode 100644 index ff714a37..00000000 --- a/vendor/github.com/mattn/go-isatty/isatty_others.go +++ /dev/null @@ -1,15 +0,0 @@ -// +build appengine js nacl - -package isatty - -// IsTerminal returns true if the file descriptor is terminal which -// is always false on js and appengine classic which is a sandboxed PaaS. -func IsTerminal(fd uintptr) bool { - return false -} - -// IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2 -// terminal. This is also always false on this environment. -func IsCygwinTerminal(fd uintptr) bool { - return false -} diff --git a/vendor/github.com/mattn/go-isatty/isatty_plan9.go b/vendor/github.com/mattn/go-isatty/isatty_plan9.go deleted file mode 100644 index bc0a7092..00000000 --- a/vendor/github.com/mattn/go-isatty/isatty_plan9.go +++ /dev/null @@ -1,22 +0,0 @@ -// +build plan9 - -package isatty - -import ( - "syscall" -) - -// IsTerminal returns true if the given file descriptor is a terminal. -func IsTerminal(fd uintptr) bool { - path, err := syscall.Fd2path(fd) - if err != nil { - return false - } - return path == "/dev/cons" || path == "/mnt/term/dev/cons" -} - -// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2 -// terminal. This is also always false on this environment. -func IsCygwinTerminal(fd uintptr) bool { - return false -} diff --git a/vendor/github.com/mattn/go-isatty/isatty_solaris.go b/vendor/github.com/mattn/go-isatty/isatty_solaris.go deleted file mode 100644 index bdd5c79a..00000000 --- a/vendor/github.com/mattn/go-isatty/isatty_solaris.go +++ /dev/null @@ -1,22 +0,0 @@ -// +build solaris -// +build !appengine - -package isatty - -import ( - "golang.org/x/sys/unix" -) - -// IsTerminal returns true if the given file descriptor is a terminal. -// see: http://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libbc/libc/gen/common/isatty.c -func IsTerminal(fd uintptr) bool { - var termio unix.Termio - err := unix.IoctlSetTermio(int(fd), unix.TCGETA, &termio) - return err == nil -} - -// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2 -// terminal. This is also always false on this environment. -func IsCygwinTerminal(fd uintptr) bool { - return false -} diff --git a/vendor/github.com/mattn/go-isatty/isatty_tcgets.go b/vendor/github.com/mattn/go-isatty/isatty_tcgets.go deleted file mode 100644 index 453b025d..00000000 --- a/vendor/github.com/mattn/go-isatty/isatty_tcgets.go +++ /dev/null @@ -1,19 +0,0 @@ -// +build linux aix -// +build !appengine -// +build !android - -package isatty - -import "golang.org/x/sys/unix" - -// IsTerminal return true if the file descriptor is terminal. -func IsTerminal(fd uintptr) bool { - _, err := unix.IoctlGetTermios(int(fd), unix.TCGETS) - return err == nil -} - -// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2 -// terminal. This is also always false on this environment. -func IsCygwinTerminal(fd uintptr) bool { - return false -} diff --git a/vendor/github.com/mattn/go-isatty/isatty_windows.go b/vendor/github.com/mattn/go-isatty/isatty_windows.go deleted file mode 100644 index 1fa86915..00000000 --- a/vendor/github.com/mattn/go-isatty/isatty_windows.go +++ /dev/null @@ -1,125 +0,0 @@ -// +build windows -// +build !appengine - -package isatty - -import ( - "errors" - "strings" - "syscall" - "unicode/utf16" - "unsafe" -) - -const ( - objectNameInfo uintptr = 1 - fileNameInfo = 2 - fileTypePipe = 3 -) - -var ( - kernel32 = syscall.NewLazyDLL("kernel32.dll") - ntdll = syscall.NewLazyDLL("ntdll.dll") - procGetConsoleMode = kernel32.NewProc("GetConsoleMode") - procGetFileInformationByHandleEx = kernel32.NewProc("GetFileInformationByHandleEx") - procGetFileType = kernel32.NewProc("GetFileType") - procNtQueryObject = ntdll.NewProc("NtQueryObject") -) - -func init() { - // Check if GetFileInformationByHandleEx is available. - if procGetFileInformationByHandleEx.Find() != nil { - procGetFileInformationByHandleEx = nil - } -} - -// IsTerminal return true if the file descriptor is terminal. -func IsTerminal(fd uintptr) bool { - var st uint32 - r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, fd, uintptr(unsafe.Pointer(&st)), 0) - return r != 0 && e == 0 -} - -// Check pipe name is used for cygwin/msys2 pty. -// Cygwin/MSYS2 PTY has a name like: -// \{cygwin,msys}-XXXXXXXXXXXXXXXX-ptyN-{from,to}-master -func isCygwinPipeName(name string) bool { - token := strings.Split(name, "-") - if len(token) < 5 { - return false - } - - if token[0] != `\msys` && - token[0] != `\cygwin` && - token[0] != `\Device\NamedPipe\msys` && - token[0] != `\Device\NamedPipe\cygwin` { - return false - } - - if token[1] == "" { - return false - } - - if !strings.HasPrefix(token[2], "pty") { - return false - } - - if token[3] != `from` && token[3] != `to` { - return false - } - - if token[4] != "master" { - return false - } - - return true -} - -// getFileNameByHandle use the undocomented ntdll NtQueryObject to get file full name from file handler -// since GetFileInformationByHandleEx is not avilable under windows Vista and still some old fashion -// guys are using Windows XP, this is a workaround for those guys, it will also work on system from -// Windows vista to 10 -// see https://stackoverflow.com/a/18792477 for details -func getFileNameByHandle(fd uintptr) (string, error) { - if procNtQueryObject == nil { - return "", errors.New("ntdll.dll: NtQueryObject not supported") - } - - var buf [4 + syscall.MAX_PATH]uint16 - var result int - r, _, e := syscall.Syscall6(procNtQueryObject.Addr(), 5, - fd, objectNameInfo, uintptr(unsafe.Pointer(&buf)), uintptr(2*len(buf)), uintptr(unsafe.Pointer(&result)), 0) - if r != 0 { - return "", e - } - return string(utf16.Decode(buf[4 : 4+buf[0]/2])), nil -} - -// IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2 -// terminal. -func IsCygwinTerminal(fd uintptr) bool { - if procGetFileInformationByHandleEx == nil { - name, err := getFileNameByHandle(fd) - if err != nil { - return false - } - return isCygwinPipeName(name) - } - - // Cygwin/msys's pty is a pipe. - ft, _, e := syscall.Syscall(procGetFileType.Addr(), 1, fd, 0, 0) - if ft != fileTypePipe || e != 0 { - return false - } - - var buf [2 + syscall.MAX_PATH]uint16 - r, _, e := syscall.Syscall6(procGetFileInformationByHandleEx.Addr(), - 4, fd, fileNameInfo, uintptr(unsafe.Pointer(&buf)), - uintptr(len(buf)*2), 0, 0) - if r == 0 || e != 0 { - return false - } - - l := *(*uint32)(unsafe.Pointer(&buf)) - return isCygwinPipeName(string(utf16.Decode(buf[2 : 2+l/2]))) -} diff --git a/vendor/github.com/rifflock/lfshook/README.md b/vendor/github.com/rifflock/lfshook/README.md deleted file mode 100644 index ece313ea..00000000 --- a/vendor/github.com/rifflock/lfshook/README.md +++ /dev/null @@ -1,87 +0,0 @@ -# Local Filesystem Hook for Logrus - -[![GoDoc](https://godoc.org/github.com/rifflock/lfshook?status.svg)](http://godoc.org/github.com/rifflock/lfshook) - -Sometimes developers like to write directly to a file on the filesystem. This is a hook for [`logrus`](https://github.com/sirupsen/logrus) which designed to allow users to do that. The log levels are dynamic at instantiation of the hook, so it is capable of logging at some or all levels. - -## Example -```go -import ( - "github.com/rifflock/lfshook" - "github.com/sirupsen/logrus" -) - -var Log *logrus.Logger - -func NewLogger() *logrus.Logger { - if Log != nil { - return Log - } - - pathMap := lfshook.PathMap{ - logrus.InfoLevel: "/var/log/info.log", - logrus.ErrorLevel: "/var/log/error.log", - } - - Log = logrus.New() - Log.Hooks.Add(lfshook.NewHook( - pathMap, - &logrus.JSONFormatter{}, - )) - return Log -} -``` - -### Formatters -`lfshook` will strip colors from any `TextFormatter` type formatters when writing to local file, because the color codes don't look great in file. - -If no formatter is provided via `lfshook.NewHook`, a default text formatter will be used. - -### Log rotation -In order to enable automatic log rotation it's possible to provide an io.Writer instead of the path string of a log file. -In combination with packages like [file-rotatelogs](https://github.com/lestrrat-go/file-rotatelogs) log rotation can easily be achieved. - -```go -package main - -import ( - rotatelogs "github.com/lestrrat-go/file-rotatelogs" - "github.com/rifflock/lfshook" - "github.com/sirupsen/logrus" -) - -var Log *logrus.Logger - -func NewLogger() *logrus.Logger { - if Log != nil { - return Log - } - - path := "/var/log/go.log" - writer := rotatelogs.New( - path+".%Y%m%d%H%M", - rotatelogs.WithLinkName(path), - rotatelogs.WithMaxAge(time.Duration(86400)*time.Second), - rotatelogs.WithRotationTime(time.Duration(604800)*time.Second), - ) - - logrus.Hooks.Add(lfshook.NewHook( - lfshook.WriterMap{ - logrus.InfoLevel: writer, - logrus.ErrorLevel: writer, - }, - &logrus.JSONFormatter, - )) - - Log = logrus.New() - Log.Hooks.Add(lfshook.NewHook( - pathMap, - &logrus.JSONFormatter{}, - )) - - return Log -} -``` - -### Note: -User who run the go application must have read/write permissions to the selected log files. If the files do not exists yet, then user must have permission to the target directory. diff --git a/vendor/github.com/rifflock/lfshook/lfshook.go b/vendor/github.com/rifflock/lfshook/lfshook.go deleted file mode 100644 index 1e8d22af..00000000 --- a/vendor/github.com/rifflock/lfshook/lfshook.go +++ /dev/null @@ -1,194 +0,0 @@ -// Package lfshook is hook for sirupsen/logrus that used for writing the logs to local files. -package lfshook - -import ( - "fmt" - "github.com/sirupsen/logrus" - "io" - "log" - "os" - "path/filepath" - "reflect" - "sync" -) - -// We are logging to file, strip colors to make the output more readable. -var defaultFormatter = &logrus.TextFormatter{DisableColors: true} - -// PathMap is map for mapping a log level to a file's path. -// Multiple levels may share a file, but multiple files may not be used for one level. -type PathMap map[logrus.Level]string - -// WriterMap is map for mapping a log level to an io.Writer. -// Multiple levels may share a writer, but multiple writers may not be used for one level. -type WriterMap map[logrus.Level]io.Writer - -// LfsHook is a hook to handle writing to local log files. -type LfsHook struct { - paths PathMap - writers WriterMap - levels []logrus.Level - lock *sync.Mutex - formatter logrus.Formatter - - defaultPath string - defaultWriter io.Writer - hasDefaultPath bool - hasDefaultWriter bool -} - -// NewHook returns new LFS hook. -// Output can be a string, io.Writer, WriterMap or PathMap. -// If using io.Writer or WriterMap, user is responsible for closing the used io.Writer. -func NewHook(output interface{}, formatter logrus.Formatter) *LfsHook { - hook := &LfsHook{ - lock: new(sync.Mutex), - } - - hook.SetFormatter(formatter) - - switch output.(type) { - case string: - hook.SetDefaultPath(output.(string)) - break - case io.Writer: - hook.SetDefaultWriter(output.(io.Writer)) - break - case PathMap: - hook.paths = output.(PathMap) - for level := range output.(PathMap) { - hook.levels = append(hook.levels, level) - } - break - case WriterMap: - hook.writers = output.(WriterMap) - for level := range output.(WriterMap) { - hook.levels = append(hook.levels, level) - } - break - default: - panic(fmt.Sprintf("unsupported level map type: %v", reflect.TypeOf(output))) - } - - return hook -} - -// SetFormatter sets the format that will be used by hook. -// If using text formatter, this method will disable color output to make the log file more readable. -func (hook *LfsHook) SetFormatter(formatter logrus.Formatter) { - hook.lock.Lock() - defer hook.lock.Unlock() - if formatter == nil { - formatter = defaultFormatter - } else { - switch formatter.(type) { - case *logrus.TextFormatter: - textFormatter := formatter.(*logrus.TextFormatter) - textFormatter.DisableColors = true - } - } - - hook.formatter = formatter -} - -// SetDefaultPath sets default path for levels that don't have any defined output path. -func (hook *LfsHook) SetDefaultPath(defaultPath string) { - hook.lock.Lock() - defer hook.lock.Unlock() - hook.defaultPath = defaultPath - hook.hasDefaultPath = true -} - -// SetDefaultWriter sets default writer for levels that don't have any defined writer. -func (hook *LfsHook) SetDefaultWriter(defaultWriter io.Writer) { - hook.lock.Lock() - defer hook.lock.Unlock() - hook.defaultWriter = defaultWriter - hook.hasDefaultWriter = true -} - -// Fire writes the log file to defined path or using the defined writer. -// User who run this function needs write permissions to the file or directory if the file does not yet exist. -func (hook *LfsHook) Fire(entry *logrus.Entry) error { - hook.lock.Lock() - defer hook.lock.Unlock() - if hook.writers != nil || hook.hasDefaultWriter { - return hook.ioWrite(entry) - } else if hook.paths != nil || hook.hasDefaultPath { - return hook.fileWrite(entry) - } - - return nil -} - -// Write a log line to an io.Writer. -func (hook *LfsHook) ioWrite(entry *logrus.Entry) error { - var ( - writer io.Writer - msg []byte - err error - ok bool - ) - - if writer, ok = hook.writers[entry.Level]; !ok { - if hook.hasDefaultWriter { - writer = hook.defaultWriter - } else { - return nil - } - } - - // use our formatter instead of entry.String() - msg, err = hook.formatter.Format(entry) - - if err != nil { - log.Println("failed to generate string for entry:", err) - return err - } - _, err = writer.Write(msg) - return err -} - -// Write a log line directly to a file. -func (hook *LfsHook) fileWrite(entry *logrus.Entry) error { - var ( - fd *os.File - path string - msg []byte - err error - ok bool - ) - - if path, ok = hook.paths[entry.Level]; !ok { - if hook.hasDefaultPath { - path = hook.defaultPath - } else { - return nil - } - } - - dir := filepath.Dir(path) - os.MkdirAll(dir, os.ModePerm) - - fd, err = os.OpenFile(path, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666) - if err != nil { - log.Println("failed to open logfile:", path, err) - return err - } - defer fd.Close() - - // use our formatter instead of entry.String() - msg, err = hook.formatter.Format(entry) - - if err != nil { - log.Println("failed to generate string for entry:", err) - return err - } - fd.Write(msg) - return nil -} - -// Levels returns configured log levels. -func (hook *LfsHook) Levels() []logrus.Level { - return logrus.AllLevels -} diff --git a/vendor/github.com/sirupsen/logrus/.gitignore b/vendor/github.com/sirupsen/logrus/.gitignore deleted file mode 100644 index 6b7d7d1e..00000000 --- a/vendor/github.com/sirupsen/logrus/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -logrus -vendor diff --git a/vendor/github.com/sirupsen/logrus/.travis.yml b/vendor/github.com/sirupsen/logrus/.travis.yml deleted file mode 100644 index 848938a6..00000000 --- a/vendor/github.com/sirupsen/logrus/.travis.yml +++ /dev/null @@ -1,25 +0,0 @@ -language: go -go_import_path: github.com/sirupsen/logrus -git: - depth: 1 -env: - - GO111MODULE=on - - GO111MODULE=off -go: [ 1.11.x, 1.12.x ] -os: [ linux, osx ] -matrix: - exclude: - - go: 1.12.x - env: GO111MODULE=off - - go: 1.11.x - os: osx -install: - - ./travis/install.sh - - if [[ "$GO111MODULE" == "on" ]]; then go mod download; fi - - if [[ "$GO111MODULE" == "off" ]]; then go get github.com/stretchr/testify/assert golang.org/x/sys/unix github.com/konsorten/go-windows-terminal-sequences; fi -script: - - ./travis/cross_build.sh - - export GOMAXPROCS=4 - - export GORACE=halt_on_error=1 - - go test -race -v ./... - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then go test -race -v -tags appengine ./... ; fi diff --git a/vendor/github.com/sirupsen/logrus/CHANGELOG.md b/vendor/github.com/sirupsen/logrus/CHANGELOG.md deleted file mode 100644 index 51a7ab0c..00000000 --- a/vendor/github.com/sirupsen/logrus/CHANGELOG.md +++ /dev/null @@ -1,200 +0,0 @@ -# 1.4.2 - * Fixes build break for plan9, nacl, solaris -# 1.4.1 -This new release introduces: - * Enhance TextFormatter to not print caller information when they are empty (#944) - * Remove dependency on golang.org/x/crypto (#932, #943) - -Fixes: - * Fix Entry.WithContext method to return a copy of the initial entry (#941) - -# 1.4.0 -This new release introduces: - * Add `DeferExitHandler`, similar to `RegisterExitHandler` but prepending the handler to the list of handlers (semantically like `defer`) (#848). - * Add `CallerPrettyfier` to `JSONFormatter` and `TextFormatter (#909, #911) - * Add `Entry.WithContext()` and `Entry.Context`, to set a context on entries to be used e.g. in hooks (#919). - -Fixes: - * Fix wrong method calls `Logger.Print` and `Logger.Warningln` (#893). - * Update `Entry.Logf` to not do string formatting unless the log level is enabled (#903) - * Fix infinite recursion on unknown `Level.String()` (#907) - * Fix race condition in `getCaller` (#916). - - -# 1.3.0 -This new release introduces: - * Log, Logf, Logln functions for Logger and Entry that take a Level - -Fixes: - * Building prometheus node_exporter on AIX (#840) - * Race condition in TextFormatter (#468) - * Travis CI import path (#868) - * Remove coloured output on Windows (#862) - * Pointer to func as field in JSONFormatter (#870) - * Properly marshal Levels (#873) - -# 1.2.0 -This new release introduces: - * A new method `SetReportCaller` in the `Logger` to enable the file, line and calling function from which the trace has been issued - * A new trace level named `Trace` whose level is below `Debug` - * A configurable exit function to be called upon a Fatal trace - * The `Level` object now implements `encoding.TextUnmarshaler` interface - -# 1.1.1 -This is a bug fix release. - * fix the build break on Solaris - * don't drop a whole trace in JSONFormatter when a field param is a function pointer which can not be serialized - -# 1.1.0 -This new release introduces: - * several fixes: - * a fix for a race condition on entry formatting - * proper cleanup of previously used entries before putting them back in the pool - * the extra new line at the end of message in text formatter has been removed - * a new global public API to check if a level is activated: IsLevelEnabled - * the following methods have been added to the Logger object - * IsLevelEnabled - * SetFormatter - * SetOutput - * ReplaceHooks - * introduction of go module - * an indent configuration for the json formatter - * output colour support for windows - * the field sort function is now configurable for text formatter - * the CLICOLOR and CLICOLOR\_FORCE environment variable support in text formater - -# 1.0.6 - -This new release introduces: - * a new api WithTime which allows to easily force the time of the log entry - which is mostly useful for logger wrapper - * a fix reverting the immutability of the entry given as parameter to the hooks - a new configuration field of the json formatter in order to put all the fields - in a nested dictionnary - * a new SetOutput method in the Logger - * a new configuration of the textformatter to configure the name of the default keys - * a new configuration of the text formatter to disable the level truncation - -# 1.0.5 - -* Fix hooks race (#707) -* Fix panic deadlock (#695) - -# 1.0.4 - -* Fix race when adding hooks (#612) -* Fix terminal check in AppEngine (#635) - -# 1.0.3 - -* Replace example files with testable examples - -# 1.0.2 - -* bug: quote non-string values in text formatter (#583) -* Make (*Logger) SetLevel a public method - -# 1.0.1 - -* bug: fix escaping in text formatter (#575) - -# 1.0.0 - -* Officially changed name to lower-case -* bug: colors on Windows 10 (#541) -* bug: fix race in accessing level (#512) - -# 0.11.5 - -* feature: add writer and writerlevel to entry (#372) - -# 0.11.4 - -* bug: fix undefined variable on solaris (#493) - -# 0.11.3 - -* formatter: configure quoting of empty values (#484) -* formatter: configure quoting character (default is `"`) (#484) -* bug: fix not importing io correctly in non-linux environments (#481) - -# 0.11.2 - -* bug: fix windows terminal detection (#476) - -# 0.11.1 - -* bug: fix tty detection with custom out (#471) - -# 0.11.0 - -* performance: Use bufferpool to allocate (#370) -* terminal: terminal detection for app-engine (#343) -* feature: exit handler (#375) - -# 0.10.0 - -* feature: Add a test hook (#180) -* feature: `ParseLevel` is now case-insensitive (#326) -* feature: `FieldLogger` interface that generalizes `Logger` and `Entry` (#308) -* performance: avoid re-allocations on `WithFields` (#335) - -# 0.9.0 - -* logrus/text_formatter: don't emit empty msg -* logrus/hooks/airbrake: move out of main repository -* logrus/hooks/sentry: move out of main repository -* logrus/hooks/papertrail: move out of main repository -* logrus/hooks/bugsnag: move out of main repository -* logrus/core: run tests with `-race` -* logrus/core: detect TTY based on `stderr` -* logrus/core: support `WithError` on logger -* logrus/core: Solaris support - -# 0.8.7 - -* logrus/core: fix possible race (#216) -* logrus/doc: small typo fixes and doc improvements - - -# 0.8.6 - -* hooks/raven: allow passing an initialized client - -# 0.8.5 - -* logrus/core: revert #208 - -# 0.8.4 - -* formatter/text: fix data race (#218) - -# 0.8.3 - -* logrus/core: fix entry log level (#208) -* logrus/core: improve performance of text formatter by 40% -* logrus/core: expose `LevelHooks` type -* logrus/core: add support for DragonflyBSD and NetBSD -* formatter/text: print structs more verbosely - -# 0.8.2 - -* logrus: fix more Fatal family functions - -# 0.8.1 - -* logrus: fix not exiting on `Fatalf` and `Fatalln` - -# 0.8.0 - -* logrus: defaults to stderr instead of stdout -* hooks/sentry: add special field for `*http.Request` -* formatter/text: ignore Windows for colors - -# 0.7.3 - -* formatter/\*: allow configuration of timestamp layout - -# 0.7.2 - -* formatter/text: Add configuration option for time format (#158) diff --git a/vendor/github.com/sirupsen/logrus/LICENSE b/vendor/github.com/sirupsen/logrus/LICENSE deleted file mode 100644 index f090cb42..00000000 --- a/vendor/github.com/sirupsen/logrus/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2014 Simon Eskildsen - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/vendor/github.com/sirupsen/logrus/README.md b/vendor/github.com/sirupsen/logrus/README.md deleted file mode 100644 index a4796eb0..00000000 --- a/vendor/github.com/sirupsen/logrus/README.md +++ /dev/null @@ -1,495 +0,0 @@ -# Logrus :walrus: [![Build Status](https://travis-ci.org/sirupsen/logrus.svg?branch=master)](https://travis-ci.org/sirupsen/logrus) [![GoDoc](https://godoc.org/github.com/sirupsen/logrus?status.svg)](https://godoc.org/github.com/sirupsen/logrus) - -Logrus is a structured logger for Go (golang), completely API compatible with -the standard library logger. - -**Seeing weird case-sensitive problems?** It's in the past been possible to -import Logrus as both upper- and lower-case. Due to the Go package environment, -this caused issues in the community and we needed a standard. Some environments -experienced problems with the upper-case variant, so the lower-case was decided. -Everything using `logrus` will need to use the lower-case: -`github.com/sirupsen/logrus`. Any package that isn't, should be changed. - -To fix Glide, see [these -comments](https://github.com/sirupsen/logrus/issues/553#issuecomment-306591437). -For an in-depth explanation of the casing issue, see [this -comment](https://github.com/sirupsen/logrus/issues/570#issuecomment-313933276). - -**Are you interested in assisting in maintaining Logrus?** Currently I have a -lot of obligations, and I am unable to provide Logrus with the maintainership it -needs. If you'd like to help, please reach out to me at `simon at author's -username dot com`. - -Nicely color-coded in development (when a TTY is attached, otherwise just -plain text): - -![Colored](http://i.imgur.com/PY7qMwd.png) - -With `log.SetFormatter(&log.JSONFormatter{})`, for easy parsing by logstash -or Splunk: - -```json -{"animal":"walrus","level":"info","msg":"A group of walrus emerges from the -ocean","size":10,"time":"2014-03-10 19:57:38.562264131 -0400 EDT"} - -{"level":"warning","msg":"The group's number increased tremendously!", -"number":122,"omg":true,"time":"2014-03-10 19:57:38.562471297 -0400 EDT"} - -{"animal":"walrus","level":"info","msg":"A giant walrus appears!", -"size":10,"time":"2014-03-10 19:57:38.562500591 -0400 EDT"} - -{"animal":"walrus","level":"info","msg":"Tremendously sized cow enters the ocean.", -"size":9,"time":"2014-03-10 19:57:38.562527896 -0400 EDT"} - -{"level":"fatal","msg":"The ice breaks!","number":100,"omg":true, -"time":"2014-03-10 19:57:38.562543128 -0400 EDT"} -``` - -With the default `log.SetFormatter(&log.TextFormatter{})` when a TTY is not -attached, the output is compatible with the -[logfmt](http://godoc.org/github.com/kr/logfmt) format: - -```text -time="2015-03-26T01:27:38-04:00" level=debug msg="Started observing beach" animal=walrus number=8 -time="2015-03-26T01:27:38-04:00" level=info msg="A group of walrus emerges from the ocean" animal=walrus size=10 -time="2015-03-26T01:27:38-04:00" level=warning msg="The group's number increased tremendously!" number=122 omg=true -time="2015-03-26T01:27:38-04:00" level=debug msg="Temperature changes" temperature=-4 -time="2015-03-26T01:27:38-04:00" level=panic msg="It's over 9000!" animal=orca size=9009 -time="2015-03-26T01:27:38-04:00" level=fatal msg="The ice breaks!" err=&{0x2082280c0 map[animal:orca size:9009] 2015-03-26 01:27:38.441574009 -0400 EDT panic It's over 9000!} number=100 omg=true -``` -To ensure this behaviour even if a TTY is attached, set your formatter as follows: - -```go - log.SetFormatter(&log.TextFormatter{ - DisableColors: true, - FullTimestamp: true, - }) -``` - -#### Logging Method Name - -If you wish to add the calling method as a field, instruct the logger via: -```go -log.SetReportCaller(true) -``` -This adds the caller as 'method' like so: - -```json -{"animal":"penguin","level":"fatal","method":"github.com/sirupsen/arcticcreatures.migrate","msg":"a penguin swims by", -"time":"2014-03-10 19:57:38.562543129 -0400 EDT"} -``` - -```text -time="2015-03-26T01:27:38-04:00" level=fatal method=github.com/sirupsen/arcticcreatures.migrate msg="a penguin swims by" animal=penguin -``` -Note that this does add measurable overhead - the cost will depend on the version of Go, but is -between 20 and 40% in recent tests with 1.6 and 1.7. You can validate this in your -environment via benchmarks: -``` -go test -bench=.*CallerTracing -``` - - -#### Case-sensitivity - -The organization's name was changed to lower-case--and this will not be changed -back. If you are getting import conflicts due to case sensitivity, please use -the lower-case import: `github.com/sirupsen/logrus`. - -#### Example - -The simplest way to use Logrus is simply the package-level exported logger: - -```go -package main - -import ( - log "github.com/sirupsen/logrus" -) - -func main() { - log.WithFields(log.Fields{ - "animal": "walrus", - }).Info("A walrus appears") -} -``` - -Note that it's completely api-compatible with the stdlib logger, so you can -replace your `log` imports everywhere with `log "github.com/sirupsen/logrus"` -and you'll now have the flexibility of Logrus. You can customize it all you -want: - -```go -package main - -import ( - "os" - log "github.com/sirupsen/logrus" -) - -func init() { - // Log as JSON instead of the default ASCII formatter. - log.SetFormatter(&log.JSONFormatter{}) - - // Output to stdout instead of the default stderr - // Can be any io.Writer, see below for File example - log.SetOutput(os.Stdout) - - // Only log the warning severity or above. - log.SetLevel(log.WarnLevel) -} - -func main() { - log.WithFields(log.Fields{ - "animal": "walrus", - "size": 10, - }).Info("A group of walrus emerges from the ocean") - - log.WithFields(log.Fields{ - "omg": true, - "number": 122, - }).Warn("The group's number increased tremendously!") - - log.WithFields(log.Fields{ - "omg": true, - "number": 100, - }).Fatal("The ice breaks!") - - // A common pattern is to re-use fields between logging statements by re-using - // the logrus.Entry returned from WithFields() - contextLogger := log.WithFields(log.Fields{ - "common": "this is a common field", - "other": "I also should be logged always", - }) - - contextLogger.Info("I'll be logged with common and other field") - contextLogger.Info("Me too") -} -``` - -For more advanced usage such as logging to multiple locations from the same -application, you can also create an instance of the `logrus` Logger: - -```go -package main - -import ( - "os" - "github.com/sirupsen/logrus" -) - -// Create a new instance of the logger. You can have any number of instances. -var log = logrus.New() - -func main() { - // The API for setting attributes is a little different than the package level - // exported logger. See Godoc. - log.Out = os.Stdout - - // You could set this to any `io.Writer` such as a file - // file, err := os.OpenFile("logrus.log", os.O_CREATE|os.O_WRONLY, 0666) - // if err == nil { - // log.Out = file - // } else { - // log.Info("Failed to log to file, using default stderr") - // } - - log.WithFields(logrus.Fields{ - "animal": "walrus", - "size": 10, - }).Info("A group of walrus emerges from the ocean") -} -``` - -#### Fields - -Logrus encourages careful, structured logging through logging fields instead of -long, unparseable error messages. For example, instead of: `log.Fatalf("Failed -to send event %s to topic %s with key %d")`, you should log the much more -discoverable: - -```go -log.WithFields(log.Fields{ - "event": event, - "topic": topic, - "key": key, -}).Fatal("Failed to send event") -``` - -We've found this API forces you to think about logging in a way that produces -much more useful logging messages. We've been in countless situations where just -a single added field to a log statement that was already there would've saved us -hours. The `WithFields` call is optional. - -In general, with Logrus using any of the `printf`-family functions should be -seen as a hint you should add a field, however, you can still use the -`printf`-family functions with Logrus. - -#### Default Fields - -Often it's helpful to have fields _always_ attached to log statements in an -application or parts of one. For example, you may want to always log the -`request_id` and `user_ip` in the context of a request. Instead of writing -`log.WithFields(log.Fields{"request_id": request_id, "user_ip": user_ip})` on -every line, you can create a `logrus.Entry` to pass around instead: - -```go -requestLogger := log.WithFields(log.Fields{"request_id": request_id, "user_ip": user_ip}) -requestLogger.Info("something happened on that request") # will log request_id and user_ip -requestLogger.Warn("something not great happened") -``` - -#### Hooks - -You can add hooks for logging levels. For example to send errors to an exception -tracking service on `Error`, `Fatal` and `Panic`, info to StatsD or log to -multiple places simultaneously, e.g. syslog. - -Logrus comes with [built-in hooks](hooks/). Add those, or your custom hook, in -`init`: - -```go -import ( - log "github.com/sirupsen/logrus" - "gopkg.in/gemnasium/logrus-airbrake-hook.v2" // the package is named "airbrake" - logrus_syslog "github.com/sirupsen/logrus/hooks/syslog" - "log/syslog" -) - -func init() { - - // Use the Airbrake hook to report errors that have Error severity or above to - // an exception tracker. You can create custom hooks, see the Hooks section. - log.AddHook(airbrake.NewHook(123, "xyz", "production")) - - hook, err := logrus_syslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "") - if err != nil { - log.Error("Unable to connect to local syslog daemon") - } else { - log.AddHook(hook) - } -} -``` -Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/var/run/syslog" or "/var/run/log"). For the detail, please check the [syslog hook README](hooks/syslog/README.md). - -A list of currently known of service hook can be found in this wiki [page](https://github.com/sirupsen/logrus/wiki/Hooks) - - -#### Level logging - -Logrus has seven logging levels: Trace, Debug, Info, Warning, Error, Fatal and Panic. - -```go -log.Trace("Something very low level.") -log.Debug("Useful debugging information.") -log.Info("Something noteworthy happened!") -log.Warn("You should probably take a look at this.") -log.Error("Something failed but I'm not quitting.") -// Calls os.Exit(1) after logging -log.Fatal("Bye.") -// Calls panic() after logging -log.Panic("I'm bailing.") -``` - -You can set the logging level on a `Logger`, then it will only log entries with -that severity or anything above it: - -```go -// Will log anything that is info or above (warn, error, fatal, panic). Default. -log.SetLevel(log.InfoLevel) -``` - -It may be useful to set `log.Level = logrus.DebugLevel` in a debug or verbose -environment if your application has that. - -#### Entries - -Besides the fields added with `WithField` or `WithFields` some fields are -automatically added to all logging events: - -1. `time`. The timestamp when the entry was created. -2. `msg`. The logging message passed to `{Info,Warn,Error,Fatal,Panic}` after - the `AddFields` call. E.g. `Failed to send event.` -3. `level`. The logging level. E.g. `info`. - -#### Environments - -Logrus has no notion of environment. - -If you wish for hooks and formatters to only be used in specific environments, -you should handle that yourself. For example, if your application has a global -variable `Environment`, which is a string representation of the environment you -could do: - -```go -import ( - log "github.com/sirupsen/logrus" -) - -init() { - // do something here to set environment depending on an environment variable - // or command-line flag - if Environment == "production" { - log.SetFormatter(&log.JSONFormatter{}) - } else { - // The TextFormatter is default, you don't actually have to do this. - log.SetFormatter(&log.TextFormatter{}) - } -} -``` - -This configuration is how `logrus` was intended to be used, but JSON in -production is mostly only useful if you do log aggregation with tools like -Splunk or Logstash. - -#### Formatters - -The built-in logging formatters are: - -* `logrus.TextFormatter`. Logs the event in colors if stdout is a tty, otherwise - without colors. - * *Note:* to force colored output when there is no TTY, set the `ForceColors` - field to `true`. To force no colored output even if there is a TTY set the - `DisableColors` field to `true`. For Windows, see - [github.com/mattn/go-colorable](https://github.com/mattn/go-colorable). - * When colors are enabled, levels are truncated to 4 characters by default. To disable - truncation set the `DisableLevelTruncation` field to `true`. - * All options are listed in the [generated docs](https://godoc.org/github.com/sirupsen/logrus#TextFormatter). -* `logrus.JSONFormatter`. Logs fields as JSON. - * All options are listed in the [generated docs](https://godoc.org/github.com/sirupsen/logrus#JSONFormatter). - -Third party logging formatters: - -* [`FluentdFormatter`](https://github.com/joonix/log). Formats entries that can be parsed by Kubernetes and Google Container Engine. -* [`GELF`](https://github.com/fabienm/go-logrus-formatters). Formats entries so they comply to Graylog's [GELF 1.1 specification](http://docs.graylog.org/en/2.4/pages/gelf.html). -* [`logstash`](https://github.com/bshuster-repo/logrus-logstash-hook). Logs fields as [Logstash](http://logstash.net) Events. -* [`prefixed`](https://github.com/x-cray/logrus-prefixed-formatter). Displays log entry source along with alternative layout. -* [`zalgo`](https://github.com/aybabtme/logzalgo). Invoking the P͉̫o̳̼̊w̖͈̰͎e̬͔̭͂r͚̼̹̲ ̫͓͉̳͈ō̠͕͖̚f̝͍̠ ͕̲̞͖͑Z̖̫̤̫ͪa͉̬͈̗l͖͎g̳̥o̰̥̅!̣͔̲̻͊̄ ̙̘̦̹̦. -* [`nested-logrus-formatter`](https://github.com/antonfisher/nested-logrus-formatter). Converts logrus fields to a nested structure. - -You can define your formatter by implementing the `Formatter` interface, -requiring a `Format` method. `Format` takes an `*Entry`. `entry.Data` is a -`Fields` type (`map[string]interface{}`) with all your fields as well as the -default ones (see Entries section above): - -```go -type MyJSONFormatter struct { -} - -log.SetFormatter(new(MyJSONFormatter)) - -func (f *MyJSONFormatter) Format(entry *Entry) ([]byte, error) { - // Note this doesn't include Time, Level and Message which are available on - // the Entry. Consult `godoc` on information about those fields or read the - // source of the official loggers. - serialized, err := json.Marshal(entry.Data) - if err != nil { - return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err) - } - return append(serialized, '\n'), nil -} -``` - -#### Logger as an `io.Writer` - -Logrus can be transformed into an `io.Writer`. That writer is the end of an `io.Pipe` and it is your responsibility to close it. - -```go -w := logger.Writer() -defer w.Close() - -srv := http.Server{ - // create a stdlib log.Logger that writes to - // logrus.Logger. - ErrorLog: log.New(w, "", 0), -} -``` - -Each line written to that writer will be printed the usual way, using formatters -and hooks. The level for those entries is `info`. - -This means that we can override the standard library logger easily: - -```go -logger := logrus.New() -logger.Formatter = &logrus.JSONFormatter{} - -// Use logrus for standard log output -// Note that `log` here references stdlib's log -// Not logrus imported under the name `log`. -log.SetOutput(logger.Writer()) -``` - -#### Rotation - -Log rotation is not provided with Logrus. Log rotation should be done by an -external program (like `logrotate(8)`) that can compress and delete old log -entries. It should not be a feature of the application-level logger. - -#### Tools - -| Tool | Description | -| ---- | ----------- | -|[Logrus Mate](https://github.com/gogap/logrus_mate)|Logrus mate is a tool for Logrus to manage loggers, you can initial logger's level, hook and formatter by config file, the logger will generated with different config at different environment.| -|[Logrus Viper Helper](https://github.com/heirko/go-contrib/tree/master/logrusHelper)|An Helper around Logrus to wrap with spf13/Viper to load configuration with fangs! And to simplify Logrus configuration use some behavior of [Logrus Mate](https://github.com/gogap/logrus_mate). [sample](https://github.com/heirko/iris-contrib/blob/master/middleware/logrus-logger/example) | - -#### Testing - -Logrus has a built in facility for asserting the presence of log messages. This is implemented through the `test` hook and provides: - -* decorators for existing logger (`test.NewLocal` and `test.NewGlobal`) which basically just add the `test` hook -* a test logger (`test.NewNullLogger`) that just records log messages (and does not output any): - -```go -import( - "github.com/sirupsen/logrus" - "github.com/sirupsen/logrus/hooks/test" - "github.com/stretchr/testify/assert" - "testing" -) - -func TestSomething(t*testing.T){ - logger, hook := test.NewNullLogger() - logger.Error("Helloerror") - - assert.Equal(t, 1, len(hook.Entries)) - assert.Equal(t, logrus.ErrorLevel, hook.LastEntry().Level) - assert.Equal(t, "Helloerror", hook.LastEntry().Message) - - hook.Reset() - assert.Nil(t, hook.LastEntry()) -} -``` - -#### Fatal handlers - -Logrus can register one or more functions that will be called when any `fatal` -level message is logged. The registered handlers will be executed before -logrus performs a `os.Exit(1)`. This behavior may be helpful if callers need -to gracefully shutdown. Unlike a `panic("Something went wrong...")` call which can be intercepted with a deferred `recover` a call to `os.Exit(1)` can not be intercepted. - -``` -... -handler := func() { - // gracefully shutdown something... -} -logrus.RegisterExitHandler(handler) -... -``` - -#### Thread safety - -By default, Logger is protected by a mutex for concurrent writes. The mutex is held when calling hooks and writing logs. -If you are sure such locking is not needed, you can call logger.SetNoLock() to disable the locking. - -Situation when locking is not needed includes: - -* You have no hooks registered, or hooks calling is already thread-safe. - -* Writing to logger.Out is already thread-safe, for example: - - 1) logger.Out is protected by locks. - - 2) logger.Out is a os.File handler opened with `O_APPEND` flag, and every write is smaller than 4k. (This allow multi-thread/multi-process writing) - - (Refer to http://www.notthewizard.com/2014/06/17/are-files-appends-really-atomic/) diff --git a/vendor/github.com/sirupsen/logrus/alt_exit.go b/vendor/github.com/sirupsen/logrus/alt_exit.go deleted file mode 100644 index 8fd189e1..00000000 --- a/vendor/github.com/sirupsen/logrus/alt_exit.go +++ /dev/null @@ -1,76 +0,0 @@ -package logrus - -// The following code was sourced and modified from the -// https://github.com/tebeka/atexit package governed by the following license: -// -// Copyright (c) 2012 Miki Tebeka . -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -// the Software, and to permit persons to whom the Software is furnished to do so, -// subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -import ( - "fmt" - "os" -) - -var handlers = []func(){} - -func runHandler(handler func()) { - defer func() { - if err := recover(); err != nil { - fmt.Fprintln(os.Stderr, "Error: Logrus exit handler error:", err) - } - }() - - handler() -} - -func runHandlers() { - for _, handler := range handlers { - runHandler(handler) - } -} - -// Exit runs all the Logrus atexit handlers and then terminates the program using os.Exit(code) -func Exit(code int) { - runHandlers() - os.Exit(code) -} - -// RegisterExitHandler appends a Logrus Exit handler to the list of handlers, -// call logrus.Exit to invoke all handlers. The handlers will also be invoked when -// any Fatal log entry is made. -// -// This method is useful when a caller wishes to use logrus to log a fatal -// message but also needs to gracefully shutdown. An example usecase could be -// closing database connections, or sending a alert that the application is -// closing. -func RegisterExitHandler(handler func()) { - handlers = append(handlers, handler) -} - -// DeferExitHandler prepends a Logrus Exit handler to the list of handlers, -// call logrus.Exit to invoke all handlers. The handlers will also be invoked when -// any Fatal log entry is made. -// -// This method is useful when a caller wishes to use logrus to log a fatal -// message but also needs to gracefully shutdown. An example usecase could be -// closing database connections, or sending a alert that the application is -// closing. -func DeferExitHandler(handler func()) { - handlers = append([]func(){handler}, handlers...) -} diff --git a/vendor/github.com/sirupsen/logrus/appveyor.yml b/vendor/github.com/sirupsen/logrus/appveyor.yml deleted file mode 100644 index 96c2ce15..00000000 --- a/vendor/github.com/sirupsen/logrus/appveyor.yml +++ /dev/null @@ -1,14 +0,0 @@ -version: "{build}" -platform: x64 -clone_folder: c:\gopath\src\github.com\sirupsen\logrus -environment: - GOPATH: c:\gopath -branches: - only: - - master -install: - - set PATH=%GOPATH%\bin;c:\go\bin;%PATH% - - go version -build_script: - - go get -t - - go test diff --git a/vendor/github.com/sirupsen/logrus/doc.go b/vendor/github.com/sirupsen/logrus/doc.go deleted file mode 100644 index da67aba0..00000000 --- a/vendor/github.com/sirupsen/logrus/doc.go +++ /dev/null @@ -1,26 +0,0 @@ -/* -Package logrus is a structured logger for Go, completely API compatible with the standard library logger. - - -The simplest way to use Logrus is simply the package-level exported logger: - - package main - - import ( - log "github.com/sirupsen/logrus" - ) - - func main() { - log.WithFields(log.Fields{ - "animal": "walrus", - "number": 1, - "size": 10, - }).Info("A walrus appears") - } - -Output: - time="2015-09-07T08:48:33Z" level=info msg="A walrus appears" animal=walrus number=1 size=10 - -For a full guide visit https://github.com/sirupsen/logrus -*/ -package logrus diff --git a/vendor/github.com/sirupsen/logrus/entry.go b/vendor/github.com/sirupsen/logrus/entry.go deleted file mode 100644 index 63e25583..00000000 --- a/vendor/github.com/sirupsen/logrus/entry.go +++ /dev/null @@ -1,407 +0,0 @@ -package logrus - -import ( - "bytes" - "context" - "fmt" - "os" - "reflect" - "runtime" - "strings" - "sync" - "time" -) - -var ( - bufferPool *sync.Pool - - // qualified package name, cached at first use - logrusPackage string - - // Positions in the call stack when tracing to report the calling method - minimumCallerDepth int - - // Used for caller information initialisation - callerInitOnce sync.Once -) - -const ( - maximumCallerDepth int = 25 - knownLogrusFrames int = 4 -) - -func init() { - bufferPool = &sync.Pool{ - New: func() interface{} { - return new(bytes.Buffer) - }, - } - - // start at the bottom of the stack before the package-name cache is primed - minimumCallerDepth = 1 -} - -// Defines the key when adding errors using WithError. -var ErrorKey = "error" - -// An entry is the final or intermediate Logrus logging entry. It contains all -// the fields passed with WithField{,s}. It's finally logged when Trace, Debug, -// Info, Warn, Error, Fatal or Panic is called on it. These objects can be -// reused and passed around as much as you wish to avoid field duplication. -type Entry struct { - Logger *Logger - - // Contains all the fields set by the user. - Data Fields - - // Time at which the log entry was created - Time time.Time - - // Level the log entry was logged at: Trace, Debug, Info, Warn, Error, Fatal or Panic - // This field will be set on entry firing and the value will be equal to the one in Logger struct field. - Level Level - - // Calling method, with package name - Caller *runtime.Frame - - // Message passed to Trace, Debug, Info, Warn, Error, Fatal or Panic - Message string - - // When formatter is called in entry.log(), a Buffer may be set to entry - Buffer *bytes.Buffer - - // Contains the context set by the user. Useful for hook processing etc. - Context context.Context - - // err may contain a field formatting error - err string -} - -func NewEntry(logger *Logger) *Entry { - return &Entry{ - Logger: logger, - // Default is three fields, plus one optional. Give a little extra room. - Data: make(Fields, 6), - } -} - -// Returns the string representation from the reader and ultimately the -// formatter. -func (entry *Entry) String() (string, error) { - serialized, err := entry.Logger.Formatter.Format(entry) - if err != nil { - return "", err - } - str := string(serialized) - return str, nil -} - -// Add an error as single field (using the key defined in ErrorKey) to the Entry. -func (entry *Entry) WithError(err error) *Entry { - return entry.WithField(ErrorKey, err) -} - -// Add a context to the Entry. -func (entry *Entry) WithContext(ctx context.Context) *Entry { - return &Entry{Logger: entry.Logger, Data: entry.Data, Time: entry.Time, err: entry.err, Context: ctx} -} - -// Add a single field to the Entry. -func (entry *Entry) WithField(key string, value interface{}) *Entry { - return entry.WithFields(Fields{key: value}) -} - -// Add a map of fields to the Entry. -func (entry *Entry) WithFields(fields Fields) *Entry { - data := make(Fields, len(entry.Data)+len(fields)) - for k, v := range entry.Data { - data[k] = v - } - fieldErr := entry.err - for k, v := range fields { - isErrField := false - if t := reflect.TypeOf(v); t != nil { - switch t.Kind() { - case reflect.Func: - isErrField = true - case reflect.Ptr: - isErrField = t.Elem().Kind() == reflect.Func - } - } - if isErrField { - tmp := fmt.Sprintf("can not add field %q", k) - if fieldErr != "" { - fieldErr = entry.err + ", " + tmp - } else { - fieldErr = tmp - } - } else { - data[k] = v - } - } - return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time, err: fieldErr, Context: entry.Context} -} - -// Overrides the time of the Entry. -func (entry *Entry) WithTime(t time.Time) *Entry { - return &Entry{Logger: entry.Logger, Data: entry.Data, Time: t, err: entry.err, Context: entry.Context} -} - -// getPackageName reduces a fully qualified function name to the package name -// There really ought to be to be a better way... -func getPackageName(f string) string { - for { - lastPeriod := strings.LastIndex(f, ".") - lastSlash := strings.LastIndex(f, "/") - if lastPeriod > lastSlash { - f = f[:lastPeriod] - } else { - break - } - } - - return f -} - -// getCaller retrieves the name of the first non-logrus calling function -func getCaller() *runtime.Frame { - - // cache this package's fully-qualified name - callerInitOnce.Do(func() { - pcs := make([]uintptr, 2) - _ = runtime.Callers(0, pcs) - logrusPackage = getPackageName(runtime.FuncForPC(pcs[1]).Name()) - - // now that we have the cache, we can skip a minimum count of known-logrus functions - // XXX this is dubious, the number of frames may vary - minimumCallerDepth = knownLogrusFrames - }) - - // Restrict the lookback frames to avoid runaway lookups - pcs := make([]uintptr, maximumCallerDepth) - depth := runtime.Callers(minimumCallerDepth, pcs) - frames := runtime.CallersFrames(pcs[:depth]) - - for f, again := frames.Next(); again; f, again = frames.Next() { - pkg := getPackageName(f.Function) - - // If the caller isn't part of this package, we're done - if pkg != logrusPackage { - return &f - } - } - - // if we got here, we failed to find the caller's context - return nil -} - -func (entry Entry) HasCaller() (has bool) { - return entry.Logger != nil && - entry.Logger.ReportCaller && - entry.Caller != nil -} - -// This function is not declared with a pointer value because otherwise -// race conditions will occur when using multiple goroutines -func (entry Entry) log(level Level, msg string) { - var buffer *bytes.Buffer - - // Default to now, but allow users to override if they want. - // - // We don't have to worry about polluting future calls to Entry#log() - // with this assignment because this function is declared with a - // non-pointer receiver. - if entry.Time.IsZero() { - entry.Time = time.Now() - } - - entry.Level = level - entry.Message = msg - if entry.Logger.ReportCaller { - entry.Caller = getCaller() - } - - entry.fireHooks() - - buffer = bufferPool.Get().(*bytes.Buffer) - buffer.Reset() - defer bufferPool.Put(buffer) - entry.Buffer = buffer - - entry.write() - - entry.Buffer = nil - - // To avoid Entry#log() returning a value that only would make sense for - // panic() to use in Entry#Panic(), we avoid the allocation by checking - // directly here. - if level <= PanicLevel { - panic(&entry) - } -} - -func (entry *Entry) fireHooks() { - entry.Logger.mu.Lock() - defer entry.Logger.mu.Unlock() - err := entry.Logger.Hooks.Fire(entry.Level, entry) - if err != nil { - fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err) - } -} - -func (entry *Entry) write() { - entry.Logger.mu.Lock() - defer entry.Logger.mu.Unlock() - serialized, err := entry.Logger.Formatter.Format(entry) - if err != nil { - fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err) - } else { - _, err = entry.Logger.Out.Write(serialized) - if err != nil { - fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err) - } - } -} - -func (entry *Entry) Log(level Level, args ...interface{}) { - if entry.Logger.IsLevelEnabled(level) { - entry.log(level, fmt.Sprint(args...)) - } -} - -func (entry *Entry) Trace(args ...interface{}) { - entry.Log(TraceLevel, args...) -} - -func (entry *Entry) Debug(args ...interface{}) { - entry.Log(DebugLevel, args...) -} - -func (entry *Entry) Print(args ...interface{}) { - entry.Info(args...) -} - -func (entry *Entry) Info(args ...interface{}) { - entry.Log(InfoLevel, args...) -} - -func (entry *Entry) Warn(args ...interface{}) { - entry.Log(WarnLevel, args...) -} - -func (entry *Entry) Warning(args ...interface{}) { - entry.Warn(args...) -} - -func (entry *Entry) Error(args ...interface{}) { - entry.Log(ErrorLevel, args...) -} - -func (entry *Entry) Fatal(args ...interface{}) { - entry.Log(FatalLevel, args...) - entry.Logger.Exit(1) -} - -func (entry *Entry) Panic(args ...interface{}) { - entry.Log(PanicLevel, args...) - panic(fmt.Sprint(args...)) -} - -// Entry Printf family functions - -func (entry *Entry) Logf(level Level, format string, args ...interface{}) { - if entry.Logger.IsLevelEnabled(level) { - entry.Log(level, fmt.Sprintf(format, args...)) - } -} - -func (entry *Entry) Tracef(format string, args ...interface{}) { - entry.Logf(TraceLevel, format, args...) -} - -func (entry *Entry) Debugf(format string, args ...interface{}) { - entry.Logf(DebugLevel, format, args...) -} - -func (entry *Entry) Infof(format string, args ...interface{}) { - entry.Logf(InfoLevel, format, args...) -} - -func (entry *Entry) Printf(format string, args ...interface{}) { - entry.Infof(format, args...) -} - -func (entry *Entry) Warnf(format string, args ...interface{}) { - entry.Logf(WarnLevel, format, args...) -} - -func (entry *Entry) Warningf(format string, args ...interface{}) { - entry.Warnf(format, args...) -} - -func (entry *Entry) Errorf(format string, args ...interface{}) { - entry.Logf(ErrorLevel, format, args...) -} - -func (entry *Entry) Fatalf(format string, args ...interface{}) { - entry.Logf(FatalLevel, format, args...) - entry.Logger.Exit(1) -} - -func (entry *Entry) Panicf(format string, args ...interface{}) { - entry.Logf(PanicLevel, format, args...) -} - -// Entry Println family functions - -func (entry *Entry) Logln(level Level, args ...interface{}) { - if entry.Logger.IsLevelEnabled(level) { - entry.Log(level, entry.sprintlnn(args...)) - } -} - -func (entry *Entry) Traceln(args ...interface{}) { - entry.Logln(TraceLevel, args...) -} - -func (entry *Entry) Debugln(args ...interface{}) { - entry.Logln(DebugLevel, args...) -} - -func (entry *Entry) Infoln(args ...interface{}) { - entry.Logln(InfoLevel, args...) -} - -func (entry *Entry) Println(args ...interface{}) { - entry.Infoln(args...) -} - -func (entry *Entry) Warnln(args ...interface{}) { - entry.Logln(WarnLevel, args...) -} - -func (entry *Entry) Warningln(args ...interface{}) { - entry.Warnln(args...) -} - -func (entry *Entry) Errorln(args ...interface{}) { - entry.Logln(ErrorLevel, args...) -} - -func (entry *Entry) Fatalln(args ...interface{}) { - entry.Logln(FatalLevel, args...) - entry.Logger.Exit(1) -} - -func (entry *Entry) Panicln(args ...interface{}) { - entry.Logln(PanicLevel, args...) -} - -// Sprintlnn => Sprint no newline. This is to get the behavior of how -// fmt.Sprintln where spaces are always added between operands, regardless of -// their type. Instead of vendoring the Sprintln implementation to spare a -// string allocation, we do the simplest thing. -func (entry *Entry) sprintlnn(args ...interface{}) string { - msg := fmt.Sprintln(args...) - return msg[:len(msg)-1] -} diff --git a/vendor/github.com/sirupsen/logrus/exported.go b/vendor/github.com/sirupsen/logrus/exported.go deleted file mode 100644 index 62fc2f21..00000000 --- a/vendor/github.com/sirupsen/logrus/exported.go +++ /dev/null @@ -1,225 +0,0 @@ -package logrus - -import ( - "context" - "io" - "time" -) - -var ( - // std is the name of the standard logger in stdlib `log` - std = New() -) - -func StandardLogger() *Logger { - return std -} - -// SetOutput sets the standard logger output. -func SetOutput(out io.Writer) { - std.SetOutput(out) -} - -// SetFormatter sets the standard logger formatter. -func SetFormatter(formatter Formatter) { - std.SetFormatter(formatter) -} - -// SetReportCaller sets whether the standard logger will include the calling -// method as a field. -func SetReportCaller(include bool) { - std.SetReportCaller(include) -} - -// SetLevel sets the standard logger level. -func SetLevel(level Level) { - std.SetLevel(level) -} - -// GetLevel returns the standard logger level. -func GetLevel() Level { - return std.GetLevel() -} - -// IsLevelEnabled checks if the log level of the standard logger is greater than the level param -func IsLevelEnabled(level Level) bool { - return std.IsLevelEnabled(level) -} - -// AddHook adds a hook to the standard logger hooks. -func AddHook(hook Hook) { - std.AddHook(hook) -} - -// WithError creates an entry from the standard logger and adds an error to it, using the value defined in ErrorKey as key. -func WithError(err error) *Entry { - return std.WithField(ErrorKey, err) -} - -// WithContext creates an entry from the standard logger and adds a context to it. -func WithContext(ctx context.Context) *Entry { - return std.WithContext(ctx) -} - -// WithField creates an entry from the standard logger and adds a field to -// it. If you want multiple fields, use `WithFields`. -// -// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal -// or Panic on the Entry it returns. -func WithField(key string, value interface{}) *Entry { - return std.WithField(key, value) -} - -// WithFields creates an entry from the standard logger and adds multiple -// fields to it. This is simply a helper for `WithField`, invoking it -// once for each field. -// -// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal -// or Panic on the Entry it returns. -func WithFields(fields Fields) *Entry { - return std.WithFields(fields) -} - -// WithTime creats an entry from the standard logger and overrides the time of -// logs generated with it. -// -// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal -// or Panic on the Entry it returns. -func WithTime(t time.Time) *Entry { - return std.WithTime(t) -} - -// Trace logs a message at level Trace on the standard logger. -func Trace(args ...interface{}) { - std.Trace(args...) -} - -// Debug logs a message at level Debug on the standard logger. -func Debug(args ...interface{}) { - std.Debug(args...) -} - -// Print logs a message at level Info on the standard logger. -func Print(args ...interface{}) { - std.Print(args...) -} - -// Info logs a message at level Info on the standard logger. -func Info(args ...interface{}) { - std.Info(args...) -} - -// Warn logs a message at level Warn on the standard logger. -func Warn(args ...interface{}) { - std.Warn(args...) -} - -// Warning logs a message at level Warn on the standard logger. -func Warning(args ...interface{}) { - std.Warning(args...) -} - -// Error logs a message at level Error on the standard logger. -func Error(args ...interface{}) { - std.Error(args...) -} - -// Panic logs a message at level Panic on the standard logger. -func Panic(args ...interface{}) { - std.Panic(args...) -} - -// Fatal logs a message at level Fatal on the standard logger then the process will exit with status set to 1. -func Fatal(args ...interface{}) { - std.Fatal(args...) -} - -// Tracef logs a message at level Trace on the standard logger. -func Tracef(format string, args ...interface{}) { - std.Tracef(format, args...) -} - -// Debugf logs a message at level Debug on the standard logger. -func Debugf(format string, args ...interface{}) { - std.Debugf(format, args...) -} - -// Printf logs a message at level Info on the standard logger. -func Printf(format string, args ...interface{}) { - std.Printf(format, args...) -} - -// Infof logs a message at level Info on the standard logger. -func Infof(format string, args ...interface{}) { - std.Infof(format, args...) -} - -// Warnf logs a message at level Warn on the standard logger. -func Warnf(format string, args ...interface{}) { - std.Warnf(format, args...) -} - -// Warningf logs a message at level Warn on the standard logger. -func Warningf(format string, args ...interface{}) { - std.Warningf(format, args...) -} - -// Errorf logs a message at level Error on the standard logger. -func Errorf(format string, args ...interface{}) { - std.Errorf(format, args...) -} - -// Panicf logs a message at level Panic on the standard logger. -func Panicf(format string, args ...interface{}) { - std.Panicf(format, args...) -} - -// Fatalf logs a message at level Fatal on the standard logger then the process will exit with status set to 1. -func Fatalf(format string, args ...interface{}) { - std.Fatalf(format, args...) -} - -// Traceln logs a message at level Trace on the standard logger. -func Traceln(args ...interface{}) { - std.Traceln(args...) -} - -// Debugln logs a message at level Debug on the standard logger. -func Debugln(args ...interface{}) { - std.Debugln(args...) -} - -// Println logs a message at level Info on the standard logger. -func Println(args ...interface{}) { - std.Println(args...) -} - -// Infoln logs a message at level Info on the standard logger. -func Infoln(args ...interface{}) { - std.Infoln(args...) -} - -// Warnln logs a message at level Warn on the standard logger. -func Warnln(args ...interface{}) { - std.Warnln(args...) -} - -// Warningln logs a message at level Warn on the standard logger. -func Warningln(args ...interface{}) { - std.Warningln(args...) -} - -// Errorln logs a message at level Error on the standard logger. -func Errorln(args ...interface{}) { - std.Errorln(args...) -} - -// Panicln logs a message at level Panic on the standard logger. -func Panicln(args ...interface{}) { - std.Panicln(args...) -} - -// Fatalln logs a message at level Fatal on the standard logger then the process will exit with status set to 1. -func Fatalln(args ...interface{}) { - std.Fatalln(args...) -} diff --git a/vendor/github.com/sirupsen/logrus/formatter.go b/vendor/github.com/sirupsen/logrus/formatter.go deleted file mode 100644 index 40888377..00000000 --- a/vendor/github.com/sirupsen/logrus/formatter.go +++ /dev/null @@ -1,78 +0,0 @@ -package logrus - -import "time" - -// Default key names for the default fields -const ( - defaultTimestampFormat = time.RFC3339 - FieldKeyMsg = "msg" - FieldKeyLevel = "level" - FieldKeyTime = "time" - FieldKeyLogrusError = "logrus_error" - FieldKeyFunc = "func" - FieldKeyFile = "file" -) - -// The Formatter interface is used to implement a custom Formatter. It takes an -// `Entry`. It exposes all the fields, including the default ones: -// -// * `entry.Data["msg"]`. The message passed from Info, Warn, Error .. -// * `entry.Data["time"]`. The timestamp. -// * `entry.Data["level"]. The level the entry was logged at. -// -// Any additional fields added with `WithField` or `WithFields` are also in -// `entry.Data`. Format is expected to return an array of bytes which are then -// logged to `logger.Out`. -type Formatter interface { - Format(*Entry) ([]byte, error) -} - -// This is to not silently overwrite `time`, `msg`, `func` and `level` fields when -// dumping it. If this code wasn't there doing: -// -// logrus.WithField("level", 1).Info("hello") -// -// Would just silently drop the user provided level. Instead with this code -// it'll logged as: -// -// {"level": "info", "fields.level": 1, "msg": "hello", "time": "..."} -// -// It's not exported because it's still using Data in an opinionated way. It's to -// avoid code duplication between the two default formatters. -func prefixFieldClashes(data Fields, fieldMap FieldMap, reportCaller bool) { - timeKey := fieldMap.resolve(FieldKeyTime) - if t, ok := data[timeKey]; ok { - data["fields."+timeKey] = t - delete(data, timeKey) - } - - msgKey := fieldMap.resolve(FieldKeyMsg) - if m, ok := data[msgKey]; ok { - data["fields."+msgKey] = m - delete(data, msgKey) - } - - levelKey := fieldMap.resolve(FieldKeyLevel) - if l, ok := data[levelKey]; ok { - data["fields."+levelKey] = l - delete(data, levelKey) - } - - logrusErrKey := fieldMap.resolve(FieldKeyLogrusError) - if l, ok := data[logrusErrKey]; ok { - data["fields."+logrusErrKey] = l - delete(data, logrusErrKey) - } - - // If reportCaller is not set, 'func' will not conflict. - if reportCaller { - funcKey := fieldMap.resolve(FieldKeyFunc) - if l, ok := data[funcKey]; ok { - data["fields."+funcKey] = l - } - fileKey := fieldMap.resolve(FieldKeyFile) - if l, ok := data[fileKey]; ok { - data["fields."+fileKey] = l - } - } -} diff --git a/vendor/github.com/sirupsen/logrus/go.mod b/vendor/github.com/sirupsen/logrus/go.mod deleted file mode 100644 index 12fdf989..00000000 --- a/vendor/github.com/sirupsen/logrus/go.mod +++ /dev/null @@ -1,10 +0,0 @@ -module github.com/sirupsen/logrus - -require ( - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/konsorten/go-windows-terminal-sequences v1.0.1 - github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/stretchr/objx v0.1.1 // indirect - github.com/stretchr/testify v1.2.2 - golang.org/x/sys v0.0.0-20190422165155-953cdadca894 -) diff --git a/vendor/github.com/sirupsen/logrus/go.sum b/vendor/github.com/sirupsen/logrus/go.sum deleted file mode 100644 index 596c318b..00000000 --- a/vendor/github.com/sirupsen/logrus/go.sum +++ /dev/null @@ -1,16 +0,0 @@ -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/konsorten/go-windows-terminal-sequences v0.0.0-20180402223658-b729f2633dfe h1:CHRGQ8V7OlCYtwaKPJi3iA7J+YdNKdo8j7nG5IgDhjs= -github.com/konsorten/go-windows-terminal-sequences v0.0.0-20180402223658-b729f2633dfe/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33 h1:I6FyU15t786LL7oL/hn43zqTuEGr4PN7F4XJ1p4E3Y8= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/vendor/github.com/sirupsen/logrus/hooks.go b/vendor/github.com/sirupsen/logrus/hooks.go deleted file mode 100644 index 3f151cdc..00000000 --- a/vendor/github.com/sirupsen/logrus/hooks.go +++ /dev/null @@ -1,34 +0,0 @@ -package logrus - -// A hook to be fired when logging on the logging levels returned from -// `Levels()` on your implementation of the interface. Note that this is not -// fired in a goroutine or a channel with workers, you should handle such -// functionality yourself if your call is non-blocking and you don't wish for -// the logging calls for levels returned from `Levels()` to block. -type Hook interface { - Levels() []Level - Fire(*Entry) error -} - -// Internal type for storing the hooks on a logger instance. -type LevelHooks map[Level][]Hook - -// Add a hook to an instance of logger. This is called with -// `log.Hooks.Add(new(MyHook))` where `MyHook` implements the `Hook` interface. -func (hooks LevelHooks) Add(hook Hook) { - for _, level := range hook.Levels() { - hooks[level] = append(hooks[level], hook) - } -} - -// Fire all the hooks for the passed level. Used by `entry.log` to fire -// appropriate hooks for a log entry. -func (hooks LevelHooks) Fire(level Level, entry *Entry) error { - for _, hook := range hooks[level] { - if err := hook.Fire(entry); err != nil { - return err - } - } - - return nil -} diff --git a/vendor/github.com/sirupsen/logrus/json_formatter.go b/vendor/github.com/sirupsen/logrus/json_formatter.go deleted file mode 100644 index 098a21a0..00000000 --- a/vendor/github.com/sirupsen/logrus/json_formatter.go +++ /dev/null @@ -1,121 +0,0 @@ -package logrus - -import ( - "bytes" - "encoding/json" - "fmt" - "runtime" -) - -type fieldKey string - -// FieldMap allows customization of the key names for default fields. -type FieldMap map[fieldKey]string - -func (f FieldMap) resolve(key fieldKey) string { - if k, ok := f[key]; ok { - return k - } - - return string(key) -} - -// JSONFormatter formats logs into parsable json -type JSONFormatter struct { - // TimestampFormat sets the format used for marshaling timestamps. - TimestampFormat string - - // DisableTimestamp allows disabling automatic timestamps in output - DisableTimestamp bool - - // DataKey allows users to put all the log entry parameters into a nested dictionary at a given key. - DataKey string - - // FieldMap allows users to customize the names of keys for default fields. - // As an example: - // formatter := &JSONFormatter{ - // FieldMap: FieldMap{ - // FieldKeyTime: "@timestamp", - // FieldKeyLevel: "@level", - // FieldKeyMsg: "@message", - // FieldKeyFunc: "@caller", - // }, - // } - FieldMap FieldMap - - // CallerPrettyfier can be set by the user to modify the content - // of the function and file keys in the json data when ReportCaller is - // activated. If any of the returned value is the empty string the - // corresponding key will be removed from json fields. - CallerPrettyfier func(*runtime.Frame) (function string, file string) - - // PrettyPrint will indent all json logs - PrettyPrint bool -} - -// Format renders a single log entry -func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { - data := make(Fields, len(entry.Data)+4) - for k, v := range entry.Data { - switch v := v.(type) { - case error: - // Otherwise errors are ignored by `encoding/json` - // https://github.com/sirupsen/logrus/issues/137 - data[k] = v.Error() - default: - data[k] = v - } - } - - if f.DataKey != "" { - newData := make(Fields, 4) - newData[f.DataKey] = data - data = newData - } - - prefixFieldClashes(data, f.FieldMap, entry.HasCaller()) - - timestampFormat := f.TimestampFormat - if timestampFormat == "" { - timestampFormat = defaultTimestampFormat - } - - if entry.err != "" { - data[f.FieldMap.resolve(FieldKeyLogrusError)] = entry.err - } - if !f.DisableTimestamp { - data[f.FieldMap.resolve(FieldKeyTime)] = entry.Time.Format(timestampFormat) - } - data[f.FieldMap.resolve(FieldKeyMsg)] = entry.Message - data[f.FieldMap.resolve(FieldKeyLevel)] = entry.Level.String() - if entry.HasCaller() { - funcVal := entry.Caller.Function - fileVal := fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line) - if f.CallerPrettyfier != nil { - funcVal, fileVal = f.CallerPrettyfier(entry.Caller) - } - if funcVal != "" { - data[f.FieldMap.resolve(FieldKeyFunc)] = funcVal - } - if fileVal != "" { - data[f.FieldMap.resolve(FieldKeyFile)] = fileVal - } - } - - var b *bytes.Buffer - if entry.Buffer != nil { - b = entry.Buffer - } else { - b = &bytes.Buffer{} - } - - encoder := json.NewEncoder(b) - if f.PrettyPrint { - encoder.SetIndent("", " ") - } - if err := encoder.Encode(data); err != nil { - return nil, fmt.Errorf("failed to marshal fields to JSON, %v", err) - } - - return b.Bytes(), nil -} diff --git a/vendor/github.com/sirupsen/logrus/logger.go b/vendor/github.com/sirupsen/logrus/logger.go deleted file mode 100644 index c0c0b1e5..00000000 --- a/vendor/github.com/sirupsen/logrus/logger.go +++ /dev/null @@ -1,351 +0,0 @@ -package logrus - -import ( - "context" - "io" - "os" - "sync" - "sync/atomic" - "time" -) - -type Logger struct { - // The logs are `io.Copy`'d to this in a mutex. It's common to set this to a - // file, or leave it default which is `os.Stderr`. You can also set this to - // something more adventurous, such as logging to Kafka. - Out io.Writer - // Hooks for the logger instance. These allow firing events based on logging - // levels and log entries. For example, to send errors to an error tracking - // service, log to StatsD or dump the core on fatal errors. - Hooks LevelHooks - // All log entries pass through the formatter before logged to Out. The - // included formatters are `TextFormatter` and `JSONFormatter` for which - // TextFormatter is the default. In development (when a TTY is attached) it - // logs with colors, but to a file it wouldn't. You can easily implement your - // own that implements the `Formatter` interface, see the `README` or included - // formatters for examples. - Formatter Formatter - - // Flag for whether to log caller info (off by default) - ReportCaller bool - - // The logging level the logger should log at. This is typically (and defaults - // to) `logrus.Info`, which allows Info(), Warn(), Error() and Fatal() to be - // logged. - Level Level - // Used to sync writing to the log. Locking is enabled by Default - mu MutexWrap - // Reusable empty entry - entryPool sync.Pool - // Function to exit the application, defaults to `os.Exit()` - ExitFunc exitFunc -} - -type exitFunc func(int) - -type MutexWrap struct { - lock sync.Mutex - disabled bool -} - -func (mw *MutexWrap) Lock() { - if !mw.disabled { - mw.lock.Lock() - } -} - -func (mw *MutexWrap) Unlock() { - if !mw.disabled { - mw.lock.Unlock() - } -} - -func (mw *MutexWrap) Disable() { - mw.disabled = true -} - -// Creates a new logger. Configuration should be set by changing `Formatter`, -// `Out` and `Hooks` directly on the default logger instance. You can also just -// instantiate your own: -// -// var log = &Logger{ -// Out: os.Stderr, -// Formatter: new(JSONFormatter), -// Hooks: make(LevelHooks), -// Level: logrus.DebugLevel, -// } -// -// It's recommended to make this a global instance called `log`. -func New() *Logger { - return &Logger{ - Out: os.Stderr, - Formatter: new(TextFormatter), - Hooks: make(LevelHooks), - Level: InfoLevel, - ExitFunc: os.Exit, - ReportCaller: false, - } -} - -func (logger *Logger) newEntry() *Entry { - entry, ok := logger.entryPool.Get().(*Entry) - if ok { - return entry - } - return NewEntry(logger) -} - -func (logger *Logger) releaseEntry(entry *Entry) { - entry.Data = map[string]interface{}{} - logger.entryPool.Put(entry) -} - -// Adds a field to the log entry, note that it doesn't log until you call -// Debug, Print, Info, Warn, Error, Fatal or Panic. It only creates a log entry. -// If you want multiple fields, use `WithFields`. -func (logger *Logger) WithField(key string, value interface{}) *Entry { - entry := logger.newEntry() - defer logger.releaseEntry(entry) - return entry.WithField(key, value) -} - -// Adds a struct of fields to the log entry. All it does is call `WithField` for -// each `Field`. -func (logger *Logger) WithFields(fields Fields) *Entry { - entry := logger.newEntry() - defer logger.releaseEntry(entry) - return entry.WithFields(fields) -} - -// Add an error as single field to the log entry. All it does is call -// `WithError` for the given `error`. -func (logger *Logger) WithError(err error) *Entry { - entry := logger.newEntry() - defer logger.releaseEntry(entry) - return entry.WithError(err) -} - -// Add a context to the log entry. -func (logger *Logger) WithContext(ctx context.Context) *Entry { - entry := logger.newEntry() - defer logger.releaseEntry(entry) - return entry.WithContext(ctx) -} - -// Overrides the time of the log entry. -func (logger *Logger) WithTime(t time.Time) *Entry { - entry := logger.newEntry() - defer logger.releaseEntry(entry) - return entry.WithTime(t) -} - -func (logger *Logger) Logf(level Level, format string, args ...interface{}) { - if logger.IsLevelEnabled(level) { - entry := logger.newEntry() - entry.Logf(level, format, args...) - logger.releaseEntry(entry) - } -} - -func (logger *Logger) Tracef(format string, args ...interface{}) { - logger.Logf(TraceLevel, format, args...) -} - -func (logger *Logger) Debugf(format string, args ...interface{}) { - logger.Logf(DebugLevel, format, args...) -} - -func (logger *Logger) Infof(format string, args ...interface{}) { - logger.Logf(InfoLevel, format, args...) -} - -func (logger *Logger) Printf(format string, args ...interface{}) { - entry := logger.newEntry() - entry.Printf(format, args...) - logger.releaseEntry(entry) -} - -func (logger *Logger) Warnf(format string, args ...interface{}) { - logger.Logf(WarnLevel, format, args...) -} - -func (logger *Logger) Warningf(format string, args ...interface{}) { - logger.Warnf(format, args...) -} - -func (logger *Logger) Errorf(format string, args ...interface{}) { - logger.Logf(ErrorLevel, format, args...) -} - -func (logger *Logger) Fatalf(format string, args ...interface{}) { - logger.Logf(FatalLevel, format, args...) - logger.Exit(1) -} - -func (logger *Logger) Panicf(format string, args ...interface{}) { - logger.Logf(PanicLevel, format, args...) -} - -func (logger *Logger) Log(level Level, args ...interface{}) { - if logger.IsLevelEnabled(level) { - entry := logger.newEntry() - entry.Log(level, args...) - logger.releaseEntry(entry) - } -} - -func (logger *Logger) Trace(args ...interface{}) { - logger.Log(TraceLevel, args...) -} - -func (logger *Logger) Debug(args ...interface{}) { - logger.Log(DebugLevel, args...) -} - -func (logger *Logger) Info(args ...interface{}) { - logger.Log(InfoLevel, args...) -} - -func (logger *Logger) Print(args ...interface{}) { - entry := logger.newEntry() - entry.Print(args...) - logger.releaseEntry(entry) -} - -func (logger *Logger) Warn(args ...interface{}) { - logger.Log(WarnLevel, args...) -} - -func (logger *Logger) Warning(args ...interface{}) { - logger.Warn(args...) -} - -func (logger *Logger) Error(args ...interface{}) { - logger.Log(ErrorLevel, args...) -} - -func (logger *Logger) Fatal(args ...interface{}) { - logger.Log(FatalLevel, args...) - logger.Exit(1) -} - -func (logger *Logger) Panic(args ...interface{}) { - logger.Log(PanicLevel, args...) -} - -func (logger *Logger) Logln(level Level, args ...interface{}) { - if logger.IsLevelEnabled(level) { - entry := logger.newEntry() - entry.Logln(level, args...) - logger.releaseEntry(entry) - } -} - -func (logger *Logger) Traceln(args ...interface{}) { - logger.Logln(TraceLevel, args...) -} - -func (logger *Logger) Debugln(args ...interface{}) { - logger.Logln(DebugLevel, args...) -} - -func (logger *Logger) Infoln(args ...interface{}) { - logger.Logln(InfoLevel, args...) -} - -func (logger *Logger) Println(args ...interface{}) { - entry := logger.newEntry() - entry.Println(args...) - logger.releaseEntry(entry) -} - -func (logger *Logger) Warnln(args ...interface{}) { - logger.Logln(WarnLevel, args...) -} - -func (logger *Logger) Warningln(args ...interface{}) { - logger.Warnln(args...) -} - -func (logger *Logger) Errorln(args ...interface{}) { - logger.Logln(ErrorLevel, args...) -} - -func (logger *Logger) Fatalln(args ...interface{}) { - logger.Logln(FatalLevel, args...) - logger.Exit(1) -} - -func (logger *Logger) Panicln(args ...interface{}) { - logger.Logln(PanicLevel, args...) -} - -func (logger *Logger) Exit(code int) { - runHandlers() - if logger.ExitFunc == nil { - logger.ExitFunc = os.Exit - } - logger.ExitFunc(code) -} - -//When file is opened with appending mode, it's safe to -//write concurrently to a file (within 4k message on Linux). -//In these cases user can choose to disable the lock. -func (logger *Logger) SetNoLock() { - logger.mu.Disable() -} - -func (logger *Logger) level() Level { - return Level(atomic.LoadUint32((*uint32)(&logger.Level))) -} - -// SetLevel sets the logger level. -func (logger *Logger) SetLevel(level Level) { - atomic.StoreUint32((*uint32)(&logger.Level), uint32(level)) -} - -// GetLevel returns the logger level. -func (logger *Logger) GetLevel() Level { - return logger.level() -} - -// AddHook adds a hook to the logger hooks. -func (logger *Logger) AddHook(hook Hook) { - logger.mu.Lock() - defer logger.mu.Unlock() - logger.Hooks.Add(hook) -} - -// IsLevelEnabled checks if the log level of the logger is greater than the level param -func (logger *Logger) IsLevelEnabled(level Level) bool { - return logger.level() >= level -} - -// SetFormatter sets the logger formatter. -func (logger *Logger) SetFormatter(formatter Formatter) { - logger.mu.Lock() - defer logger.mu.Unlock() - logger.Formatter = formatter -} - -// SetOutput sets the logger output. -func (logger *Logger) SetOutput(output io.Writer) { - logger.mu.Lock() - defer logger.mu.Unlock() - logger.Out = output -} - -func (logger *Logger) SetReportCaller(reportCaller bool) { - logger.mu.Lock() - defer logger.mu.Unlock() - logger.ReportCaller = reportCaller -} - -// ReplaceHooks replaces the logger hooks and returns the old ones -func (logger *Logger) ReplaceHooks(hooks LevelHooks) LevelHooks { - logger.mu.Lock() - oldHooks := logger.Hooks - logger.Hooks = hooks - logger.mu.Unlock() - return oldHooks -} diff --git a/vendor/github.com/sirupsen/logrus/logrus.go b/vendor/github.com/sirupsen/logrus/logrus.go deleted file mode 100644 index 8644761f..00000000 --- a/vendor/github.com/sirupsen/logrus/logrus.go +++ /dev/null @@ -1,186 +0,0 @@ -package logrus - -import ( - "fmt" - "log" - "strings" -) - -// Fields type, used to pass to `WithFields`. -type Fields map[string]interface{} - -// Level type -type Level uint32 - -// Convert the Level to a string. E.g. PanicLevel becomes "panic". -func (level Level) String() string { - if b, err := level.MarshalText(); err == nil { - return string(b) - } else { - return "unknown" - } -} - -// ParseLevel takes a string level and returns the Logrus log level constant. -func ParseLevel(lvl string) (Level, error) { - switch strings.ToLower(lvl) { - case "panic": - return PanicLevel, nil - case "fatal": - return FatalLevel, nil - case "error": - return ErrorLevel, nil - case "warn", "warning": - return WarnLevel, nil - case "info": - return InfoLevel, nil - case "debug": - return DebugLevel, nil - case "trace": - return TraceLevel, nil - } - - var l Level - return l, fmt.Errorf("not a valid logrus Level: %q", lvl) -} - -// UnmarshalText implements encoding.TextUnmarshaler. -func (level *Level) UnmarshalText(text []byte) error { - l, err := ParseLevel(string(text)) - if err != nil { - return err - } - - *level = Level(l) - - return nil -} - -func (level Level) MarshalText() ([]byte, error) { - switch level { - case TraceLevel: - return []byte("trace"), nil - case DebugLevel: - return []byte("debug"), nil - case InfoLevel: - return []byte("info"), nil - case WarnLevel: - return []byte("warning"), nil - case ErrorLevel: - return []byte("error"), nil - case FatalLevel: - return []byte("fatal"), nil - case PanicLevel: - return []byte("panic"), nil - } - - return nil, fmt.Errorf("not a valid logrus level %d", level) -} - -// A constant exposing all logging levels -var AllLevels = []Level{ - PanicLevel, - FatalLevel, - ErrorLevel, - WarnLevel, - InfoLevel, - DebugLevel, - TraceLevel, -} - -// These are the different logging levels. You can set the logging level to log -// on your instance of logger, obtained with `logrus.New()`. -const ( - // PanicLevel level, highest level of severity. Logs and then calls panic with the - // message passed to Debug, Info, ... - PanicLevel Level = iota - // FatalLevel level. Logs and then calls `logger.Exit(1)`. It will exit even if the - // logging level is set to Panic. - FatalLevel - // ErrorLevel level. Logs. Used for errors that should definitely be noted. - // Commonly used for hooks to send errors to an error tracking service. - ErrorLevel - // WarnLevel level. Non-critical entries that deserve eyes. - WarnLevel - // InfoLevel level. General operational entries about what's going on inside the - // application. - InfoLevel - // DebugLevel level. Usually only enabled when debugging. Very verbose logging. - DebugLevel - // TraceLevel level. Designates finer-grained informational events than the Debug. - TraceLevel -) - -// Won't compile if StdLogger can't be realized by a log.Logger -var ( - _ StdLogger = &log.Logger{} - _ StdLogger = &Entry{} - _ StdLogger = &Logger{} -) - -// StdLogger is what your logrus-enabled library should take, that way -// it'll accept a stdlib logger and a logrus logger. There's no standard -// interface, this is the closest we get, unfortunately. -type StdLogger interface { - Print(...interface{}) - Printf(string, ...interface{}) - Println(...interface{}) - - Fatal(...interface{}) - Fatalf(string, ...interface{}) - Fatalln(...interface{}) - - Panic(...interface{}) - Panicf(string, ...interface{}) - Panicln(...interface{}) -} - -// The FieldLogger interface generalizes the Entry and Logger types -type FieldLogger interface { - WithField(key string, value interface{}) *Entry - WithFields(fields Fields) *Entry - WithError(err error) *Entry - - Debugf(format string, args ...interface{}) - Infof(format string, args ...interface{}) - Printf(format string, args ...interface{}) - Warnf(format string, args ...interface{}) - Warningf(format string, args ...interface{}) - Errorf(format string, args ...interface{}) - Fatalf(format string, args ...interface{}) - Panicf(format string, args ...interface{}) - - Debug(args ...interface{}) - Info(args ...interface{}) - Print(args ...interface{}) - Warn(args ...interface{}) - Warning(args ...interface{}) - Error(args ...interface{}) - Fatal(args ...interface{}) - Panic(args ...interface{}) - - Debugln(args ...interface{}) - Infoln(args ...interface{}) - Println(args ...interface{}) - Warnln(args ...interface{}) - Warningln(args ...interface{}) - Errorln(args ...interface{}) - Fatalln(args ...interface{}) - Panicln(args ...interface{}) - - // IsDebugEnabled() bool - // IsInfoEnabled() bool - // IsWarnEnabled() bool - // IsErrorEnabled() bool - // IsFatalEnabled() bool - // IsPanicEnabled() bool -} - -// Ext1FieldLogger (the first extension to FieldLogger) is superfluous, it is -// here for consistancy. Do not use. Use Logger or Entry instead. -type Ext1FieldLogger interface { - FieldLogger - Tracef(format string, args ...interface{}) - Trace(args ...interface{}) - Traceln(args ...interface{}) -} diff --git a/vendor/github.com/sirupsen/logrus/terminal_check_appengine.go b/vendor/github.com/sirupsen/logrus/terminal_check_appengine.go deleted file mode 100644 index 2403de98..00000000 --- a/vendor/github.com/sirupsen/logrus/terminal_check_appengine.go +++ /dev/null @@ -1,11 +0,0 @@ -// +build appengine - -package logrus - -import ( - "io" -) - -func checkIfTerminal(w io.Writer) bool { - return true -} diff --git a/vendor/github.com/sirupsen/logrus/terminal_check_bsd.go b/vendor/github.com/sirupsen/logrus/terminal_check_bsd.go deleted file mode 100644 index 3c4f43f9..00000000 --- a/vendor/github.com/sirupsen/logrus/terminal_check_bsd.go +++ /dev/null @@ -1,13 +0,0 @@ -// +build darwin dragonfly freebsd netbsd openbsd - -package logrus - -import "golang.org/x/sys/unix" - -const ioctlReadTermios = unix.TIOCGETA - -func isTerminal(fd int) bool { - _, err := unix.IoctlGetTermios(fd, ioctlReadTermios) - return err == nil -} - diff --git a/vendor/github.com/sirupsen/logrus/terminal_check_no_terminal.go b/vendor/github.com/sirupsen/logrus/terminal_check_no_terminal.go deleted file mode 100644 index 97af92c6..00000000 --- a/vendor/github.com/sirupsen/logrus/terminal_check_no_terminal.go +++ /dev/null @@ -1,11 +0,0 @@ -// +build js nacl plan9 - -package logrus - -import ( - "io" -) - -func checkIfTerminal(w io.Writer) bool { - return false -} diff --git a/vendor/github.com/sirupsen/logrus/terminal_check_notappengine.go b/vendor/github.com/sirupsen/logrus/terminal_check_notappengine.go deleted file mode 100644 index 3293fb3c..00000000 --- a/vendor/github.com/sirupsen/logrus/terminal_check_notappengine.go +++ /dev/null @@ -1,17 +0,0 @@ -// +build !appengine,!js,!windows,!nacl,!plan9 - -package logrus - -import ( - "io" - "os" -) - -func checkIfTerminal(w io.Writer) bool { - switch v := w.(type) { - case *os.File: - return isTerminal(int(v.Fd())) - default: - return false - } -} diff --git a/vendor/github.com/sirupsen/logrus/terminal_check_solaris.go b/vendor/github.com/sirupsen/logrus/terminal_check_solaris.go deleted file mode 100644 index f6710b3b..00000000 --- a/vendor/github.com/sirupsen/logrus/terminal_check_solaris.go +++ /dev/null @@ -1,11 +0,0 @@ -package logrus - -import ( - "golang.org/x/sys/unix" -) - -// IsTerminal returns true if the given file descriptor is a terminal. -func isTerminal(fd int) bool { - _, err := unix.IoctlGetTermio(fd, unix.TCGETA) - return err == nil -} diff --git a/vendor/github.com/sirupsen/logrus/terminal_check_unix.go b/vendor/github.com/sirupsen/logrus/terminal_check_unix.go deleted file mode 100644 index 355dc966..00000000 --- a/vendor/github.com/sirupsen/logrus/terminal_check_unix.go +++ /dev/null @@ -1,13 +0,0 @@ -// +build linux aix - -package logrus - -import "golang.org/x/sys/unix" - -const ioctlReadTermios = unix.TCGETS - -func isTerminal(fd int) bool { - _, err := unix.IoctlGetTermios(fd, ioctlReadTermios) - return err == nil -} - diff --git a/vendor/github.com/sirupsen/logrus/terminal_check_windows.go b/vendor/github.com/sirupsen/logrus/terminal_check_windows.go deleted file mode 100644 index 572889db..00000000 --- a/vendor/github.com/sirupsen/logrus/terminal_check_windows.go +++ /dev/null @@ -1,34 +0,0 @@ -// +build !appengine,!js,windows - -package logrus - -import ( - "io" - "os" - "syscall" - - sequences "github.com/konsorten/go-windows-terminal-sequences" -) - -func initTerminal(w io.Writer) { - switch v := w.(type) { - case *os.File: - sequences.EnableVirtualTerminalProcessing(syscall.Handle(v.Fd()), true) - } -} - -func checkIfTerminal(w io.Writer) bool { - var ret bool - switch v := w.(type) { - case *os.File: - var mode uint32 - err := syscall.GetConsoleMode(syscall.Handle(v.Fd()), &mode) - ret = (err == nil) - default: - ret = false - } - if ret { - initTerminal(w) - } - return ret -} diff --git a/vendor/github.com/sirupsen/logrus/text_formatter.go b/vendor/github.com/sirupsen/logrus/text_formatter.go deleted file mode 100644 index e01587c4..00000000 --- a/vendor/github.com/sirupsen/logrus/text_formatter.go +++ /dev/null @@ -1,295 +0,0 @@ -package logrus - -import ( - "bytes" - "fmt" - "os" - "runtime" - "sort" - "strings" - "sync" - "time" -) - -const ( - red = 31 - yellow = 33 - blue = 36 - gray = 37 -) - -var baseTimestamp time.Time - -func init() { - baseTimestamp = time.Now() -} - -// TextFormatter formats logs into text -type TextFormatter struct { - // Set to true to bypass checking for a TTY before outputting colors. - ForceColors bool - - // Force disabling colors. - DisableColors bool - - // Override coloring based on CLICOLOR and CLICOLOR_FORCE. - https://bixense.com/clicolors/ - EnvironmentOverrideColors bool - - // Disable timestamp logging. useful when output is redirected to logging - // system that already adds timestamps. - DisableTimestamp bool - - // Enable logging the full timestamp when a TTY is attached instead of just - // the time passed since beginning of execution. - FullTimestamp bool - - // TimestampFormat to use for display when a full timestamp is printed - TimestampFormat string - - // The fields are sorted by default for a consistent output. For applications - // that log extremely frequently and don't use the JSON formatter this may not - // be desired. - DisableSorting bool - - // The keys sorting function, when uninitialized it uses sort.Strings. - SortingFunc func([]string) - - // Disables the truncation of the level text to 4 characters. - DisableLevelTruncation bool - - // QuoteEmptyFields will wrap empty fields in quotes if true - QuoteEmptyFields bool - - // Whether the logger's out is to a terminal - isTerminal bool - - // FieldMap allows users to customize the names of keys for default fields. - // As an example: - // formatter := &TextFormatter{ - // FieldMap: FieldMap{ - // FieldKeyTime: "@timestamp", - // FieldKeyLevel: "@level", - // FieldKeyMsg: "@message"}} - FieldMap FieldMap - - // CallerPrettyfier can be set by the user to modify the content - // of the function and file keys in the data when ReportCaller is - // activated. If any of the returned value is the empty string the - // corresponding key will be removed from fields. - CallerPrettyfier func(*runtime.Frame) (function string, file string) - - terminalInitOnce sync.Once -} - -func (f *TextFormatter) init(entry *Entry) { - if entry.Logger != nil { - f.isTerminal = checkIfTerminal(entry.Logger.Out) - } -} - -func (f *TextFormatter) isColored() bool { - isColored := f.ForceColors || (f.isTerminal && (runtime.GOOS != "windows")) - - if f.EnvironmentOverrideColors { - if force, ok := os.LookupEnv("CLICOLOR_FORCE"); ok && force != "0" { - isColored = true - } else if ok && force == "0" { - isColored = false - } else if os.Getenv("CLICOLOR") == "0" { - isColored = false - } - } - - return isColored && !f.DisableColors -} - -// Format renders a single log entry -func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { - data := make(Fields) - for k, v := range entry.Data { - data[k] = v - } - prefixFieldClashes(data, f.FieldMap, entry.HasCaller()) - keys := make([]string, 0, len(data)) - for k := range data { - keys = append(keys, k) - } - - var funcVal, fileVal string - - fixedKeys := make([]string, 0, 4+len(data)) - if !f.DisableTimestamp { - fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyTime)) - } - fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyLevel)) - if entry.Message != "" { - fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyMsg)) - } - if entry.err != "" { - fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyLogrusError)) - } - if entry.HasCaller() { - if f.CallerPrettyfier != nil { - funcVal, fileVal = f.CallerPrettyfier(entry.Caller) - } else { - funcVal = entry.Caller.Function - fileVal = fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line) - } - - if funcVal != "" { - fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyFunc)) - } - if fileVal != "" { - fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyFile)) - } - } - - if !f.DisableSorting { - if f.SortingFunc == nil { - sort.Strings(keys) - fixedKeys = append(fixedKeys, keys...) - } else { - if !f.isColored() { - fixedKeys = append(fixedKeys, keys...) - f.SortingFunc(fixedKeys) - } else { - f.SortingFunc(keys) - } - } - } else { - fixedKeys = append(fixedKeys, keys...) - } - - var b *bytes.Buffer - if entry.Buffer != nil { - b = entry.Buffer - } else { - b = &bytes.Buffer{} - } - - f.terminalInitOnce.Do(func() { f.init(entry) }) - - timestampFormat := f.TimestampFormat - if timestampFormat == "" { - timestampFormat = defaultTimestampFormat - } - if f.isColored() { - f.printColored(b, entry, keys, data, timestampFormat) - } else { - - for _, key := range fixedKeys { - var value interface{} - switch { - case key == f.FieldMap.resolve(FieldKeyTime): - value = entry.Time.Format(timestampFormat) - case key == f.FieldMap.resolve(FieldKeyLevel): - value = entry.Level.String() - case key == f.FieldMap.resolve(FieldKeyMsg): - value = entry.Message - case key == f.FieldMap.resolve(FieldKeyLogrusError): - value = entry.err - case key == f.FieldMap.resolve(FieldKeyFunc) && entry.HasCaller(): - value = funcVal - case key == f.FieldMap.resolve(FieldKeyFile) && entry.HasCaller(): - value = fileVal - default: - value = data[key] - } - f.appendKeyValue(b, key, value) - } - } - - b.WriteByte('\n') - return b.Bytes(), nil -} - -func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []string, data Fields, timestampFormat string) { - var levelColor int - switch entry.Level { - case DebugLevel, TraceLevel: - levelColor = gray - case WarnLevel: - levelColor = yellow - case ErrorLevel, FatalLevel, PanicLevel: - levelColor = red - default: - levelColor = blue - } - - levelText := strings.ToUpper(entry.Level.String()) - if !f.DisableLevelTruncation { - levelText = levelText[0:4] - } - - // Remove a single newline if it already exists in the message to keep - // the behavior of logrus text_formatter the same as the stdlib log package - entry.Message = strings.TrimSuffix(entry.Message, "\n") - - caller := "" - if entry.HasCaller() { - funcVal := fmt.Sprintf("%s()", entry.Caller.Function) - fileVal := fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line) - - if f.CallerPrettyfier != nil { - funcVal, fileVal = f.CallerPrettyfier(entry.Caller) - } - - if fileVal == "" { - caller = funcVal - } else if funcVal == "" { - caller = fileVal - } else { - caller = fileVal + " " + funcVal - } - } - - if f.DisableTimestamp { - fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m%s %-44s ", levelColor, levelText, caller, entry.Message) - } else if !f.FullTimestamp { - fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d]%s %-44s ", levelColor, levelText, int(entry.Time.Sub(baseTimestamp)/time.Second), caller, entry.Message) - } else { - fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s]%s %-44s ", levelColor, levelText, entry.Time.Format(timestampFormat), caller, entry.Message) - } - for _, k := range keys { - v := data[k] - fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=", levelColor, k) - f.appendValue(b, v) - } -} - -func (f *TextFormatter) needsQuoting(text string) bool { - if f.QuoteEmptyFields && len(text) == 0 { - return true - } - for _, ch := range text { - if !((ch >= 'a' && ch <= 'z') || - (ch >= 'A' && ch <= 'Z') || - (ch >= '0' && ch <= '9') || - ch == '-' || ch == '.' || ch == '_' || ch == '/' || ch == '@' || ch == '^' || ch == '+') { - return true - } - } - return false -} - -func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key string, value interface{}) { - if b.Len() > 0 { - b.WriteByte(' ') - } - b.WriteString(key) - b.WriteByte('=') - f.appendValue(b, value) -} - -func (f *TextFormatter) appendValue(b *bytes.Buffer, value interface{}) { - stringVal, ok := value.(string) - if !ok { - stringVal = fmt.Sprint(value) - } - - if !f.needsQuoting(stringVal) { - b.WriteString(stringVal) - } else { - b.WriteString(fmt.Sprintf("%q", stringVal)) - } -} diff --git a/vendor/github.com/sirupsen/logrus/writer.go b/vendor/github.com/sirupsen/logrus/writer.go deleted file mode 100644 index 9e1f7513..00000000 --- a/vendor/github.com/sirupsen/logrus/writer.go +++ /dev/null @@ -1,64 +0,0 @@ -package logrus - -import ( - "bufio" - "io" - "runtime" -) - -func (logger *Logger) Writer() *io.PipeWriter { - return logger.WriterLevel(InfoLevel) -} - -func (logger *Logger) WriterLevel(level Level) *io.PipeWriter { - return NewEntry(logger).WriterLevel(level) -} - -func (entry *Entry) Writer() *io.PipeWriter { - return entry.WriterLevel(InfoLevel) -} - -func (entry *Entry) WriterLevel(level Level) *io.PipeWriter { - reader, writer := io.Pipe() - - var printFunc func(args ...interface{}) - - switch level { - case TraceLevel: - printFunc = entry.Trace - case DebugLevel: - printFunc = entry.Debug - case InfoLevel: - printFunc = entry.Info - case WarnLevel: - printFunc = entry.Warn - case ErrorLevel: - printFunc = entry.Error - case FatalLevel: - printFunc = entry.Fatal - case PanicLevel: - printFunc = entry.Panic - default: - printFunc = entry.Print - } - - go entry.writerScanner(reader, printFunc) - runtime.SetFinalizer(writer, writerFinalizer) - - return writer -} - -func (entry *Entry) writerScanner(reader *io.PipeReader, printFunc func(args ...interface{})) { - scanner := bufio.NewScanner(reader) - for scanner.Scan() { - printFunc(scanner.Text()) - } - if err := scanner.Err(); err != nil { - entry.Errorf("Error while reading from Writer: %s", err) - } - reader.Close() -} - -func writerFinalizer(writer *io.PipeWriter) { - writer.Close() -} diff --git a/vendor/modules.txt b/vendor/modules.txt index ac92dbe5..307f6033 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -2,6 +2,10 @@ github.com/BurntSushi/toml # github.com/DATA-DOG/go-sqlmock v1.3.3 github.com/DATA-DOG/go-sqlmock +# github.com/acmacalister/skittles v0.0.0-20160609003031-7423546701e1 +github.com/acmacalister/skittles +# github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4 +github.com/alecthomas/units # github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 github.com/anmitsu/go-shlex # github.com/aws/aws-sdk-go v1.25.8 @@ -52,9 +56,6 @@ github.com/cloudflare/brotli-go/brotli github.com/cloudflare/brotli-go/common github.com/cloudflare/brotli-go/dec github.com/cloudflare/brotli-go/enc -# github.com/cloudflare/cfssl v0.0.0-20141119014638-2f7f44e802e2 -github.com/cloudflare/cfssl/log -github.com/cloudflare/cfssl/revoke # github.com/cloudflare/golibs v0.0.0-20170913112048-333127dbecfc github.com/cloudflare/golibs/lrucache # github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58 @@ -137,8 +138,6 @@ github.com/jmespath/go-jmespath # github.com/jmoiron/sqlx v1.2.0 github.com/jmoiron/sqlx github.com/jmoiron/sqlx/reflectx -# github.com/konsorten/go-windows-terminal-sequences v1.0.2 -github.com/konsorten/go-windows-terminal-sequences # github.com/kshvakov/clickhouse v1.3.11 github.com/kshvakov/clickhouse github.com/kshvakov/clickhouse/lib/binary @@ -154,10 +153,6 @@ github.com/kshvakov/clickhouse/lib/writebuffer github.com/lib/pq github.com/lib/pq/oid github.com/lib/pq/scram -# github.com/mattn/go-colorable v0.1.4 -github.com/mattn/go-colorable -# github.com/mattn/go-isatty v0.0.10 -github.com/mattn/go-isatty # github.com/mattn/go-sqlite3 v1.11.0 github.com/mattn/go-sqlite3 # github.com/matttproud/golang_protobuf_extensions v1.0.1 @@ -194,10 +189,6 @@ github.com/prometheus/common/model github.com/prometheus/procfs github.com/prometheus/procfs/internal/fs github.com/prometheus/procfs/internal/util -# github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5 -github.com/rifflock/lfshook -# github.com/sirupsen/logrus v1.4.2 -github.com/sirupsen/logrus # github.com/stretchr/testify v1.3.0 github.com/stretchr/testify/assert github.com/stretchr/testify/require diff --git a/watcher/file_test.go b/watcher/file_test.go index ab09fe42..93f57769 100644 --- a/watcher/file_test.go +++ b/watcher/file_test.go @@ -47,7 +47,7 @@ func TestFileChanged(t *testing.T) { assert.NoError(t, err) // give it time to trigger - time.Sleep(10 * time.Millisecond) + time.Sleep(20 * time.Millisecond) service.Shutdown() assert.Equal(t, filePath, n.eventPath, "notifier didn't get an new file write event") diff --git a/websocket/websocket.go b/websocket/websocket.go index 31d7f3d8..961a10f9 100644 --- a/websocket/websocket.go +++ b/websocket/websocket.go @@ -14,9 +14,9 @@ import ( "time" "github.com/cloudflare/cloudflared/h2mux" + "github.com/cloudflare/cloudflared/logger" "github.com/cloudflare/cloudflared/sshserver" "github.com/gorilla/websocket" - "github.com/sirupsen/logrus" ) const ( @@ -125,7 +125,7 @@ func DefaultStreamHandler(wsConn *Conn, remoteConn net.Conn, _ http.Header) { // StartProxyServer will start a websocket server that will decode // the websocket data and write the resulting data to the provided -func StartProxyServer(logger *logrus.Logger, listener net.Listener, staticHost string, shutdownC <-chan struct{}, streamHandler func(wsConn *Conn, remoteConn net.Conn, requestHeaders http.Header)) error { +func StartProxyServer(logger logger.Service, listener net.Listener, staticHost string, shutdownC <-chan struct{}, streamHandler func(wsConn *Conn, remoteConn net.Conn, requestHeaders http.Header)) error { upgrader := websocket.Upgrader{ ReadBufferSize: 1024, WriteBufferSize: 1024, @@ -151,7 +151,7 @@ func StartProxyServer(logger *logrus.Logger, listener net.Listener, staticHost s stream, err := net.Dial("tcp", finalDestination) if err != nil { - logger.WithError(err).Error("Cannot connect to remote.") + logger.Errorf("Cannot connect to remote: %s", err) return } defer stream.Close() @@ -162,7 +162,7 @@ func StartProxyServer(logger *logrus.Logger, listener net.Listener, staticHost s } conn, err := upgrader.Upgrade(w, r, nil) if err != nil { - logger.WithError(err).Error("failed to upgrade") + logger.Errorf("failed to upgrade: %s", err) return } conn.SetReadDeadline(time.Now().Add(pongWait)) @@ -250,14 +250,14 @@ func changeRequestScheme(req *http.Request) string { } // pinger simulates the websocket connection to keep it alive -func pinger(logger *logrus.Logger, ws *websocket.Conn, done chan struct{}) { +func pinger(logger logger.Service, ws *websocket.Conn, done chan struct{}) { ticker := time.NewTicker(pingPeriod) defer ticker.Stop() for { select { case <-ticker.C: if err := ws.WriteControl(websocket.PingMessage, []byte{}, time.Now().Add(writeWait)); err != nil { - logger.WithError(err).Debug("failed to send ping message") + logger.Debugf("failed to send ping message: %s", err) } case <-done: return diff --git a/websocket/websocket_test.go b/websocket/websocket_test.go index 1b5eef48..615753a0 100644 --- a/websocket/websocket_test.go +++ b/websocket/websocket_test.go @@ -9,8 +9,8 @@ import ( "testing" "github.com/cloudflare/cloudflared/hello" + "github.com/cloudflare/cloudflared/logger" "github.com/cloudflare/cloudflared/tlsconfig" - "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" "golang.org/x/net/websocket" ) @@ -62,7 +62,7 @@ func TestGenerateAcceptKey(t *testing.T) { } func TestServe(t *testing.T) { - logger := logrus.New() + logger := logger.NewOutputWriter(logger.NewMockWriteManager()) shutdownC := make(chan struct{}) errC := make(chan error) listener, err := hello.CreateTLSListener("localhost:1111") @@ -105,7 +105,7 @@ func TestServe(t *testing.T) { // remoteAddress := "localhost:1113" // listenerAddress := "localhost:1112" // message := "Good morning Austin! Time for another sunny day in the great state of Texas." -// logger := logrus.New() +// logger := logger.NewOutputWriter(logger.NewMockWriteManager()) // shutdownC := make(chan struct{}) // listener, err := net.Listen("tcp", listenerAddress)