Skip to content

Commit 9a998fd

Browse files
authored
Improve the way the Go client logs messages (#125)
* Minor test updates * Improve the way the Go client logs messages * Minor comment * Add test for log levels * Correctly reset log writer * Bump version
1 parent 46177f2 commit 9a998fd

File tree

18 files changed

+198
-70
lines changed

18 files changed

+198
-70
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
# ----------------------------------------------------------------------------------------------------------------------
99

1010
# This is the version of the coherence-go-client
11-
VERSION ?=2.1.0
11+
VERSION ?=2.2.0
1212
CURRDIR := $(shell pwd)
1313
USER_ID := $(shell echo "`id -u`:`id -g`")
1414

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ For local development, we recommend using the Coherence CE Docker image; it cont
4444
everything necessary for the client to operate correctly.
4545

4646
```bash
47-
docker run -d -p 1408:1408 -p 30000:30000 ghcr.io/oracle/coherence-ce:24.09.3
47+
docker run -d -p 1408:1408 -p 30000:30000 ghcr.io/oracle/coherence-ce:25.03.1
4848
```
4949

5050
## Installation

coherence/common.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2022, 2024 Oracle and/or its affiliates.
2+
* Copyright (c) 2022, 2025 Oracle and/or its affiliates.
33
* Licensed under the Universal Permissive License v 1.0 as shown at
44
* https://oss.oracle.com/licenses/upl.
55
*/
@@ -35,13 +35,13 @@ const (
3535
envDisconnectTimeout = "COHERENCE_SESSION_DISCONNECT_TIMEOUT"
3636
envReadyTimeout = "COHERENCE_READY_TIMEOUT"
3737

38-
// envSessionDebug enables session debug messages to be displayed.
38+
// envSessionDebug enables session debug messages to be displayed. Deprecated, use COHERENCE_LOG_LEVEL=DEBUG instead.
3939
envSessionDebug = "COHERENCE_SESSION_DEBUG"
4040

41-
// envMessageDebug enables message debug messages to be displayed.
41+
// envMessageDebug enables message debug messages to be displayed. Deprecated, use COHERENCE_LOG_LEVEL=ALL instead.
4242
envMessageDebug = "COHERENCE_MESSAGE_DEBUG"
4343

44-
// envResolverDebug enables resolver debug messages to be displayed.
44+
// envResolverDebug enables resolver debug messages to be displayed. Deprecated, use COHERENCE_LOG_LEVEL=DEBUG instead.
4545
envResolverDebug = "COHERENCE_RESOLVER_DEBUG"
4646

4747
// envResolverDebug sets the number of retries when the resolver fails.
@@ -50,6 +50,9 @@ const (
5050
// envResolverDebug enables randomization of addresses returned by resolver
5151
envResolverRandomize = "COHERENCE_RESOLVER_RANDOMIZE"
5252

53+
// the Coherence log level: 1 -> 5 (ERROR -> ALL)
54+
envLogLevel = "COHERENCE_LOG_LEVEL"
55+
5356
// Integer.MAX_VALUE on Java
5457
integerMaxValue = 2147483647
5558

coherence/doc.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,27 @@ You also have the ability to control maximum amount of time, in milliseconds, a
9999
without successfully reconnecting. For this you use the option [coherence.WithDisconnectTimeout] or the environment
100100
variable COHERENCE_SESSION_DISCONNECT_TIMEOUT.
101101
102+
# Setting Log Levels
103+
104+
The Coherence Go client supports setting the following log levels to change verbosity of messages output.
105+
The default level is 'INFO' and this can be changed by setting the environment variable COHERENCE_LOG_LEVEL to one of the following values:
106+
107+
- ERROR
108+
109+
- WARNING
110+
111+
- INFO
112+
113+
- DEBUG
114+
115+
- ALL
116+
117+
All messages at and above the level are displayed. For example setting to WARNING will only show ERROR and WARNING messages.
118+
Where as setting DEBUG, will show ERROR, WARNING, INFO and DEBUG messages.
119+
120+
Note: Setting to ALL should only be used to diagnose issues as directed by Oracle Support. This level will
121+
output a large volume of messages.
122+
102123
# Obtaining a NamedMap or NamedCache
103124
104125
Once a session has been created, the [GetNamedMap](session, name, ...options) or [GetNamedCache](session, name, ...options)

coherence/log_level_test.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* Copyright (c) 2025 Oracle and/or its affiliates.
3+
* Licensed under the Universal Permissive License v 1.0 as shown at
4+
* https://oss.oracle.com/licenses/upl.
5+
*/
6+
7+
package coherence
8+
9+
import (
10+
"bytes"
11+
"github.com/onsi/gomega"
12+
"log"
13+
"testing"
14+
)
15+
16+
func TestErrorLogLevel(t *testing.T) {
17+
runLogLevelTest(t, ERROR, ERROR, true)
18+
runLogLevelTest(t, WARNING, ERROR, false)
19+
runLogLevelTest(t, INFO, ERROR, false)
20+
runLogLevelTest(t, DEBUG, ERROR, false)
21+
runLogLevelTest(t, ALL, ERROR, false)
22+
23+
runLogLevelTest(t, ERROR, WARNING, true)
24+
runLogLevelTest(t, WARNING, WARNING, true)
25+
runLogLevelTest(t, INFO, WARNING, false)
26+
runLogLevelTest(t, DEBUG, WARNING, false)
27+
runLogLevelTest(t, ALL, WARNING, false)
28+
29+
runLogLevelTest(t, ERROR, INFO, true)
30+
runLogLevelTest(t, WARNING, INFO, true)
31+
runLogLevelTest(t, INFO, INFO, true)
32+
runLogLevelTest(t, DEBUG, INFO, false)
33+
runLogLevelTest(t, ALL, INFO, false)
34+
35+
runLogLevelTest(t, ERROR, DEBUG, true)
36+
runLogLevelTest(t, WARNING, DEBUG, true)
37+
runLogLevelTest(t, INFO, DEBUG, true)
38+
runLogLevelTest(t, DEBUG, DEBUG, true)
39+
runLogLevelTest(t, ALL, DEBUG, false)
40+
41+
runLogLevelTest(t, ERROR, ALL, true)
42+
runLogLevelTest(t, WARNING, ALL, true)
43+
runLogLevelTest(t, INFO, ALL, true)
44+
runLogLevelTest(t, DEBUG, ALL, true)
45+
runLogLevelTest(t, ALL, ALL, true)
46+
}
47+
48+
func runLogLevelTest(t *testing.T, messageLevel, testLogLevel logLevel, expectOutput bool) {
49+
g := gomega.NewWithT(t)
50+
const message = "MESSAGE"
51+
52+
var buf bytes.Buffer
53+
54+
origOutput := log.Writer()
55+
log.SetOutput(&buf)
56+
defer log.SetOutput(origOutput)
57+
setLogLevel(testLogLevel.String())
58+
59+
logMessage(messageLevel, message)
60+
output := buf.String()
61+
62+
if expectOutput {
63+
g.Expect(output).To(gomega.ContainSubstring(message))
64+
} else {
65+
g.Expect(output).To(gomega.Not(gomega.ContainSubstring(message)))
66+
}
67+
}

coherence/named_map_client.go

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2022, 2024 Oracle and/or its affiliates.
2+
* Copyright (c) 2022, 2025 Oracle and/or its affiliates.
33
* Licensed under the Universal Permissive License v 1.0 as shown at
44
* https://oss.oracle.com/licenses/upl.
55
*/
@@ -14,7 +14,6 @@ import (
1414
"github.com/oracle/coherence-go-client/v2/coherence/extractors"
1515
"github.com/oracle/coherence-go-client/v2/coherence/filters"
1616
"github.com/oracle/coherence-go-client/v2/coherence/processors"
17-
"log"
1817
"sync"
1918
"time"
2019
)
@@ -456,7 +455,7 @@ func (nm *NamedMapClient[K, V]) Release() {
456455
if nm.baseClient.nearCacheListener != nil {
457456
err := nm.RemoveListener(context.Background(), nm.baseClient.nearCacheListener.listener)
458457
if err != nil {
459-
log.Printf("unable to remove listener to near cache: %v", err)
458+
logMessage(WARNING, "unable to remove listener to near cache: %v", err)
460459
}
461460
}
462461

@@ -995,7 +994,7 @@ func newNamedMapReconnectListener[K comparable, V any](nm NamedMapClient[K, V])
995994
// re-register listeners for the NamedMap
996995
namedMap := convertNamedMapClient[K, V](&nm)
997996
if err := reRegisterListeners[K, V](context.Background(), &namedMap, nm.baseClient); err != nil {
998-
log.Println(err)
997+
logMessage(WARNING, "unable to re-register listeners: %v", err)
999998
}
1000999
})
10011000

@@ -1057,7 +1056,7 @@ func newNearNamedMapMapLister[K comparable, V any](nc NamedMapClient[K, V], cach
10571056
listener.listener.OnAny(func(e MapEvent[K, V]) {
10581057
err := processNearCacheEvent(nc.baseClient.nearCache, e)
10591058
if err != nil {
1060-
log.Println("Error processing near cache MapEvent", e)
1059+
logMessage(WARNING, "Error processing near cache MapEvent: %v", e)
10611060
}
10621061
})
10631062

coherence/resolver.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2022, 2024 Oracle and/or its affiliates.
2+
* Copyright (c) 2022, 2025 Oracle and/or its affiliates.
33
* Licensed under the Universal Permissive License v 1.0 as shown at
44
* https://oss.oracle.com/licenses/upl.
55
*/
@@ -219,10 +219,13 @@ func parseNsLookupString(addresses string) ([]string, error) {
219219
}
220220

221221
func checkResolverDebug() {
222-
if getBoolValueFromEnvVarOrDefault(envResolverDebug, false) {
222+
if getBoolValueFromEnvVarOrDefault(envResolverDebug, false) || currentLogLevel >= int(DEBUG) {
223223
// enable session debugging
224224
resolverDebug = func(s string, v ...any) {
225225
logMessage(DEBUG, s, v...)
226226
}
227+
if currentLogLevel <= int(DEBUG) {
228+
currentLogLevel = int(DEBUG)
229+
}
227230
}
228231
}

coherence/session.go

Lines changed: 49 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2022, 2024 Oracle and/or its affiliates.
2+
* Copyright (c) 2022, 2025 Oracle and/or its affiliates.
33
* Licensed under the Universal Permissive License v 1.0 as shown at
44
* https://oss.oracle.com/licenses/upl.
55
*/
@@ -20,7 +20,6 @@ import (
2020
"google.golang.org/grpc/credentials/insecure"
2121
"google.golang.org/grpc/resolver"
2222
"google.golang.org/grpc/status"
23-
"log"
2423
"os"
2524
"reflect"
2625
"strconv"
@@ -72,9 +71,8 @@ type Session struct {
7271
connectMutex sync.RWMutex // mutes for connection attempts
7372
firstConnectAttempted bool // indicates if the first connection has been attempted
7473
hasConnected bool // indicates if the session has ever connected
75-
debug func(string, ...any) // a function to output debug messages
76-
debugConnection func(string, ...any) // a function to output debug messages for gRPCV1 connections
77-
messageDebugMode string // either "on" or "full"
74+
debug func(string, ...any) // a function to output DEBUG messages
75+
debugConnection func(string, ...any) // a function to output ALL messages for gRPCV1 connections
7876
requestID int64 // request id for gRPC v1
7977
filterID int64 // filter id for gRPC v1
8078
v1StreamManagerCache *streamManagerV1
@@ -192,24 +190,26 @@ func NewSession(ctx context.Context, options ...func(session *SessionOptions)) (
192190
// ensure name resolver has been registered
193191
resolver.Register(&nsLookupResolverBuilder{})
194192

195-
if getBoolValueFromEnvVarOrDefault(envSessionDebug, false) {
193+
// set the coherenceLogLevel
194+
setLogLevel(getStringValueFromEnvVarOrDefault(envLogLevel, "3"))
195+
196+
if getBoolValueFromEnvVarOrDefault(envSessionDebug, false) || currentLogLevel >= int(DEBUG) {
196197
// enable session debugging
197198
session.debug = func(format string, v ...any) {
198199
logMessage(DEBUG, format, v...)
199200
}
201+
if currentLogLevel <= int(DEBUG) {
202+
currentLogLevel = int(DEBUG)
203+
}
200204
}
201205

202206
messageDebug := getStringValueFromEnvVarOrDefault(envMessageDebug, "")
203-
if messageDebug != "" {
207+
if messageDebug != "" || currentLogLevel == int(ALL) {
204208
// enable session debugging
205209
session.debugConnection = func(s string, v ...any) {
206-
msg := getLogMessage(DEBUG, s, v...)
207-
if session.messageDebugMode == "on" && len(msg) > 256 {
208-
msg = msg[:256]
209-
}
210-
log.Println(msg)
210+
logMessage(DEBUG, s, v...)
211211
}
212-
session.messageDebugMode = messageDebug
212+
currentLogLevel = int(ALL)
213213
}
214214

215215
// apply any options
@@ -257,6 +257,37 @@ func NewSession(ctx context.Context, options ...func(session *SessionOptions)) (
257257
return session, session.ensureConnection()
258258
}
259259

260+
// setLogLevel sets the log level from the COHERENCE_LOG_LEVEL environment variable.
261+
func setLogLevel(envLevel string) {
262+
var level int
263+
264+
// try to convert from integer first
265+
if lvl, err := strconv.Atoi(envLevel); err == nil {
266+
if lvl >= 1 && lvl <= 5 {
267+
currentLogLevel = lvl
268+
return
269+
}
270+
}
271+
272+
// fall-through, check for string values
273+
switch envLevel {
274+
case "ERROR":
275+
level = 1
276+
case "WARNING":
277+
level = 2
278+
case "INFO":
279+
level = 3
280+
case "DEBUG":
281+
level = 4
282+
case "ALL":
283+
level = 5
284+
default:
285+
level = 3 // INFO
286+
}
287+
288+
currentLogLevel = level
289+
}
290+
260291
func getTimeoutValue(envVar, defaultValue, description string) (time.Duration, error) {
261292
timeoutString := getStringValueFromEnvVarOrDefault(envVar, defaultValue)
262293
timeout, err := strconv.ParseInt(timeoutString, 10, 64)
@@ -418,7 +449,7 @@ func (s *Session) Close() {
418449
return newSessionLifecycleEvent(s, Closed)
419450
})
420451
if err != nil {
421-
log.Printf("unable to close session %s %v", s.sessionID, err)
452+
logMessage(WARNING, "unable to close session %s %v", s.sessionID, err)
422453
}
423454
} else {
424455
defer s.mapMutex.Unlock()
@@ -753,7 +784,7 @@ func (s *SessionOptions) createTLSOption() (grpc.DialOption, error) {
753784
// check if a tls.Config has been set and use this, otherwise continue to check for env and other options
754785
if s.TlSConfig != nil {
755786
if s.TlSConfig.InsecureSkipVerify {
756-
log.Println(insecureWarning)
787+
logMessage(WARNING, insecureWarning)
757788
}
758789
return grpc.WithTransportCredentials(credentials.NewTLS(s.TlSConfig)), nil
759790
}
@@ -774,7 +805,7 @@ func (s *SessionOptions) createTLSOption() (grpc.DialOption, error) {
774805

775806
ignoreInvalidCerts := ignoreInvalidCertsEnv == "true"
776807
if ignoreInvalidCerts {
777-
log.Println(insecureWarning)
808+
logMessage(WARNING, insecureWarning)
778809
}
779810
s.IgnoreInvalidCerts = ignoreInvalidCerts
780811

@@ -801,7 +832,7 @@ func (s *SessionOptions) createTLSOption() (grpc.DialOption, error) {
801832
if s.CaCertPath != "" {
802833
cp = x509.NewCertPool()
803834

804-
log.Println("loading CA certificate")
835+
logMessage(DEBUG, "loading CA certificate")
805836
if err = validateFilePath(s.CaCertPath); err != nil {
806837
return nil, err
807838
}
@@ -817,7 +848,7 @@ func (s *SessionOptions) createTLSOption() (grpc.DialOption, error) {
817848
}
818849

819850
if s.ClientCertPath != "" && s.ClientKeyPath != "" {
820-
log.Println("loading client certificate and key, cert=", s.ClientCertPath, "key=", s.ClientKeyPath)
851+
logMessage(DEBUG, "loading client certificate and key paths, cert=%s, key=%s", s.ClientCertPath, s.ClientKeyPath)
821852
if err = validateFilePath(s.ClientCertPath); err != nil {
822853
return nil, err
823854
}

coherence/session_test.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2022, 2024 Oracle and/or its affiliates.
2+
* Copyright (c) 2022, 2025 Oracle and/or its affiliates.
33
* Licensed under the Universal Permissive License v 1.0 as shown at
44
* https://oss.oracle.com/licenses/upl.
55
*/
@@ -9,7 +9,6 @@ package coherence
99
import (
1010
"context"
1111
"github.com/onsi/gomega"
12-
"os"
1312
"strconv"
1413
"testing"
1514
"time"
@@ -22,8 +21,6 @@ func TestSessionValidation(t *testing.T) {
2221
ctx = context.Background()
2322
)
2423

25-
os.Setenv("COHERENCE_SESSION_DEBUG", "true")
26-
2724
_, err = NewSession(ctx, WithFormat("not-json"))
2825
g.Expect(err).To(gomega.Equal(ErrInvalidFormat))
2926

0 commit comments

Comments
 (0)