Skip to content

Commit

Permalink
[Off-chain, Polylog] feat: polylog context integration (#218)
Browse files Browse the repository at this point in the history
* chore: add `Level` interface

* test: add testutils

* chore: improve interface godocs

* refactor: function signatures to types

* fix: isolate #Func() sub-test

* chore: add zerolog logger implementation

* chore: comment out flaky scenario & add TODO comment

* refactor: mv pkg/polylog/polyzero/doc.go pkg/polylog/polyzero/godoc.go

* refactor: mv pkg/polylog/doc.go pkg/polylog/godoc.go

* fix: `polyzero.zerologLogger#WithContext()` - test forthcoming in #218

* fix: add missing `polylog.CtxKey` const

* feat: add `polylog.Ctx()` function

* chore: add `polyzero.GetZerologLogger()` w/ `test` build constraint

* chore: add test build tag to golangci-lint run call

* test: add testable example

* chore: setup `polylog.DefaultContextLogger`

* chore: update godoc comment

* chore: review feedback improvements

Co-authored-by: Daniel Olshansky <[email protected]>

* chore: review feedback improvements

Co-authored-by: Daniel Olshansky <[email protected]>

* chore: review feedback improvements

* chore: review feedback improvements

* fix: goimports

* fix: test

* chore: review feedback improvements

Co-authored-by: Daniel Olshansky <[email protected]>

* chore: chore: review feedback improvements

Co-authored-by: Daniel Olshansky <[email protected]>

* chore: add event method names to test cases

* chore: update go.mod

* chore: rename `FnMethodSpy` -> `EventFuncSpy`

Co-authored-by: Daniel Olshansky <[email protected]>

* refactor: rename `FnMethodSpy` -> `EventFuncSpy`

* chore: review feedback improvements

Co-authored-by: Daniel Olshansky <[email protected]>

* chore: review feedback improvements

Co-authored-by: Daniel Olshansky <[email protected]>

---------

Co-authored-by: Daniel Olshansky <[email protected]>
  • Loading branch information
bryanchriswhite and Olshansk authored Dec 5, 2023
1 parent 4b2b844 commit e36097e
Show file tree
Hide file tree
Showing 6 changed files with 151 additions and 2 deletions.
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ localnet_regenesis: ## Regenerate the localnet genesis file

.PHONY: go_lint
go_lint: ## Run all go linters
golangci-lint run --timeout 5m
golangci-lint run --timeout 5m --build-tags test

go_imports: check_go_version ## Run goimports on all go files
go run ./tools/scripts/goimports
Expand All @@ -139,7 +139,7 @@ test_e2e: ## Run all E2E tests
export POCKET_NODE=$(POCKET_NODE) && \
export APPGATE_SERVER=$(APPGATE_SERVER) && \
POKTROLLD_HOME=../../$(POKTROLLD_HOME) && \
go test -v ./e2e/tests/... -tags=e2e
go test -v ./e2e/tests/... -tags=e2e,test

.PHONY: go_test_verbose
go_test_verbose: check_go_version ## Run all go tests verbosely
Expand Down
30 changes: 30 additions & 0 deletions pkg/polylog/context.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,39 @@
package polylog

import "context"

// CtxKey is the key used to store the polylog.Logger in a context.Context. This
// is **independant** of any logger-implementation-specific context key that may
// be used internal to any of the logger implementations. Polylog attempts to
// provide a ubiquitous interface for storing and retrieving loggers from the
// context but also to integrate with the underlying logger implementations as
// seamlessly as possible.
const CtxKey = "polylog/context"

// DefaultContextLogger is the default logger implementation used when no logger
// is associated with a context. It is assigned in the implementation package's
// init() function to avoid potentially creating import cycles.
// The default logger implementation is zerolog (i.e. pkg/polylog/polyzero).
//
// IMPORTANT: In order for the default to be populated, the polyzero package MUST
// be part of the build. Otherwise, the polyzero package's init function will
// neither be included in the build nor executed. If no such import exists, the
// polyzero package can be imported for side effects only, e.g.:
//
// import _ "github.com/pokt-network/poktroll/pkg/polylog/polyzero"
var DefaultContextLogger Logger

// Ctx returns the Logger associated with the ctx. If no logger is associated,
// DefaultContextLogger is returned, unless DefaultContextLogger is nil, in which
// case a disabled logger is returned.
//
// To get a context which is associated a given logger, call the respective logger's
// #WithContext() method. Then this function can be used to retrieve it from that
// (or a context derived from that) context, later and elsewhere.
func Ctx(ctx context.Context) Logger {
logger, ok := ctx.Value(CtxKey).(Logger)
if !ok {
return DefaultContextLogger
}
return logger
}
43 changes: 43 additions & 0 deletions pkg/polylog/context_example_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package polylog_test

import (
"context"
"os"

"github.com/rs/zerolog"

"github.com/pokt-network/poktroll/pkg/polylog"
"github.com/pokt-network/poktroll/pkg/polylog/polyzero"
)

func ExampleCtx() {
// Use whichever zerolog level you need.
level := zerolog.InfoLevel

// Specify the lowest level to log. I.e.: calls to level methods "lower"
// than this will be ignored.
levelOpt := polyzero.WithLevel(level)

// Construct a context, this is typically received as an argument.
ctx := context.Background()

// Construct expectedLogger.
// NB: adding WithOutput is optional; defaults to os.Stderr. It is needed
// here to print to stdout for testable example purposes.
expectedLogger := polyzero.NewLogger(levelOpt, polyzero.WithOutput(os.Stdout))

// Add fields to the expectedLogger's context so that we can identify it in the output.
expectedLogger = expectedLogger.With("label", "my_test_logger")

// Associate the expectedLogger with the context and update the context reference.
ctx = expectedLogger.WithContext(ctx)

// Retrieve the logger from the context.
retrievedLogger := polylog.Ctx(ctx)

// Log and check that the output matches our expectations.
retrievedLogger.Info().Msg("info message")

// Output:
// {"level":"info","label":"my_test_logger","message":"info message"}
}
43 changes: 43 additions & 0 deletions pkg/polylog/context_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package polylog_test

import (
"context"
"testing"

"github.com/rs/zerolog"
"github.com/stretchr/testify/require"

"github.com/pokt-network/poktroll/pkg/polylog"
"github.com/pokt-network/poktroll/pkg/polylog/polyzero"
)

func TestWithContext_Ctx(t *testing.T) {
var (
expectedLogger = polyzero.NewLogger()
ctx = context.Background()
)

// Ensure that no logger is associated with the context.
existingLogger, ok := ctx.Value(polylog.CtxKey).(polylog.Logger)
require.False(t, ok)
require.Nil(t, existingLogger)

// Retrieve the default logger from the context using polylog and assert
// that it matches the default context logger.
defaultLogger := polylog.Ctx(ctx)
require.Equal(t, polylog.DefaultContextLogger, defaultLogger)

// Associate a logger with a context.
ctx = expectedLogger.WithContext(ctx)

// Retrieve the associated logger from the context using polylog and assert
// that it matches the one constructed at the beginning of the test.
actualLogger := polylog.Ctx(ctx)
require.Equal(t, expectedLogger, actualLogger)

// Retrieve the associated logger from the context using zerolog and assert
// that it matches the one constructed at the beginning of the test.
actualZerologLogger := zerolog.Ctx(ctx)
expectedZerologLogger := polyzero.GetZerologLogger(expectedLogger)
require.Equal(t, expectedZerologLogger, actualZerologLogger)
}
15 changes: 15 additions & 0 deletions pkg/polylog/polyzero/default.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package polyzero

import "github.com/pokt-network/poktroll/pkg/polylog"

func init() {
// Set the default logger to a polyzero logger. This is the logger which will
// be returned when calling polylog.Ctx() with a context which has no logger
// associated.
//
// This is assigned here to avoid an import cycle. Note that dependency init
// functions are called before dependents. It is therefore safe to override
// this default logger assignment in polylog consumer code, including in
// consumer package init functions.
polylog.DefaultContextLogger = NewLogger()
}
18 changes: 18 additions & 0 deletions pkg/polylog/polyzero/test_logger.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//go:build test

package polyzero

import (
"github.com/rs/zerolog"

"github.com/pokt-network/poktroll/pkg/polylog"
)

// GetZerologLogger is a helper function which provides direct access to the
// underlying zerolog logger for testing purposes; e.g. use in assertions. To use
// this helper, ensure that the build tag/constraint "test" is set (e.g. `go build -tags=test`).
// It MUST be defined in this package (as opposed to somewhere in testutils), as
// by definition, it references unexported members of this package.
func GetZerologLogger(polylogger polylog.Logger) *zerolog.Logger {
return &polylogger.(*zerologLogger).Logger
}

0 comments on commit e36097e

Please sign in to comment.