Skip to content
This repository was archived by the owner on Jun 18, 2025. It is now read-only.

Commit db117dc

Browse files
authored
Merge pull request #119 from EventStore/logging
Implement Logging abstraction.
2 parents 9ef0bb9 + c0cb15a commit db117dc

7 files changed

Lines changed: 105 additions & 31 deletions

File tree

esdb/configuration.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"crypto/x509"
55
"fmt"
66
"io/ioutil"
7-
"log"
87
"strconv"
98
"strings"
109
"time"
@@ -74,6 +73,15 @@ type Configuration struct {
7473
// The amount of time (in milliseconds) a non-streaming operation should take to complete before resulting in a
7574
// DeadlineExceeded. Defaults to 10 seconds.
7675
DefaultDeadline *time.Duration
76+
77+
// Logging abstraction used by the client.
78+
Logger LoggingFunc
79+
}
80+
81+
func (conf *Configuration) applyLogger(level LogLevel, format string, args ...interface{}) {
82+
if conf.Logger != nil {
83+
conf.Logger(level, format, args)
84+
}
7785
}
7886

7987
// ParseConnectionString creates a Configuration based on an EventStoreDb connection string.
@@ -85,6 +93,7 @@ func ParseConnectionString(connectionString string) (*Configuration, error) {
8593
KeepAliveInterval: 10 * time.Second,
8694
KeepAliveTimeout: 10 * time.Second,
8795
NodePreference: NodePreference_Leader,
96+
Logger: ConsoleLogging(),
8897
}
8998

9099
schemeIndex := strings.Index(connectionString, SchemeSeparator)
@@ -243,7 +252,7 @@ func parseSetting(k, v string, config *Configuration) error {
243252
}
244253

245254
if config.KeepAliveInterval >= 0 && config.KeepAliveInterval < 10*time.Second {
246-
log.Printf("Specified KeepAliveInterval of %d is less than recommended 10_000 ms", config.KeepAliveInterval)
255+
config.applyLogger(LogWarn, "specified KeepAliveInterval of %d is less than recommended 10_000 ms", config.KeepAliveInterval)
247256
}
248257
case "keepalivetimeout":
249258
err := parseKeepAliveSetting(k, v, &config.KeepAliveTimeout)

esdb/endpoint.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,14 +67,18 @@ func ParseEndPoint(s string) (*EndPoint, error) {
6767
func NewGrpcClient(config Configuration) *grpcClient {
6868
channel := make(chan msg)
6969
closeFlag := new(int32)
70+
logger := logger{
71+
callback: config.Logger,
72+
}
7073

7174
atomic.StoreInt32(closeFlag, 0)
7275

73-
go connectionStateMachine(config, closeFlag, channel)
76+
go connectionStateMachine(config, closeFlag, channel, &logger)
7477

7578
return &grpcClient{
7679
channel: channel,
7780
closeFlag: closeFlag,
7881
once: new(sync.Once),
82+
logger: &logger,
7983
}
8084
}

esdb/impl.go

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55
"crypto/tls"
66
"encoding/base64"
77
"fmt"
8-
"log"
98
"math/rand"
109
"strconv"
1110
"strings"
@@ -29,6 +28,7 @@ type grpcClient struct {
2928
channel chan msg
3029
closeFlag *int32
3130
once *sync.Once
31+
logger *logger
3232
}
3333

3434
func (client *grpcClient) handleError(handle *connectionHandle, headers metadata.MD, trailers metadata.MD, err error) error {
@@ -54,7 +54,7 @@ func (client *grpcClient) handleError(handle *connectionHandle, headers metadata
5454
}
5555

5656
client.channel <- msg
57-
log.Printf("[error] Not leader exception, reconnecting to %v", endpoint)
57+
client.logger.error("not leader exception, reconnecting to %v", endpoint)
5858
return &Error{code: ErrorNotLeader}
5959
}
6060
}
@@ -71,7 +71,7 @@ func (client *grpcClient) handleError(handle *connectionHandle, headers metadata
7171
return &Error{code: code}
7272
}
7373

