diff --git a/cmd/coder/main.go b/cmd/coder/main.go index 1147a87e..8ba4ab19 100644 --- a/cmd/coder/main.go +++ b/cmd/coder/main.go @@ -9,10 +9,9 @@ import ( "os" "runtime" + "cdr.dev/coder-cli/internal/clog" "cdr.dev/coder-cli/internal/cmd" "cdr.dev/coder-cli/internal/x/xterminal" - - "go.coder.com/flog" ) // Using a global for the version so it can be set at build time using ldflags. @@ -31,7 +30,8 @@ func main() { stdoutState, err := xterminal.MakeOutputRaw(os.Stdout.Fd()) if err != nil { - flog.Fatal("set output to raw: %s", err) + clog.Log(clog.Fatal(fmt.Sprintf("set output to raw: %s", err))) + os.Exit(1) } defer func() { // Best effort. Would result in broken terminal on window but nothing we can do about it. @@ -42,6 +42,7 @@ func main() { app.Version = fmt.Sprintf("%s %s %s/%s", version, runtime.Version(), runtime.GOOS, runtime.GOARCH) if err := app.ExecuteContext(ctx); err != nil { - flog.Fatal("%v", err) + clog.Log(err) + os.Exit(1) } } diff --git a/coder-sdk/error.go b/coder-sdk/error.go index 2f365a47..91ebfa30 100644 --- a/coder-sdk/error.go +++ b/coder-sdk/error.go @@ -12,8 +12,8 @@ import ( // ErrNotFound describes an error case in which the requested resource could not be found var ErrNotFound = xerrors.Errorf("resource not found") -// apiError is the expected payload format for our errors. -type apiError struct { +// APIError is the expected payload format for our errors. +type APIError struct { Err struct { Msg string `json:"msg"` } `json:"error"` @@ -30,7 +30,7 @@ func (e *HTTPError) Error() string { return fmt.Sprintf("dump response: %+v", err) } - var msg apiError + var msg APIError // Try to decode the payload as an error, if it fails or if there is no error message, // return the response URL with the dump. if err := json.NewDecoder(e.Response.Body).Decode(&msg); err != nil || msg.Err.Msg == "" { @@ -38,7 +38,7 @@ func (e *HTTPError) Error() string { } // If the payload was a in the expected error format with a message, include it. - return fmt.Sprintf("%s\n%s%s", e.Response.Request.URL, dump, msg.Err.Msg) + return msg.Err.Msg } func bodyError(resp *http.Response) error { diff --git a/go.mod b/go.mod index 6b75ad80..b8918186 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,6 @@ require ( github.com/rjeczalik/notify v0.9.2 github.com/spf13/cobra v1.0.0 github.com/stretchr/testify v1.6.1 - go.coder.com/flog v0.0.0-20190906214207-47dd47ea0512 golang.org/x/crypto v0.0.0-20200422194213-44a606286825 golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13 diff --git a/internal/activity/pusher.go b/internal/activity/pusher.go index 7f0f0e00..6d0a224f 100644 --- a/internal/activity/pusher.go +++ b/internal/activity/pusher.go @@ -2,13 +2,13 @@ package activity import ( "context" + "fmt" "time" "golang.org/x/time/rate" "cdr.dev/coder-cli/coder-sdk" - - "go.coder.com/flog" + "cdr.dev/coder-cli/internal/clog" ) const pushInterval = time.Minute @@ -42,6 +42,6 @@ func (p *Pusher) Push(ctx context.Context) { } if err := p.client.PushActivity(ctx, p.source, p.envID); err != nil { - flog.Error("push activity: %s", err) + clog.Log(clog.Error(fmt.Sprintf("push activity: %s", err))) } } diff --git a/internal/clog/error.go b/internal/clog/error.go new file mode 100644 index 00000000..71183573 --- /dev/null +++ b/internal/clog/error.go @@ -0,0 +1,131 @@ +package clog + +import ( + "errors" + "fmt" + "os" + "strings" + + "github.com/fatih/color" + "golang.org/x/xerrors" +) + +// CLIMessage provides a human-readable message for CLI errors and messages. +type CLIMessage struct { + Level string + Color color.Attribute + Header string + Lines []string +} + +// CLIError wraps a CLIMessage and allows consumers to treat it as a normal error. +type CLIError struct { + CLIMessage + error +} + +// String formats the CLI message for consumption by a human. +func (m CLIMessage) String() string { + var str strings.Builder + str.WriteString(fmt.Sprintf("%s: %s\n", + color.New(m.Color).Sprint(m.Level), + color.New(color.Bold).Sprint(m.Header)), + ) + for _, line := range m.Lines { + str.WriteString(fmt.Sprintf(" %s %s\n", color.New(m.Color).Sprint("|"), line)) + } + return str.String() +} + +// Log logs the given error to stderr, defaulting to "fatal" if the error is not a CLIError. +// If the error is a CLIError, the plain error chain is ignored and the CLIError +// is logged on its own. +func Log(err error) { + var cliErr CLIError + if !xerrors.As(err, &cliErr) { + cliErr = Fatal(err.Error()) + } + fmt.Fprintln(os.Stderr, cliErr.String()) +} + +// LogInfo prints the given info message to stderr. +func LogInfo(header string, lines ...string) { + fmt.Fprint(os.Stderr, CLIMessage{ + Level: "info", + Color: color.FgBlue, + Header: header, + Lines: lines, + }.String()) +} + +// LogSuccess prints the given info message to stderr. +func LogSuccess(header string, lines ...string) { + fmt.Fprint(os.Stderr, CLIMessage{ + Level: "success", + Color: color.FgGreen, + Header: header, + Lines: lines, + }.String()) +} + +// Warn creates an error with the level "warning". +func Warn(header string, lines ...string) CLIError { + return CLIError{ + CLIMessage: CLIMessage{ + Color: color.FgYellow, + Level: "warning", + Header: header, + Lines: lines, + }, + error: errors.New(header), + } +} + +// Error creates an error with the level "error". +func Error(header string, lines ...string) CLIError { + return CLIError{ + CLIMessage: CLIMessage{ + Color: color.FgRed, + Level: "error", + Header: header, + Lines: lines, + }, + error: errors.New(header), + } +} + +// Fatal creates an error with the level "fatal". +func Fatal(header string, lines ...string) CLIError { + return CLIError{ + CLIMessage: CLIMessage{ + Color: color.FgRed, + Level: "fatal", + Header: header, + Lines: lines, + }, + error: errors.New(header), + } +} + +// Bold provides a convenience wrapper around color.New for brevity when logging. +func Bold(a string) string { + return color.New(color.Bold).Sprint(a) +} + +// Tip formats according to the given format specifier and prepends a bolded "tip: " header. +func Tip(format string, a ...interface{}) string { + return fmt.Sprintf("%s %s", Bold("tip:"), fmt.Sprintf(format, a...)) +} + +// Hint formats according to the given format specifier and prepends a bolded "hint: " header. +func Hint(format string, a ...interface{}) string { + return fmt.Sprintf("%s %s", Bold("hint:"), fmt.Sprintf(format, a...)) +} + +// Cause formats according to the given format specifier and prepends a bolded "cause: " header. +func Cause(format string, a ...interface{}) string { + return fmt.Sprintf("%s %s", Bold("cause:"), fmt.Sprintf(format, a...)) +} + +// BlankLine is an empty string meant to be used in CLIMessage and CLIError construction. +const BlankLine = "" diff --git a/internal/cmd/auth.go b/internal/cmd/auth.go index 8297bbd6..ff789ca3 100644 --- a/internal/cmd/auth.go +++ b/internal/cmd/auth.go @@ -8,10 +8,14 @@ import ( "golang.org/x/xerrors" "cdr.dev/coder-cli/coder-sdk" + "cdr.dev/coder-cli/internal/clog" "cdr.dev/coder-cli/internal/config" ) -var errNeedLogin = xerrors.New("failed to read session credentials: did you run \"coder login\"?") +var errNeedLogin = clog.Fatal( + "failed to read session credentials", + clog.Hint(`did you run "coder login [https://coder.domain.com]"?`), +) func newClient() (*coder.Client, error) { sessionToken, err := config.Session.Read() diff --git a/internal/cmd/ceapi.go b/internal/cmd/ceapi.go index e15b101d..f9b4ebe6 100644 --- a/internal/cmd/ceapi.go +++ b/internal/cmd/ceapi.go @@ -5,6 +5,7 @@ import ( "fmt" "cdr.dev/coder-cli/coder-sdk" + "cdr.dev/coder-cli/internal/clog" "golang.org/x/xerrors" ) @@ -73,10 +74,12 @@ func findEnv(ctx context.Context, client *coder.Client, envName, userEmail strin found = append(found, env.Name) } - return nil, notFoundButDidFind{ - needle: envName, - haystack: found, - } + return nil, clog.Fatal( + "failed to find environment", + fmt.Sprintf("environment %q not found in %q", envName, found), + clog.BlankLine, + clog.Tip("run \"coder envs ls\" to view your environments"), + ) } type notFoundButDidFind struct { diff --git a/internal/cmd/envs.go b/internal/cmd/envs.go index b18be8a6..50020eba 100644 --- a/internal/cmd/envs.go +++ b/internal/cmd/envs.go @@ -2,15 +2,16 @@ package cmd import ( "encoding/json" + "fmt" "os" + "sync/atomic" "cdr.dev/coder-cli/coder-sdk" + "cdr.dev/coder-cli/internal/clog" "cdr.dev/coder-cli/internal/x/xtabwriter" "github.com/spf13/cobra" "golang.org/x/sync/errgroup" "golang.org/x/xerrors" - - "go.coder.com/flog" ) func envsCommand() *cobra.Command { @@ -37,7 +38,7 @@ func envsCommand() *cobra.Command { return err } if len(envs) < 1 { - flog.Info("no environments found") + clog.LogInfo("no environments found") return nil } @@ -92,26 +93,33 @@ coder envs --user charlie@coder.com ls -o json \ } var egroup errgroup.Group + var fails int32 for _, envName := range args { envName := envName egroup.Go(func() error { env, err := findEnv(cmd.Context(), client, envName, *user) if err != nil { - flog.Error("failed to find environment by name \"%s\": %v", envName, err) - return xerrors.Errorf("find environment by name: %w", err) + atomic.AddInt32(&fails, 1) + clog.Log(err) + return xerrors.Errorf("find env by name: %w", err) } if err = client.StopEnvironment(cmd.Context(), env.ID); err != nil { - flog.Error("failed to stop environment \"%s\": %v", env.Name, err) - return xerrors.Errorf("stop environment: %w", err) + atomic.AddInt32(&fails, 1) + err = clog.Fatal(fmt.Sprintf("stop environment %q", env.Name), + clog.Cause(err.Error()), clog.BlankLine, + clog.Hint("current environment status is %q", env.LatestStat.ContainerStatus), + ) + clog.Log(err) + return err } - flog.Success("Successfully stopped environment %q", envName) + clog.LogSuccess(fmt.Sprintf("successfully stopped environment %q", envName)) return nil }) } if err = egroup.Wait(); err != nil { - return xerrors.Errorf("some stop operations failed") + return clog.Fatal(fmt.Sprintf("%d failure(s) emitted", fails)) } return nil }, diff --git a/internal/cmd/login.go b/internal/cmd/login.go index a1740b48..fdcbe765 100644 --- a/internal/cmd/login.go +++ b/internal/cmd/login.go @@ -9,14 +9,13 @@ import ( "strings" "cdr.dev/coder-cli/coder-sdk" + "cdr.dev/coder-cli/internal/clog" "cdr.dev/coder-cli/internal/config" "cdr.dev/coder-cli/internal/loginsrv" "github.com/pkg/browser" "github.com/spf13/cobra" "golang.org/x/sync/errgroup" "golang.org/x/xerrors" - - "go.coder.com/flog" ) func makeLoginCmd() *cobra.Command { @@ -140,7 +139,7 @@ func login(cmd *cobra.Command, envURL *url.URL, urlCfg, sessionCfg config.File) return xerrors.Errorf("store config: %w", err) } - flog.Success("Logged in.") + clog.LogSuccess("logged in") return nil } diff --git a/internal/cmd/logout.go b/internal/cmd/logout.go index 621ed89e..b653d68f 100644 --- a/internal/cmd/logout.go +++ b/internal/cmd/logout.go @@ -3,11 +3,10 @@ package cmd import ( "os" + "cdr.dev/coder-cli/internal/clog" "cdr.dev/coder-cli/internal/config" "github.com/spf13/cobra" "golang.org/x/xerrors" - - "go.coder.com/flog" ) func makeLogoutCmd() *cobra.Command { @@ -22,11 +21,11 @@ func logout(_ *cobra.Command, _ []string) error { err := config.Session.Delete() if err != nil { if os.IsNotExist(err) { - flog.Info("no active session") + clog.LogInfo("no active session") return nil } return xerrors.Errorf("delete session: %w", err) } - flog.Success("logged out") + clog.LogSuccess("logged out") return nil } diff --git a/internal/cmd/rebuild.go b/internal/cmd/rebuild.go index a5d42a50..41f3913d 100644 --- a/internal/cmd/rebuild.go +++ b/internal/cmd/rebuild.go @@ -8,11 +8,11 @@ import ( "time" "cdr.dev/coder-cli/coder-sdk" + "cdr.dev/coder-cli/internal/clog" "github.com/briandowns/spinner" "github.com/fatih/color" "github.com/manifoldco/promptui" "github.com/spf13/cobra" - "go.coder.com/flog" "golang.org/x/crypto/ssh/terminal" "golang.org/x/xerrors" ) @@ -56,7 +56,10 @@ coder envs rebuild backend-env --force`, return err } } else { - flog.Info("Use \"coder envs watch-build %s\" to follow the build logs", env.Name) + clog.LogSuccess( + "successfully started rebuild", + clog.Tip("run \"coder envs watch-build %s\" to follow the build logs", env.Name), + ) } return nil }, diff --git a/internal/cmd/resourcemanager.go b/internal/cmd/resourcemanager.go index f5366f21..d4ee20bb 100644 --- a/internal/cmd/resourcemanager.go +++ b/internal/cmd/resourcemanager.go @@ -8,8 +8,8 @@ import ( "text/tabwriter" "cdr.dev/coder-cli/coder-sdk" + "cdr.dev/coder-cli/internal/clog" "github.com/spf13/cobra" - "go.coder.com/flog" "golang.org/x/xerrors" ) @@ -212,8 +212,10 @@ func printResourceTop(writer io.Writer, groups []groupable, labeler envLabeler, _, _ = fmt.Fprint(tabwriter, "\n") } if len(userResources) == 0 { - flog.Info("No groups for the given filters exist with active environments.") - flog.Info("Use \"--show-empty\" to see groups with no resources.") + clog.LogInfo( + "no groups for the given filters exist with active environments", + clog.Tip("run \"--show-empty\" to see groups with no resources."), + ) } return nil } diff --git a/internal/cmd/secrets.go b/internal/cmd/secrets.go index 9d98faac..879d6bde 100644 --- a/internal/cmd/secrets.go +++ b/internal/cmd/secrets.go @@ -10,9 +10,8 @@ import ( "golang.org/x/xerrors" "cdr.dev/coder-cli/coder-sdk" + "cdr.dev/coder-cli/internal/clog" "cdr.dev/coder-cli/internal/x/xtabwriter" - - "go.coder.com/flog" ) func makeSecretsCmd() *cobra.Command { @@ -154,7 +153,7 @@ func listSecrets(userEmail *string) func(cmd *cobra.Command, _ []string) error { } if len(secrets) < 1 { - flog.Info("No secrets found") + clog.LogInfo("no secrets found") return nil } @@ -212,10 +211,12 @@ func makeRemoveSecrets(userEmail *string) func(c *cobra.Command, args []string) for _, n := range args { err := client.DeleteSecretByName(cmd.Context(), n, user.ID) if err != nil { - flog.Error("failed to delete secret %q: %v", n, err) + clog.Log(clog.Error( + fmt.Sprintf("failed to delete secret %q: %v", n, err), + )) errorSeen = true } else { - flog.Success("successfully deleted secret %q", n) + clog.LogSuccess("successfully deleted secret: %q", n) } } if errorSeen { diff --git a/internal/cmd/shell.go b/internal/cmd/shell.go index 4e08edf0..76c2c8d4 100644 --- a/internal/cmd/shell.go +++ b/internal/cmd/shell.go @@ -2,6 +2,7 @@ package cmd import ( "context" + "fmt" "io" "os" "strings" @@ -15,10 +16,9 @@ import ( "cdr.dev/coder-cli/coder-sdk" "cdr.dev/coder-cli/internal/activity" + "cdr.dev/coder-cli/internal/clog" "cdr.dev/coder-cli/internal/x/xterminal" "cdr.dev/wsep" - - "go.coder.com/flog" ) func getEnvsForCompletion(user string) func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { @@ -153,7 +153,7 @@ func runCommand(ctx context.Context, envName, command string, args []string) err if err != nil { var closeErr websocket.CloseError if xerrors.As(err, &closeErr) { - return xerrors.Errorf("network error, is %q online?", envName) + return networkErr(client, env) } return xerrors.Errorf("start remote command: %w", err) } @@ -179,7 +179,6 @@ func runCommand(ctx context.Context, envName, command string, args []string) err } }() go func() { - if _, err := io.Copy(os.Stderr, process.Stderr()); err != nil { cancel() } @@ -188,13 +187,26 @@ func runCommand(ctx context.Context, envName, command string, args []string) err if err := process.Wait(); err != nil { var closeErr websocket.CloseError if xerrors.Is(err, ctx.Err()) || xerrors.As(err, &closeErr) { - return xerrors.Errorf("network error, is %q online?", envName) + return networkErr(client, env) } return err } return nil } +func networkErr(client *coder.Client, env *coder.Environment) error { + if env.LatestStat.ContainerStatus != coder.EnvironmentOn { + return clog.Fatal( + "environment is not running", + fmt.Sprintf("environment %q is not running", env.Name), + fmt.Sprintf("its current status is %q", env.LatestStat.ContainerStatus), + clog.BlankLine, + clog.Tip("run \"coder envs rebuild %s --follow\" to start the environment", env.Name), + ) + } + return xerrors.Errorf("network error, is %q online?", env.Name) +} + func heartbeat(ctx context.Context, conn *websocket.Conn, interval time.Duration) { ticker := time.NewTicker(interval) defer ticker.Stop() @@ -204,9 +216,10 @@ func heartbeat(ctx context.Context, conn *websocket.Conn, interval time.Duration case <-ctx.Done(): return case <-ticker.C: - if err := conn.Ping(ctx); err != nil { - // NOTE: Prefix with \r\n to attempt to have clearer output as we might still be in raw mode. - flog.Fatal("\r\nFailed to ping websocket: %s, exiting.", err) + if err := conn.Ping(ctx); err != nil || true { + // don't try to do multi-line here because the raw mode makes things weird + clog.Log(clog.Fatal("failed to ping websocket, exiting: " + err.Error())) + os.Exit(1) } } } diff --git a/internal/cmd/sync.go b/internal/cmd/sync.go index 0ba32095..05d9cd00 100644 --- a/internal/cmd/sync.go +++ b/internal/cmd/sync.go @@ -9,11 +9,10 @@ import ( "strings" "cdr.dev/coder-cli/coder-sdk" + "cdr.dev/coder-cli/internal/clog" "cdr.dev/coder-cli/internal/sync" "github.com/spf13/cobra" "golang.org/x/xerrors" - - "go.coder.com/flog" ) func makeSyncCmd() *cobra.Command { @@ -96,7 +95,7 @@ func makeRunSync(init *bool) func(cmd *cobra.Command, args []string) error { remoteVersion, rsyncErr := s.Version() if rsyncErr != nil { - flog.Info("Unable to determine remote rsync version. Proceeding cautiously.") + clog.LogInfo("unable to determine remote rsync version: proceeding cautiously") } else if localVersion != remoteVersion { return xerrors.Errorf("rsync protocol mismatch: local = %s, remote = %s", localVersion, remoteVersion) } diff --git a/internal/cmd/urls.go b/internal/cmd/urls.go index b79ddaa8..c91427da 100644 --- a/internal/cmd/urls.go +++ b/internal/cmd/urls.go @@ -14,9 +14,8 @@ import ( "golang.org/x/xerrors" "cdr.dev/coder-cli/coder-sdk" + "cdr.dev/coder-cli/internal/clog" "cdr.dev/coder-cli/internal/x/xtabwriter" - - "go.coder.com/flog" ) func makeURLCmd() *cobra.Command { @@ -70,7 +69,7 @@ var urlAccessLevel = map[string]string{ func validatePort(port string) (int, error) { p, err := strconv.ParseUint(port, 10, 16) if err != nil { - flog.Error("Invalid port") + clog.Log(clog.Error("invalid port")) return 0, err } if p < 1 { @@ -83,7 +82,7 @@ func validatePort(port string) (int, error) { func accessLevelIsValid(level string) bool { _, ok := urlAccessLevel[level] if !ok { - flog.Error("Invalid access level") + clog.Log(clog.Error("invalid access level")) } return ok } @@ -101,7 +100,7 @@ func makeListDevURLs(outputFmt *string) func(cmd *cobra.Command, args []string) switch *outputFmt { case "human": if len(devURLs) < 1 { - flog.Info("No devURLs found for environment %q", envName) + clog.LogInfo(fmt.Sprintf("no devURLs found for environment %q", envName)) return nil } err := xtabwriter.WriteTable(len(devURLs), func(i int) interface{} { @@ -168,13 +167,13 @@ func makeCreateDevURL() *cobra.Command { urlID, found := devURLID(portNum, urls) if found { - flog.Info("Updating devurl for port %v", port) + clog.LogInfo(fmt.Sprintf("updating devurl for port %v", port)) err := client.UpdateDevURL(cmd.Context(), env.ID, urlID, portNum, urlname, access) if err != nil { return xerrors.Errorf("update DevURL: %w", err) } } else { - flog.Info("Adding devurl for port %v", port) + clog.LogInfo(fmt.Sprintf("Adding devurl for port %v", port)) err := client.InsertDevURL(cmd.Context(), env.ID, portNum, urlname, access) if err != nil { return xerrors.Errorf("insert DevURL: %w", err) @@ -236,7 +235,7 @@ func removeDevURL(cmd *cobra.Command, args []string) error { urlID, found := devURLID(portNum, urls) if found { - flog.Info("Deleting devurl for port %v", port) + clog.LogInfo(fmt.Sprintf("deleting devurl for port %v", port)) } else { return xerrors.Errorf("No devurl found for port %v", port) } diff --git a/internal/sync/eventcache.go b/internal/sync/eventcache.go index 85a5e8ac..f4e757f8 100644 --- a/internal/sync/eventcache.go +++ b/internal/sync/eventcache.go @@ -5,8 +5,6 @@ import ( "time" "github.com/rjeczalik/notify" - - "go.coder.com/flog" ) type timedEvent struct { @@ -17,18 +15,14 @@ type timedEvent struct { type eventCache map[string]timedEvent func (cache eventCache) Add(ev timedEvent) { - log := flog.New() - log.Prefix = ev.Path() + ": " lastEvent, ok := cache[ev.Path()] if ok { switch { // If the file was quickly created and then destroyed, pretend nothing ever happened. case lastEvent.Event() == notify.Create && ev.Event() == notify.Remove: delete(cache, ev.Path()) - log.Info("ignored Create then Remove") return } - log.Info("replaced %s with %s", lastEvent.Event(), ev.Event()) } // Only let the latest event for a path have action. cache[ev.Path()] = ev diff --git a/internal/sync/sync.go b/internal/sync/sync.go index 83e6ca85..177ff9c9 100644 --- a/internal/sync/sync.go +++ b/internal/sync/sync.go @@ -21,10 +21,9 @@ import ( "golang.org/x/sync/semaphore" "golang.org/x/xerrors" - "go.coder.com/flog" - "cdr.dev/coder-cli/coder-sdk" "cdr.dev/coder-cli/internal/activity" + "cdr.dev/coder-cli/internal/clog" "cdr.dev/wsep" ) @@ -119,7 +118,7 @@ func (s Sync) remoteCmd(ctx context.Context, prog string, args ...string) error // initSync performs the initial synchronization of the directory. func (s Sync) initSync() error { - flog.Info("doing initial sync (%s -> %s)", s.LocalDir, s.RemoteDir) + clog.LogInfo(fmt.Sprintf("doing initial sync (%s -> %s)", s.LocalDir, s.RemoteDir)) start := time.Now() // Delete old files on initial sync (e.g git checkout). @@ -128,7 +127,9 @@ func (s Sync) initSync() error { if err := s.syncPaths(true, s.LocalDir+"/.", s.RemoteDir); err != nil { return err } - flog.Success("finished initial sync (%s)", time.Since(start).Truncate(time.Millisecond)) + clog.LogSuccess( + fmt.Sprintf("finished initial sync (%s)", time.Since(start).Truncate(time.Millisecond)), + ) return nil } @@ -193,16 +194,16 @@ func (s Sync) work(ev timedEvent) { case notify.Remove: err = s.handleDelete(localPath) default: - flog.Info("unhandled event %+v %s", ev.Event(), ev.Path()) + clog.LogInfo(fmt.Sprintf("unhandled event %+v %s", ev.Event(), ev.Path())) } log := fmt.Sprintf("%v %s (%s)", ev.Event(), filepath.Base(localPath), time.Since(ev.CreatedAt).Truncate(time.Millisecond*10), ) if err != nil { - flog.Error(log+": %s", err) + clog.Log(clog.Error(fmt.Sprintf("%s: %s", log, err))) } else { - flog.Success(log) + clog.LogSuccess(log) } } @@ -331,7 +332,7 @@ func (s Sync) Run() error { return nil } - flog.Info("watching %s for changes", s.LocalDir) + clog.LogInfo(fmt.Sprintf("watching %s for changes", s.LocalDir)) var droppedEvents uint64 // Timed events lets us track how long each individual file takes to @@ -347,7 +348,7 @@ func (s Sync) Run() error { }: default: if atomic.AddUint64(&droppedEvents, 1) == 1 { - flog.Info("dropped event, sync should restart soon") + clog.LogInfo("dropped event, sync should restart soon") } } }
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: