Skip to content

Commit

Permalink
Feature: #2 Add Transactional Handler (#5)
Browse files Browse the repository at this point in the history
* add: transactional handler

* add: Store in context/Get from context

* add: slog handler's mock

* CI/CD: #1 Add CI/CD (#4)

* add: ci/cd workflow

* mod: go generate step

* mod: remove unused field
  • Loading branch information
miyamo2 authored Feb 26, 2024
1 parent 0baabd8 commit a69f812
Show file tree
Hide file tree
Showing 8 changed files with 677 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,5 @@

# Go workspace file
go.work

.idea/
40 changes: 40 additions & 0 deletions context.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package altnrslog

import (
"context"
"errors"
"log/slog"
)

type loggerKey struct{}

var (
// ErrInvalidHandler is returned when the handler is not a TransactionalHandler.
ErrInvalidHandler = errors.New("invalid handler")
// ErrNotStored is returned when the logger is not stored in context.Context.
ErrNotStored = errors.New("logger not stored")
)

// FromContext returns *slog.Logger with *TransactionalHandler, stored in the context.Context.
// If it does not exist, return ErrNotStored.
func FromContext(ctx context.Context) (*slog.Logger, error) {
logger, ok := ctx.Value(loggerKey{}).(*slog.Logger)
if !ok {
return nil, ErrNotStored
}
_, ok = logger.Handler().(*TransactionalHandler)
if !ok {
return nil, ErrNotStored
}
return logger, nil
}

// StoreToContext stores the *slog.Logger in context.Context.
// Logger must be set to *TransactionalHandler in the Handler
func StoreToContext(ctx context.Context, logger *slog.Logger) (context.Context, error) {
_, ok := logger.Handler().(*TransactionalHandler)
if !ok {
return ctx, ErrInvalidHandler
}
return context.WithValue(ctx, loggerKey{}, logger), nil
}
120 changes: 120 additions & 0 deletions context_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package altnrslog

import (
"context"
"errors"
"log/slog"
"reflect"
"testing"
)

func TestFromContext(t *testing.T) {
txHandler := &TransactionalHandler{}
txLogger := slog.New(txHandler)
jsonHandler := &slog.JSONHandler{}
jsonLogger := slog.New(jsonHandler)

type args struct {
ctx context.Context
}
type want struct {
logger *slog.Logger
err error
}
type test struct {
args args
want want
}
tests := map[string]test{
"happy-path": {
args: args{
ctx: context.WithValue(context.Background(), loggerKey{}, txLogger),
},
want: want{
logger: txLogger,
},
},
"unhappy-path: json logger": {
args: args{
ctx: context.WithValue(context.Background(), loggerKey{}, jsonLogger),
},
want: want{
err: ErrNotStored,
},
},
"unhappy-path: no logger": {
args: args{
ctx: context.Background(),
},
want: want{
err: ErrNotStored,
},
},
}
for name, tt := range tests {
t.Run(name, func(t *testing.T) {
got, err := FromContext(tt.args.ctx)
if !errors.Is(err, tt.want.err) {
t.Errorf("FromContext() error = %v, want %v", err, tt.want.err)
return
}
if !reflect.DeepEqual(got, tt.want.logger) {
t.Errorf("FromContext() got = %v, want %v", got, tt.want.logger)
}
})
}
}

func TestStoreToContext(t *testing.T) {
txHandler := &TransactionalHandler{}
txLogger := slog.New(txHandler)
jsonHandler := &slog.JSONHandler{}
jsonLogger := slog.New(jsonHandler)

type args struct {
ctx context.Context
logger *slog.Logger
}
type want struct {
ctx context.Context
err error
}
type test struct {
args args
want want
}

tests := map[string]test{
"happy-path": {
args: args{
ctx: context.Background(),
logger: txLogger,
},
want: want{
ctx: context.WithValue(context.Background(), loggerKey{}, txLogger),
},
},
"unhappy-path: json logger": {
args: args{
ctx: context.Background(),
logger: jsonLogger,
},
want: want{
ctx: context.Background(),
err: ErrInvalidHandler,
},
},
}
for name, tt := range tests {
t.Run(name, func(t *testing.T) {
got, err := StoreToContext(tt.args.ctx, tt.args.logger)
if !errors.Is(err, tt.want.err) {
t.Errorf("StoreToContext() error = %v, want %v", err, tt.want.err)
return
}
if !reflect.DeepEqual(got, tt.want.ctx) {
t.Errorf("StoreToContext() got = %v, want %v", got, tt.want)
}
})
}
}
21 changes: 21 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
module github.com/miyamo2/altnrslog

go 1.21

require (
github.com/google/go-cmp v0.6.0
github.com/newrelic/go-agent/v3 v3.30.0
github.com/newrelic/go-agent/v3/integrations/logcontext-v2/logWriter v1.0.1
go.uber.org/mock v0.4.0
)

require (
github.com/golang/protobuf v1.5.3 // indirect
github.com/newrelic/go-agent/v3/integrations/logcontext-v2/nrwriter v1.0.0 // indirect
golang.org/x/net v0.9.0 // indirect
golang.org/x/sys v0.7.0 // indirect
golang.org/x/text v0.9.0 // indirect
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect
google.golang.org/grpc v1.56.3 // indirect
google.golang.org/protobuf v1.30.0 // indirect
)
29 changes: 29 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/newrelic/go-agent/v3 v3.30.0 h1:ZXHCT/Cot4iIPwcegCZURuRQOsfmGA6wilW+S3bfBjY=
github.com/newrelic/go-agent/v3 v3.30.0/go.mod h1:9utrgxlSryNqRrTvII2XBL+0lpofXbqXApvVWPpbzUg=
github.com/newrelic/go-agent/v3/integrations/logcontext-v2/logWriter v1.0.1 h1:QHUFxBPgC8YYcCRCGfteWJUnaQjetEFTmd67KQ44t1M=
github.com/newrelic/go-agent/v3/integrations/logcontext-v2/logWriter v1.0.1/go.mod h1:eATzimYSQLsG+VFK2xJXWmniC1+zr44K7Owz5WEAoqc=
github.com/newrelic/go-agent/v3/integrations/logcontext-v2/nrwriter v1.0.0 h1:ugrng2OpXAEmwCQgLNmIGM8m0MZiitpswBVotVjyivA=
github.com/newrelic/go-agent/v3/integrations/logcontext-v2/nrwriter v1.0.0/go.mod h1:5+hmfTxwzTj022CzgB8RpMZeY4AVBav25MvcTKSX/vg=
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A=
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU=
google.golang.org/grpc v1.56.3 h1:8I4C0Yq1EjstUzUJzpcRVbuYA2mODtEmpWiQoN/b2nc=
google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
97 changes: 97 additions & 0 deletions mock/slog_handler.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit a69f812

Please sign in to comment.