74-
log.Printf("[error] unexpected exception: %v", err)
74+
client.logger.error("unexpected exception: %v", err)
7575

7676
msg := reconnect{
7777
correlation: handle.Id(),
@@ -176,7 +176,7 @@ func newConnectionHandle(id uuid.UUID, serverInfo *ServerInfo, connection *grpc.
176176
}
177177
}
178178

179-
func connectionStateMachine(config Configuration, closeFlag *int32, channel chan msg) {
179+
func connectionStateMachine(config Configuration, closeFlag *int32, channel chan msg, logger *logger) {
180180
state := newConnectionState(config)
181181

182182
for {
@@ -187,7 +187,7 @@ func connectionStateMachine(config Configuration, closeFlag *int32, channel chan
187187
err := state.connection.Close()
188188

189189
if err != nil {
190-
log.Printf("[warn] error when closing gRPC connection: %v", err)
190+
logger.warn("error when closing gRPC connection. %v", err)
191191
}
192192
}
193193

@@ -199,7 +199,7 @@ func connectionStateMachine(config Configuration, closeFlag *int32, channel chan
199199
{
200200
// Means we need to create a grpc connection.
201201
if state.correlation == uuid.Nil {
202-
conn, serverInfo, err := discoverNode(state.config)
202+
conn, serverInfo, err := discoverNode(state.config, logger)
203203

204204
if err != nil {
205205
atomic.StoreInt32(closeFlag, 1)
@@ -234,7 +234,7 @@ func connectionStateMachine(config Configuration, closeFlag *int32, channel chan
234234
if evt.endpoint == nil {
235235
// Means that in the next iteration cycle, the discovery process will start.
236236
state.correlation = uuid.Nil
237-
log.Printf("[info] Starting a new discovery process")
237+
logger.info("starting a new discovery process")
238238
continue
239239
}
240240

@@ -243,18 +243,18 @@ func connectionStateMachine(config Configuration, closeFlag *int32, channel chan
243243
state.connection = nil
244244
}
245245

246-
log.Printf("[info] Connecting to leader node %s ...", evt.endpoint.String())
246+
logger.info("Connecting to leader node %s ...", evt.endpoint.String())
247247
conn, err := createGrpcConnection(&state.config, evt.endpoint.String())
248248

249249
if err != nil {
250-
log.Printf("[error] exception when connecting to suggested node %s", evt.endpoint.String())
250+
logger.error("exception when connecting to suggested node %s", evt.endpoint.String())
251251
state.correlation = uuid.Nil
252252
continue
253253
}
254254

255255
serverInfo, err := getSupportedMethods(context.Background(), &state.config, conn)
256256
if err != nil {
257-
log.Printf("[error] exception when fetching server features from suggested node %s: %v", evt.endpoint.String(), err)
257+
logger.error("exception when fetching server features from suggested node %s: %v", evt.endpoint.String(), err)
258258
state.correlation = uuid.Nil
259259
continue
260260
}
@@ -263,7 +263,7 @@ func connectionStateMachine(config Configuration, closeFlag *int32, channel chan
263263
state.connection = conn
264264
state.serverInfo = serverInfo
265265

266-
log.Printf("[info] Successfully connected to leader node %s", evt.endpoint.String())
266+
logger.info("successfully connected to leader node %s", evt.endpoint.String())
267267
}
268268
}
269269
}
@@ -432,7 +432,7 @@ func allowedNodeState() []gossipApi.MemberInfo_VNodeState {
432432
}
433433
}
434434

435-
func discoverNode(conf Configuration) (*grpc.ClientConn, *ServerInfo, error) {
435+
func discoverNode(conf Configuration, logger *logger) (*grpc.ClientConn, *ServerInfo, error) {
436436
var connection *grpc.ClientConn = nil
437437
var serverInfo *ServerInfo = nil
438438
var err error
@@ -461,12 +461,12 @@ func discoverNode(conf Configuration) (*grpc.ClientConn, *ServerInfo, error) {
461461

462462
for attempt < conf.MaxDiscoverAttempts {
463463
attempt += 1
464-
log.Printf("[info] discovery attempt %v/%v", attempt, conf.MaxDiscoverAttempts)
464+
logger.info("discovery attempt %v/%v", attempt, conf.MaxDiscoverAttempts)
465465
for _, candidate := range candidates {
466-
log.Printf("[debug] trying candidate '%s'...", candidate)
466+
logger.debug("trying candidate '%s'...", candidate)
467467
connection, err = createGrpcConnection(&conf, candidate)
468468
if err != nil {
469-
log.Printf("[warn] Error when creating a grpc connection for candidate %s: %v", candidate, err)
469+
logger.warn("error when creating a grpc connection for candidate %s: %v", candidate, err)
470470

471471
continue
472472
}
@@ -478,7 +478,7 @@ func discoverNode(conf Configuration) (*grpc.ClientConn, *ServerInfo, error) {
478478

479479
s, ok := status.FromError(err)
480480
if !ok || (s != nil && s.Code() != codes.OK) {
481-
log.Printf("[warn] Error when reading gossip from candidate %s: %v", candidate, err)
481+
logger.warn("error when reading gossip from candidate %s: %v", candidate, err)
482482
cancel()
483483
continue
484484
}
@@ -488,37 +488,37 @@ func discoverNode(conf Configuration) (*grpc.ClientConn, *ServerInfo, error) {
488488
selected, err := pickBestCandidate(info, conf.NodePreference)
489489

490490
if err != nil {
491-
log.Printf("[warn] Eror when picking best candidate out of %s gossip response: %v", candidate, err)
491+
logger.warn("error when picking best candidate out of %s gossip response: %v", candidate, err)
492492
continue
493493
}
494494

495495
selectedAddress := fmt.Sprintf("%s:%d", selected.GetHttpEndPoint().GetAddress(), selected.GetHttpEndPoint().GetPort())
496-
log.Printf("[info] Best candidate found. %s (%s)", selectedAddress, selected.State.String())
496+
logger.info("best candidate found. %s (%s)", selectedAddress, selected.State.String())
497497
if candidate != selectedAddress {
498498
candidate = selectedAddress
499499
_ = connection.Close()
500500
connection, err = createGrpcConnection(&conf, selectedAddress)
501501

502502
if err != nil {
503-
log.Printf("[warn] Error when creating gRPC connection for the selected candidate '%s': %v", selectedAddress, err)
503+
logger.warn("error when creating gRPC connection for the selected candidate '%s': %v", selectedAddress, err)
504504
continue
505505
}
506506
}
507507
}
508508

509-
log.Printf("[debug] Attempting node supported features retrieval on '%s'...", candidate)
509+
logger.debug("attempting node supported features retrieval on '%s'...", candidate)
510510
serverInfo, err = getSupportedMethods(context.Background(), &conf, connection)
511511
if err != nil {
512-
log.Printf("[warn] Error when creating reading server features from the best candidate '%s': %v", candidate, err)
512+
logger.warn("error when creating reading server features from the best candidate '%s': %v", candidate, err)
513513
_ = connection.Close()
514514
connection = nil
515515
continue
516516
}
517517

518518
if serverInfo != nil {
519-
log.Printf("[debug] Retrieved supported features on node '%s' successfully", candidate)
519+
logger.debug("retrieved supported features on node '%s' successfully", candidate)
520520
} else {
521-
log.Printf("[debug] Selected node '%s' doesn't support a supported features endpoint", candidate)
521+
logger.debug("selected node '%s' doesn't support a supported features endpoint", candidate)
522522
}
523523

524524
break

esdb/logging.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package esdb
2+
3+
import (
4+
"fmt"
5+
"log"
6+
"strings"
7+
)
8+
9+
type LogLevel = string
10+
11+
const (
12+
LogDebug LogLevel = "debug"
13+
LogInfo LogLevel = "info"
14+
LogWarn LogLevel = "warn"
15+
LogError LogLevel = "error"
16+
)
17+
18+
type LoggingFunc = func(level LogLevel, format string, args ...interface{})
19+
20+
func ConsoleLogging() LoggingFunc {
21+
return func(level LogLevel, format string, args ...interface{}) {
22+
scoped := fmt.Sprintf("[%s]", level)
23+
format = strings.Join([]string{scoped, format}, " ")
24+
log.Printf(format, args...)
25+
}
26+
}
27+
28+
func NoopLogging() LoggingFunc {
29+
return func(scope string, format string, args ...interface{}) {
30+
31+
}
32+
}
33+
34+
type logger struct {
35+
callback LoggingFunc
36+
}
37+
38+
func (log *logger) error(format string, args ...interface{}) {
39+
if log.callback != nil {
40+
log.callback(LogError, format, args...)
41+
}
42+
}
43+
44+
func (log *logger) warn(format string, args ...interface{}) {
45+
if log.callback != nil {
46+
log.callback(LogWarn, format, args...)
47+
}
48+
}
49+
50+
func (log *logger) debug(format string, args ...interface{}) {
51+
if log.callback != nil {
52+
log.callback(LogDebug, format, args...)
53+
}
54+
}
55+
56+
func (log *logger) info(format string, args ...interface{}) {
57+
if log.callback != nil {
58+
log.callback(LogInfo, format, args...)
59+
}
60+
}

esdb/persistent_subscription.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55
"fmt"
66
"sync/atomic"
77

8-
"log"
98
"sync"
109

1110
"github.com/EventStore/EventStore-Client-Go/v2/protos/persistent"
@@ -29,6 +28,7 @@ type PersistentSubscription struct {
2928
once *sync.Once
3029
closed *int32
3130
cancel context.CancelFunc
31+
logger *logger
3232
}
3333

3434
func (connection *PersistentSubscription) Recv() *PersistentSubscriptionEvent {
@@ -44,7 +44,7 @@ func (connection *PersistentSubscription) Recv() *PersistentSubscriptionEvent {
4444
if err != nil {
4545
atomic.StoreInt32(connection.closed, 1)
4646

47-
log.Printf("[error] subscription has dropped. Reason: %v", err)
47+
connection.logger.error("subscription has dropped. Reason: %v", err)
4848

4949
dropped := SubscriptionDropped{
5050
Error: err,
@@ -146,6 +146,7 @@ func NewPersistentSubscription(
146146
client persistent.PersistentSubscriptions_ReadClient,
147147
subscriptionId string,
148148
cancel context.CancelFunc,
149+
logger *logger,
149150
) *PersistentSubscription {
150151
once := new(sync.Once)
151152
closed := new(int32)
@@ -157,5 +158,6 @@ func NewPersistentSubscription(
157158
once: once,
158159
closed: closed,
159160
cancel: cancel,
161+
logger: logger,
160162
}
161163
}

esdb/persistent_subscription_client.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ func (client *persistentClient) ConnectToPersistentSubscription(
5050
asyncConnection := NewPersistentSubscription(
5151
readClient,
5252
readResult.GetSubscriptionConfirmation().SubscriptionId,
53-
cancel)
53+
cancel, client.inner.logger)
5454

5555
return asyncConnection, nil
5656
}

esdb/subscriptions.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package esdb
33
import (
44
"context"
55
"fmt"
6-
"log"
76
"sync"
87
"sync/atomic"
98

@@ -63,7 +62,7 @@ func (sub *Subscription) Recv() *SubscriptionEvent {
6362

6463
result, err := sub.inner.Recv()
6564
if err != nil {
66-
log.Printf("[error] subscription has dropped. Reason: %v", err)
65+
sub.client.grpcClient.logger.error("subscription has dropped. Reason: %v", err)
6766

6867
dropped := SubscriptionDropped{
6968
Error: err,

0 commit comments

Comments
 (0)