Skip to content

Commit

Permalink
Add slog adapter
Browse files Browse the repository at this point in the history
  • Loading branch information
Quinn-With-Two-Ns committed Aug 30, 2023
1 parent a7f9cdf commit a6a5910
Show file tree
Hide file tree
Showing 6 changed files with 159 additions and 2 deletions.
5 changes: 3 additions & 2 deletions internal/internal_event_handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -239,15 +239,16 @@ func newWorkflowExecutionEventHandler(
mutableSideEffectCallCounter: make(map[string]int),
sdkFlags: newSDKFlags(capabilities),
}
context.logger = ilog.NewReplayLogger(
// Attempt to skip 1 log level to remove the ReplayLogger from the stack.
context.logger = log.Skip(ilog.NewReplayLogger(
log.With(logger,
tagWorkflowType, workflowInfo.WorkflowType.Name,
tagWorkflowID, workflowInfo.WorkflowExecution.ID,
tagRunID, workflowInfo.WorkflowExecution.RunID,
tagAttempt, workflowInfo.Attempt,
),
&context.isReplay,
&context.enableLoggingInReplay)
&context.enableLoggingInReplay), 1)

if metricsHandler != nil {
context.metricsHandler = metrics.NewReplayAwareHandler(&context.isReplay, metricsHandler).
Expand Down
8 changes: 8 additions & 0 deletions internal/log/replay_logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,3 +80,11 @@ func (l *ReplayLogger) Error(msg string, keyvals ...interface{}) {
func (l *ReplayLogger) With(keyvals ...interface{}) log.Logger {
return NewReplayLogger(log.With(l.logger, keyvals...), l.isReplay, l.enableLoggingInReplay)
}

// AddCallerSkip increases the caller skip depth if the underlying logger supports it.
func (l *ReplayLogger) AddCallerSkip(depth int) log.Logger {
if sl, ok := l.logger.(log.WithSkipCallers); ok {
return NewReplayLogger(sl.AddCallerSkip(depth), l.isReplay, l.enableLoggingInReplay)
}
return l
}
6 changes: 6 additions & 0 deletions log/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,10 @@ type (
Warn(msg string, keyvals ...interface{})
Error(msg string, keyvals ...interface{})
}

// WithSkipCallers is an optional interface that a Logger can implement that
// indicate the number of stack frames to skip.
WithSkipCallers interface {
AddCallerSkip(int) Logger
}
)
94 changes: 94 additions & 0 deletions log/slog.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// The MIT License
//
// Copyright (c) 2020 Temporal Technologies Inc. All rights reserved.
//
// Copyright (c) 2020 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

//go:build go1.21

package log

import (
"context"
"log/slog"
"runtime"
"time"
)

type slogLogger struct {
logger *slog.Logger
depth int
}

// NewStructuredLogger creates an adapter around the given logger to be passed to Temporal.
func NewStructuredLogger(logger *slog.Logger) Logger {
return &slogLogger{
logger: logger,
depth: 3,
}
}

func (s *slogLogger) Debug(msg string, keyvals ...interface{}) {
s.log(context.Background(), slog.LevelDebug, msg, keyvals...)
}

func (s *slogLogger) Info(msg string, keyvals ...interface{}) {
s.log(context.Background(), slog.LevelInfo, msg, keyvals...)
}

func (s *slogLogger) Warn(msg string, keyvals ...interface{}) {
s.log(context.Background(), slog.LevelWarn, msg, keyvals...)
}

func (s *slogLogger) Error(msg string, keyvals ...interface{}) {
s.log(context.Background(), slog.LevelError, msg, keyvals...)
}

func (s *slogLogger) log(ctx context.Context, level slog.Level, msg string, args ...any) {
if !s.logger.Enabled(ctx, level) {
return
}

var pcs [1]uintptr
runtime.Callers(s.depth, pcs[:])

record := slog.NewRecord(time.Now(), level, msg, pcs[0])
record.Add(args...)

if ctx == nil {
ctx = context.Background()
}
_ = s.logger.Handler().Handle(ctx, record)
}

func (s *slogLogger) With(keyvals ...interface{}) Logger {
return &slogLogger{
logger: s.logger.With(keyvals...),
depth: s.depth,
}
}

func (s *slogLogger) AddCallerSkip(depth int) Logger {
return &slogLogger{
logger: s.logger,
depth: s.depth + depth,
}
}
31 changes: 31 additions & 0 deletions log/slog_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// The MIT License
//
// Copyright (c) 2020 Temporal Technologies Inc. All rights reserved.
//
// Copyright (c) 2020 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

//go:build go1.21

package log

import "testing"

func TestSlogAddapter(t *testing.T) {}
17 changes: 17 additions & 0 deletions log/with_logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,15 @@ func With(logger Logger, keyvals ...interface{}) Logger {
return newWithLogger(logger, keyvals...)
}

// Skip creates a child Logger that increase increases its' caller skip depth if it
// implements [WithSkipCallers]. Otherwise returns the original logger.
func Skip(logger Logger, depth int) Logger {
if sl, ok := logger.(WithSkipCallers); ok {
return sl.AddCallerSkip(depth)
}
return logger
}

type withLogger struct {
logger Logger
keyvals []interface{}
Expand Down Expand Up @@ -71,3 +80,11 @@ func (l *withLogger) Warn(msg string, keyvals ...interface{}) {
func (l *withLogger) Error(msg string, keyvals ...interface{}) {
l.logger.Error(msg, l.prependKeyvals(keyvals)...)
}

// AddCallerSkip increases the caller skip depth if the underlying logger supports it.
func (l *withLogger) AddCallerSkip(depth int) Logger {
if sl, ok := l.logger.(WithSkipCallers); ok {
return newWithLogger(sl.AddCallerSkip(depth), l.keyvals)
}
return l
}

0 comments on commit a6a5910

Please sign in to comment.