Skip to content

Commit 7a7af6a

Browse files
authored
Merge pull request #1134 from wakatime/feature/backoff-debug-message
Log backoff as debug
2 parents 7d914bc + ad9089d commit 7a7af6a

File tree

5 files changed

+177
-13
lines changed

5 files changed

+177
-13
lines changed

cmd/run.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import (
3737

3838
"github.com/spf13/cobra"
3939
"github.com/spf13/viper"
40+
"go.uber.org/zap/zapcore"
4041
iniv1 "gopkg.in/ini.v1"
4142
"gopkg.in/natefinch/lumberjack.v2"
4243
)
@@ -340,7 +341,9 @@ func runCmd(ctx context.Context, v *viper.Viper, verbose bool, sendDiagsOnErrors
340341
verbose = verbose || errwaka.ShouldLogError()
341342
}
342343

343-
if verbose {
344+
if errloglevel, ok := err.(wakaerror.LogLevel); ok {
345+
logger.Logf(zapcore.Level(errloglevel.LogLevel()), "failed to run command: %s", err)
346+
} else if verbose {
344347
logger.Errorf("failed to run command: %s", err)
345348
}
346349

cmd/run_test.go

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"time"
1414

1515
"github.com/wakatime/wakatime-cli/cmd"
16+
"github.com/wakatime/wakatime-cli/pkg/api"
1617
"github.com/wakatime/wakatime-cli/pkg/exitcode"
1718
"github.com/wakatime/wakatime-cli/pkg/log"
1819
"github.com/wakatime/wakatime-cli/pkg/offline"
@@ -77,6 +78,73 @@ func TestRunCmd_Err(t *testing.T) {
7778
assert.Eventually(t, func() bool { return numCalls == 0 }, time.Second, 50*time.Millisecond)
7879
}
7980

81+
func TestRunCmd_ErrBackoff(t *testing.T) {
82+
testServerURL, router, tearDown := setupTestServer()
83+
defer tearDown()
84+
85+
ctx := context.Background()
86+
87+
var numCalls int
88+
89+
router.HandleFunc("/plugins/errors", func(_ http.ResponseWriter, _ *http.Request) {
90+
numCalls++
91+
})
92+
93+
version.OS = "some os"
94+
version.Arch = "some architecture"
95+
version.Version = "some version"
96+
97+
tmpDir := t.TempDir()
98+
99+
offlineQueueFile, err := os.CreateTemp(tmpDir, "")
100+
require.NoError(t, err)
101+
102+
defer offlineQueueFile.Close()
103+
104+
logFile, err := os.CreateTemp(tmpDir, "")
105+
require.NoError(t, err)
106+
107+
defer logFile.Close()
108+
109+
v := viper.New()
110+
v.Set("api-url", testServerURL)
111+
v.Set("entity", "/path/to/file")
112+
v.Set("key", "00000000-0000-4000-8000-000000000000")
113+
v.Set("log-file", logFile.Name())
114+
v.Set("offline-queue-file", offlineQueueFile.Name())
115+
v.Set("plugin", "vim")
116+
117+
logger, err := cmd.SetupLogging(ctx, v)
118+
require.NoError(t, err)
119+
120+
defer logger.Flush()
121+
122+
ctx = log.ToContext(ctx, logger)
123+
124+
var cmdNumCalls int
125+
126+
cmdFn := func(_ context.Context, _ *viper.Viper) (int, error) {
127+
cmdNumCalls++
128+
return 42, api.ErrBackoff{Err: errors.New("fail")}
129+
}
130+
131+
err = cmd.RunCmd(ctx, v, false, false, cmdFn)
132+
require.Error(t, err)
133+
134+
var errexitcode exitcode.Err
135+
136+
require.ErrorAs(t, err, &errexitcode)
137+
138+
assert.Equal(t, 42, err.(exitcode.Err).Code)
139+
assert.Equal(t, 1, cmdNumCalls)
140+
assert.Eventually(t, func() bool { return numCalls == 0 }, time.Second, 50*time.Millisecond)
141+
142+
output, err := io.ReadAll(logFile)
143+
require.NoError(t, err)
144+
145+
assert.Empty(t, string(output))
146+
}
147+
80148
func TestRunCmd_Verbose_Err(t *testing.T) {
81149
testServerURL, router, tearDown := setupTestServer()
82150
defer tearDown()
@@ -104,6 +172,7 @@ func TestRunCmd_Verbose_Err(t *testing.T) {
104172
v.Set("key", "00000000-0000-4000-8000-000000000000")
105173
v.Set("offline-queue-file", offlineQueueFile.Name())
106174
v.Set("plugin", "vim")
175+
v.Set("verbose", true)
107176

108177
var cmdNumCalls int
109178

@@ -123,6 +192,73 @@ func TestRunCmd_Verbose_Err(t *testing.T) {
123192
assert.Eventually(t, func() bool { return numCalls == 0 }, time.Second, 50*time.Millisecond)
124193
}
125194

195+
func TestRunCmd_Verbose_ErrBackoff(t *testing.T) {
196+
testServerURL, router, tearDown := setupTestServer()
197+
defer tearDown()
198+
199+
ctx := context.Background()
200+
201+
var numCalls int
202+
203+
router.HandleFunc("/plugins/errors", func(_ http.ResponseWriter, _ *http.Request) {
204+
numCalls++
205+
})
206+
207+
version.OS = "some os"
208+
version.Arch = "some architecture"
209+
version.Version = "some version"
210+
211+
tmpDir := t.TempDir()
212+
213+
offlineQueueFile, err := os.CreateTemp(tmpDir, "")
214+
require.NoError(t, err)
215+
216+
defer offlineQueueFile.Close()
217+
218+
logFile, err := os.CreateTemp(tmpDir, "")
219+
require.NoError(t, err)
220+
221+
defer logFile.Close()
222+
223+
v := viper.New()
224+
v.Set("api-url", testServerURL)
225+
v.Set("entity", "/path/to/file")
226+
v.Set("key", "00000000-0000-4000-8000-000000000000")
227+
v.Set("offline-queue-file", offlineQueueFile.Name())
228+
v.Set("plugin", "vim")
229+
v.Set("log-file", logFile.Name())
230+
v.Set("verbose", true)
231+
232+
logger, err := cmd.SetupLogging(ctx, v)
233+
require.NoError(t, err)
234+
235+
defer logger.Flush()
236+
237+
ctx = log.ToContext(ctx, logger)
238+
239+
var cmdNumCalls int
240+
241+
cmdFn := func(_ context.Context, _ *viper.Viper) (int, error) {
242+
cmdNumCalls++
243+
return 42, api.ErrBackoff{Err: errors.New("fail")}
244+
}
245+
246+
err = cmd.RunCmd(ctx, v, true, false, cmdFn)
247+
248+
var errexitcode exitcode.Err
249+
250+
require.ErrorAs(t, err, &errexitcode)
251+
252+
assert.Equal(t, 42, err.(exitcode.Err).Code)
253+
assert.Equal(t, 1, cmdNumCalls)
254+
assert.Eventually(t, func() bool { return numCalls == 0 }, time.Second, 50*time.Millisecond)
255+
256+
output, err := io.ReadAll(logFile)
257+
require.NoError(t, err)
258+
259+
assert.Contains(t, string(output), "failed to run command: fail")
260+
}
261+
126262
func TestRunCmd_SendDiagnostics_Err(t *testing.T) {
127263
testServerURL, router, tearDown := setupTestServer()
128264
defer tearDown()

pkg/api/error.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import (
55

66
"github.com/wakatime/wakatime-cli/pkg/exitcode"
77
"github.com/wakatime/wakatime-cli/pkg/wakaerror"
8+
9+
"go.uber.org/zap/zapcore"
810
)
911

1012
// Err represents a general api error.
@@ -120,6 +122,11 @@ func (ErrBackoff) ExitCode() int {
120122
return exitcode.ErrBackoff
121123
}
122124

125+
// LogLevel method to implement wakaerror.LogLevel interface.
126+
func (ErrBackoff) LogLevel() int8 {
127+
return int8(zapcore.DebugLevel)
128+
}
129+
123130
// Message method to implement wakaerror.Error interface.
124131
func (e ErrBackoff) Message() string {
125132
return fmt.Sprintf("rate limited: %s", e.Err)

pkg/log/log.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,16 @@ func SetJww(verbose bool, w io.Writer) {
130130
}
131131
}
132132

133+
// Log logs a message at the given level.
134+
func (l Logger) Log(level zapcore.Level, msg string) {
135+
l.entry.Log(level, msg)
136+
}
137+
138+
// Logf logs a message at the given level.
139+
func (l Logger) Logf(level zapcore.Level, format string, args ...any) {
140+
l.entry.Log(level, fmt.Sprintf(format, args...))
141+
}
142+
133143
// Debugf logs a message at level Debug.
134144
func (l *Logger) Debugf(format string, args ...any) {
135145
l.entry.Log(zapcore.DebugLevel, fmt.Sprintf(format, args...))

pkg/wakaerror/wakaerror.go

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,22 @@
11
package wakaerror
22

3-
// Error is a custom error interface.
4-
type Error interface {
5-
// ExitCode returns the exit code for the error.
6-
ExitCode() int
7-
// Message returns the error message.
8-
Message() string
9-
// SendDiagsOnErrors returns true when diagnostics should be sent on error.
10-
SendDiagsOnErrors() bool
11-
// ShouldLogError returns true when error should be logged.
12-
ShouldLogError() bool
13-
error
14-
}
3+
type (
4+
// Error is a custom error interface.
5+
Error interface {
6+
// ExitCode returns the exit code for the error.
7+
ExitCode() int
8+
// Message returns the error message.
9+
Message() string
10+
// SendDiagsOnErrors returns true when diagnostics should be sent on error.
11+
SendDiagsOnErrors() bool
12+
// ShouldLogError returns true when error should be logged.
13+
ShouldLogError() bool
14+
error
15+
}
16+
17+
// LogLevel is a custom log level interface to return log level for error.
18+
LogLevel interface {
19+
// LogLevel returns the log level for the error.
20+
LogLevel() int8
21+
}
22+
)

0 commit comments

Comments
 (0)