diff --git a/client.go b/client.go index dfd9866..44c2660 100644 --- a/client.go +++ b/client.go @@ -53,9 +53,9 @@ func (c *Workwx) WithApp(corpSecret string, agentID int64) *WorkwxApp { CorpSecret: corpSecret, AgentID: agentID, - accessToken: &token{mutex: &sync.RWMutex{}}, - jsapiTicket: &token{mutex: &sync.RWMutex{}}, - jsapiTicketAgentConfig: &token{mutex: &sync.RWMutex{}}, + accessToken: &token{mutex: &sync.RWMutex{}, logger: c.opts.Logger}, + jsapiTicket: &token{mutex: &sync.RWMutex{}, logger: c.opts.Logger}, + jsapiTicketAgentConfig: &token{mutex: &sync.RWMutex{}, logger: c.opts.Logger}, } app.accessToken.setGetTokenFunc(app.getAccessToken) app.jsapiTicket.setGetTokenFunc(app.getJSAPITicket) diff --git a/client_options.go b/client_options.go index ef8e27b..2c0ee2c 100644 --- a/client_options.go +++ b/client_options.go @@ -10,6 +10,7 @@ const DefaultQYAPIHost = "https://qyapi.weixin.qq.com" type options struct { QYAPIHost string HTTP *http.Client + Logger Logger } // CtorOption 客户端对象构造参数 @@ -22,6 +23,7 @@ func defaultOptions() options { return options{ QYAPIHost: DefaultQYAPIHost, HTTP: &http.Client{}, + Logger: newDefaultLogger(), } } @@ -62,3 +64,22 @@ var _ CtorOption = (*withHTTPClient)(nil) func (x *withHTTPClient) applyTo(y *options) { y.HTTP = x.x } + +// +// +// + +type withLogger struct { + x Logger +} + +// WithLogger 使用给定的 logger 作为日志打印的方式。 +func WithLogger(logger Logger) CtorOption { + return &withLogger{x: logger} +} + +var _ CtorOption = (*withLogger)(nil) + +func (x *withLogger) applyTo(y *options) { + y.Logger = x.x +} diff --git a/client_options_test.go b/client_options_test.go index 159ea99..d7b7934 100644 --- a/client_options_test.go +++ b/client_options_test.go @@ -1,6 +1,7 @@ package workwx import ( + "fmt" "net/http" "testing" @@ -52,3 +53,29 @@ func TestWithQYAPIHost(t *testing.T) { }) }) } + +func TestWithLogger(t *testing.T) { + c.Convey("给定一个 options", t, func() { + opts := options{} + + c.Convey("用 WithLogger 修饰它", func() { + + l := &myLogger{} + o := WithLogger(l) + o.applyTo(&opts) + + c.Convey("options.Logger 应该变了", func() { + c.So(opts.Logger, c.ShouldEqual, l) + }) + }) + }) +} + +type myLogger struct{} + +func (*myLogger) Info(msg string) { + fmt.Println("INFO: " + msg) +} +func (*myLogger) Error(msg string) { + fmt.Println("ERROR: " + msg) +} diff --git a/logger.go b/logger.go new file mode 100644 index 0000000..e1e1f94 --- /dev/null +++ b/logger.go @@ -0,0 +1,16 @@ +package workwx + +type Logger interface { + Info(msg string) + Error(msg string) +} + +type defaultLogger struct{} + +func (l *defaultLogger) Info(msg string) {} + +func (l *defaultLogger) Error(msg string) {} + +func newDefaultLogger() *defaultLogger { + return &defaultLogger{} +} diff --git a/token.go b/token.go index 63620d3..660cfe4 100644 --- a/token.go +++ b/token.go @@ -2,6 +2,7 @@ package workwx import ( "context" + "fmt" "sync" "time" @@ -18,6 +19,7 @@ type token struct { tokenInfo lastRefresh time.Time getTokenFunc func() (tokenInfo, error) + logger Logger } // getAccessToken 获取 access token @@ -150,8 +152,7 @@ func (t *token) tokenRefresher(ctx context.Context) { case <-time.After(waitDuration): retryer := backoff.WithContext(backoff.NewExponentialBackOff(), ctx) if err := backoff.Retry(t.syncToken, retryer); err != nil { - // TODO: logging - _ = err + t.logger.Error(fmt.Sprintf("failed to refresh token: %v. will retry.", err)) } waitUntilTime := t.lastRefresh.Add(t.expiresIn).Add(-refreshTimeWindow) @@ -160,6 +161,7 @@ func (t *token) tokenRefresher(ctx context.Context) { waitDuration = minRefreshDuration } case <-ctx.Done(): + t.logger.Info("tokenRefresher terminated") return } }