Skip to content

Commit 5043b24

Browse files
feat!: simplify and improve logging hook (#372)
* refactor: fix logging hook output Signed-off-by: Sahid Velji <[email protected]> * fix: make consts unexported and camelCase Signed-off-by: Sahid Velji <[email protected]> * refactor: remove `NewCustomLoggingHook` in favor of `NewLoggingHook` Signed-off-by: Sahid Velji <[email protected]> * fixup: func name Signed-off-by: Todd Baert <[email protected]> --------- Signed-off-by: Sahid Velji <[email protected]> Signed-off-by: Todd Baert <[email protected]> Co-authored-by: Todd Baert <[email protected]>
1 parent 36241dc commit 5043b24

File tree

2 files changed

+43
-67
lines changed

2 files changed

+43
-67
lines changed

openfeature/hooks/logging_hook.go

Lines changed: 31 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,18 @@ import (
88
)
99

1010
const (
11-
DOMAIN_KEY = "domain"
12-
PROVIDER_NAME_KEY = "provider_name"
13-
FLAG_KEY_KEY = "flag_key"
14-
DEFAULT_VALUE_KEY = "default_value"
15-
EVALUATION_CONTEXT_KEY = "evaluation_context"
16-
ERROR_MESSAGE_KEY = "error_message"
17-
REASON_KEY = "reason"
18-
VARIANT_KEY = "variant"
19-
VALUE_KEY = "value"
20-
STAGE_KEY = "stage"
11+
domainKey = "domain"
12+
providerNameKey = "provider_name"
13+
flagKeyKey = "flag_key"
14+
defaultValueKey = "default_value"
15+
evaluationContextKey = "evaluation_context"
16+
targetingKeyKey = "targeting_key"
17+
attributesKey = "attributes"
18+
errorMessageKey = "error_message"
19+
reasonKey = "reason"
20+
variantKey = "variant"
21+
valueKey = "value"
22+
stageKey = "stage"
2123
)
2224

2325
// LoggingHook is a [of.Hook] that logs the flag evaluation lifecycle.
@@ -26,46 +28,35 @@ type LoggingHook struct {
2628
logger *slog.Logger
2729
}
2830

29-
// NewLoggingHook returns a new [LoggingHook] with the default logger.
30-
// To provide a custom logger, use [NewCustomLoggingHook].
31-
func NewLoggingHook(includeEvaluationContext bool) (*LoggingHook, error) {
32-
return NewCustomLoggingHook(includeEvaluationContext, slog.Default())
33-
}
34-
35-
// NewCustomLoggingHook returns a new [LoggingHook] with the provided logger.
36-
func NewCustomLoggingHook(includeEvaluationContext bool, logger *slog.Logger) (*LoggingHook, error) {
31+
// NewLoggingHook returns a new [LoggingHook] with the provided logger.
32+
func NewLoggingHook(includeEvaluationContext bool, logger *slog.Logger) *LoggingHook {
3733
return &LoggingHook{
3834
logger: logger,
3935
includeEvaluationContext: includeEvaluationContext,
40-
}, nil
41-
}
42-
43-
type MarshaledEvaluationContext struct {
44-
TargetingKey string
45-
Attributes map[string]any
36+
}
4637
}
4738

4839
func (h *LoggingHook) buildArgs(hookContext of.HookContext) []slog.Attr {
4940
args := []slog.Attr{
50-
slog.String(DOMAIN_KEY, hookContext.ClientMetadata().Domain()),
51-
slog.String(PROVIDER_NAME_KEY, hookContext.ProviderMetadata().Name),
52-
slog.String(FLAG_KEY_KEY, hookContext.FlagKey()),
53-
slog.Any(DEFAULT_VALUE_KEY, hookContext.DefaultValue()),
41+
slog.String(domainKey, hookContext.ClientMetadata().Domain()),
42+
slog.String(providerNameKey, hookContext.ProviderMetadata().Name),
43+
slog.String(flagKeyKey, hookContext.FlagKey()),
44+
slog.Any(defaultValueKey, hookContext.DefaultValue()),
5445
}
5546
if h.includeEvaluationContext {
56-
marshaledEvaluationContext := MarshaledEvaluationContext{
57-
TargetingKey: hookContext.EvaluationContext().TargetingKey(),
58-
Attributes: hookContext.EvaluationContext().Attributes(),
59-
}
60-
args = append(args, slog.Any(EVALUATION_CONTEXT_KEY, marshaledEvaluationContext))
47+
args = append(args,
48+
slog.Group(evaluationContextKey,
49+
slog.String(targetingKeyKey, hookContext.EvaluationContext().TargetingKey()),
50+
slog.Any(attributesKey, hookContext.EvaluationContext().Attributes()),
51+
))
6152
}
6253

6354
return args
6455
}
6556

6657
func (h *LoggingHook) Before(ctx context.Context, hookContext of.HookContext, hookHints of.HookHints) (*of.EvaluationContext, error) {
6758
args := h.buildArgs(hookContext)
68-
args = append(args, slog.String(STAGE_KEY, "before"))
59+
args = append(args, slog.String(stageKey, "before"))
6960
h.logger.LogAttrs(ctx, slog.LevelDebug, "Before stage", args...)
7061
return nil, nil
7162
}
@@ -75,10 +66,10 @@ func (h *LoggingHook) After(ctx context.Context, hookContext of.HookContext,
7566
) error {
7667
args := h.buildArgs(hookContext)
7768
args = append(args,
78-
slog.String(REASON_KEY, string(flagEvaluationDetails.Reason)),
79-
slog.String(VARIANT_KEY, flagEvaluationDetails.Variant),
80-
slog.Any(VALUE_KEY, flagEvaluationDetails.Value),
81-
slog.String(STAGE_KEY, "after"),
69+
slog.String(reasonKey, string(flagEvaluationDetails.Reason)),
70+
slog.String(variantKey, flagEvaluationDetails.Variant),
71+
slog.Any(valueKey, flagEvaluationDetails.Value),
72+
slog.String(stageKey, "after"),
8273
)
8374
h.logger.LogAttrs(ctx, slog.LevelDebug, "After stage", args...)
8475
return nil
@@ -87,8 +78,8 @@ func (h *LoggingHook) After(ctx context.Context, hookContext of.HookContext,
8778
func (h *LoggingHook) Error(ctx context.Context, hookContext of.HookContext, err error, hookHints of.HookHints) {
8879
args := h.buildArgs(hookContext)
8980
args = append(args,
90-
slog.Any(ERROR_MESSAGE_KEY, err),
91-
slog.String(STAGE_KEY, "error"),
81+
slog.Any(errorMessageKey, err),
82+
slog.String(stageKey, "error"),
9283
)
9384
h.logger.LogAttrs(ctx, slog.LevelError, "Error stage", args...)
9485
}

openfeature/hooks/logging_hook_test.go

Lines changed: 12 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -13,21 +13,15 @@ import (
1313
)
1414

1515
func TestCreateLoggingHookWithDefaultLoggerAndContextInclusion(t *testing.T) {
16-
hook, err := NewLoggingHook(true)
17-
if err != nil {
18-
t.Fatalf("expected no error, got %v", err)
19-
}
16+
hook := NewLoggingHook(true, slog.Default())
2017
if hook == nil {
2118
t.Fatal("expected a valid LoggingHook, got nil")
2219
}
2320
}
2421

2522
func TestLoggingHookInitializesCorrectly(t *testing.T) {
2623
logger := slog.New(slog.NewJSONHandler(os.Stderr, nil))
27-
hook, err := NewCustomLoggingHook(true, logger)
28-
if err != nil {
29-
t.Error("expected no error")
30-
}
24+
hook := NewLoggingHook(true, logger)
3125

3226
if hook.logger != logger {
3327
t.Errorf("Expected logger to be %v, got %v", logger, hook.logger)
@@ -39,10 +33,7 @@ func TestLoggingHookInitializesCorrectly(t *testing.T) {
3933
}
4034

4135
func TestLoggingHookHandlesNilLoggerGracefully(t *testing.T) {
42-
hook, err := NewCustomLoggingHook(false, nil)
43-
if err != nil {
44-
t.Error("expected no error")
45-
}
36+
hook := NewLoggingHook(false, nil)
4637

4738
if hook.logger != nil {
4839
t.Errorf("Expected logger to be nil, got %v", hook.logger)
@@ -58,24 +49,18 @@ func TestLoggingHookLogsMessagesAsExpected(t *testing.T) {
5849
handler := slog.NewJSONHandler(buf, &slog.HandlerOptions{Level: slog.LevelDebug})
5950
logger := slog.New(handler)
6051

61-
hook, err := NewCustomLoggingHook(false, logger)
62-
if err != nil {
63-
t.Error("expected no error")
64-
}
52+
hook := NewLoggingHook(false, logger)
6553

6654
// Check if resultMap contains all key-value pairs from expected
6755
testLoggingHookLogsMessagesAsExpected(*hook, logger, t, buf)
6856
}
6957

7058
func TestLoggingHookLogsMessagesAsExpectedIncludeEvaluationContext(t *testing.T) {
71-
var buf = new(bytes.Buffer)
59+
buf := new(bytes.Buffer)
7260
handler := slog.NewJSONHandler(buf, &slog.HandlerOptions{Level: slog.LevelDebug})
7361
logger := slog.New(handler)
7462

75-
hook, err := NewCustomLoggingHook(true, logger)
76-
if err != nil {
77-
t.Error("expected no error")
78-
}
63+
hook := NewLoggingHook(true, logger)
7964

8065
// Check if resultMap contains all key-value pairs from expected
8166
testLoggingHookLogsMessagesAsExpected(*hook, logger, t, buf)
@@ -184,7 +169,7 @@ func testLoggingHookLogsMessagesAsExpected(hook LoggingHook, logger *slog.Logger
184169
}
185170

186171
func prepareOutput(buf *bytes.Buffer, t *testing.T) map[string]map[string]any {
187-
var ms = make(map[string]map[string]any)
172+
ms := make(map[string]map[string]any)
188173
for _, line := range bytes.Split(buf.Bytes(), []byte{'\n'}) {
189174
if len(line) == 0 {
190175
continue
@@ -216,11 +201,11 @@ func compare(expected map[string]map[string]any, ms map[string]map[string]any, t
216201
}
217202

218203
if hook.includeEvaluationContext {
219-
evaluationContext, exists := resultInnerMap[EVALUATION_CONTEXT_KEY]
204+
evaluationContext, exists := resultInnerMap[evaluationContextKey]
220205
if !exists {
221-
t.Errorf("Inner key %s not found in resultMap[%s]", EVALUATION_CONTEXT_KEY, key)
206+
t.Errorf("Inner key %s not found in resultMap[%s]", evaluationContextKey, key)
222207
}
223-
attributes, attributesExists := evaluationContext.(map[string]any)["Attributes"]
208+
attributes, attributesExists := evaluationContext.(map[string]any)[attributesKey]
224209
if !attributesExists {
225210
t.Errorf("attributes do not exist")
226211
}
@@ -231,8 +216,8 @@ func compare(expected map[string]map[string]any, ms map[string]map[string]any, t
231216
if color != "green" {
232217
t.Errorf("expected green color in evaluationContext")
233218
}
234-
if evaluationContext.(map[string]any)["TargetingKey"] != "target1" {
235-
t.Errorf("expected TargetingKey in evaluationContext")
219+
if evaluationContext.(map[string]any)[targetingKeyKey] != "target1" {
220+
t.Errorf("expected targeting_key in evaluationContext")
236221
}
237222
}
238223
}

0 commit comments

Comments
 (0)