Skip to content

Commit

Permalink
Add advanced logging (#11)
Browse files Browse the repository at this point in the history
  • Loading branch information
bbengfort authored Jan 5, 2024
1 parent c9a3eef commit d8b08ad
Show file tree
Hide file tree
Showing 9 changed files with 501 additions and 31 deletions.
20 changes: 15 additions & 5 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,20 @@ import (
"crypto/x509"

"github.com/rotationalio/confire"
"github.com/rs/zerolog"
"github.com/trisacrypto/courier/pkg/logger"
"github.com/trisacrypto/trisa/pkg/trust"
)

type Config struct {
BindAddr string `split_words:"true" default:":8842"`
Mode string `split_words:"true" default:"release"`
MTLS MTLSConfig `split_words:"true"`
LocalStorage LocalStorageConfig `split_words:"true"`
GCPSecretManager GCPSecretsConfig `split_words:"true"`
Maintenance bool `default:"false"`
BindAddr string `split_words:"true" default:":8842"`
Mode string `split_words:"true" default:"release"`
LogLevel logger.LevelDecoder `split_words:"true" default:"info"`
ConsoleLog bool `split_words:"true" default:"false"`
MTLS MTLSConfig `split_words:"true"`
LocalStorage LocalStorageConfig `split_words:"true"`
GCPSecretManager GCPSecretsConfig `split_words:"true"`
processed bool
}

Expand Down Expand Up @@ -95,6 +100,11 @@ func (c Config) Validate() (err error) {
return nil
}

// Parse and return the zerolog log level for configuring global logging.
func (c Config) GetLogLevel() zerolog.Level {
return zerolog.Level(c.LogLevel)
}

func (c *MTLSConfig) Validate() error {
if c.Insecure {
return nil
Expand Down
7 changes: 7 additions & 0 deletions pkg/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,18 @@ import (
"os"
"testing"

"github.com/rs/zerolog"
"github.com/stretchr/testify/require"
"github.com/trisacrypto/courier/pkg/config"
)

// Define a test environment for the config tests.
var testEnv = map[string]string{
"COURIER_MAINTENANCE": "true",
"COURIER_BIND_ADDR": ":8080",
"COURIER_MODE": "debug",
"COURIER_LOG_LEVEL": "warn",
"COURIER_CONSOLE_LOG": "true",
"COURIER_MTLS_INSECURE": "false",
"COURIER_MTLS_CERT_PATH": "/path/to/cert",
"COURIER_MTLS_POOL_PATH": "/path/to/pool",
Expand Down Expand Up @@ -40,8 +44,11 @@ func TestConfig(t *testing.T) {
require.NoError(t, err, "could not create config from test environment")
require.False(t, conf.IsZero(), "config should be processed")

require.True(t, conf.Maintenance)
require.Equal(t, testEnv["COURIER_BIND_ADDR"], conf.BindAddr)
require.Equal(t, testEnv["COURIER_MODE"], conf.Mode)
require.Equal(t, zerolog.WarnLevel, conf.GetLogLevel())
require.True(t, conf.ConsoleLog)
require.False(t, conf.MTLS.Insecure)
require.Equal(t, testEnv["COURIER_MTLS_CERT_PATH"], conf.MTLS.CertPath)
require.Equal(t, testEnv["COURIER_MTLS_POOL_PATH"], conf.MTLS.PoolPath)
Expand Down
92 changes: 92 additions & 0 deletions pkg/logger/level.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package logger

import (
"encoding/json"
"fmt"
"strings"

"github.com/rs/zerolog"
)

// LogLevelDecoder deserializes the log level from a config string.
type LevelDecoder zerolog.Level

// Names of log levels for use in encoding/decoding from strings.
const (
llPanic = "panic"
llFatal = "fatal"
llError = "error"
llWarn = "warn"
llInfo = "info"
llDebug = "debug"
llTrace = "trace"
)

// Decode implements confire Decoder interface.
func (ll *LevelDecoder) Decode(value string) error {
value = strings.TrimSpace(strings.ToLower(value))
switch value {
case llPanic:
*ll = LevelDecoder(zerolog.PanicLevel)
case llFatal:
*ll = LevelDecoder(zerolog.FatalLevel)
case llError:
*ll = LevelDecoder(zerolog.ErrorLevel)
case llWarn:
*ll = LevelDecoder(zerolog.WarnLevel)
case llInfo:
*ll = LevelDecoder(zerolog.InfoLevel)
case llDebug:
*ll = LevelDecoder(zerolog.DebugLevel)
case llTrace:
*ll = LevelDecoder(zerolog.TraceLevel)
default:
return fmt.Errorf("unknown log level %q", value)
}
return nil
}

// Encode converts the loglevel into a string for use in YAML and JSON
func (ll *LevelDecoder) Encode() (string, error) {
switch zerolog.Level(*ll) {
case zerolog.PanicLevel:
return llPanic, nil
case zerolog.FatalLevel:
return llFatal, nil
case zerolog.ErrorLevel:
return llError, nil
case zerolog.WarnLevel:
return llWarn, nil
case zerolog.InfoLevel:
return llInfo, nil
case zerolog.DebugLevel:
return llDebug, nil
case zerolog.TraceLevel:
return llTrace, nil
default:
return "", fmt.Errorf("unknown log level %d", ll)
}
}

func (ll LevelDecoder) String() string {
ls, _ := ll.Encode()
return ls
}

// UnmarshalJSON implements json.Unmarshaler
func (ll *LevelDecoder) UnmarshalJSON(data []byte) error {
var ls string
if err := json.Unmarshal(data, &ls); err != nil {
return err
}
return ll.Decode(ls)
}

// MarshalJSON implements json.Marshaler
func (ll LevelDecoder) MarshalJSON() ([]byte, error) {
ls, err := ll.Encode()
if err != nil {
return nil, err
}
return json.Marshal(ls)
}
85 changes: 85 additions & 0 deletions pkg/logger/level_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package logger_test

import (
"encoding/json"
"fmt"
"testing"

"github.com/rs/zerolog"
"github.com/stretchr/testify/require"
"github.com/trisacrypto/courier/pkg/logger"
)

func TestLevelDecoder(t *testing.T) {
testTable := []struct {
value string
expected zerolog.Level
}{
{
"panic", zerolog.PanicLevel,
},
{
"FATAL", zerolog.FatalLevel,
},
{
"Error", zerolog.ErrorLevel,
},
{
" warn ", zerolog.WarnLevel,
},
{
"iNFo", zerolog.InfoLevel,
},
{
"debug", zerolog.DebugLevel,
},
{
"trace", zerolog.TraceLevel,
},
}

// Test valid cases
for _, testCase := range testTable {
var level logger.LevelDecoder
err := level.Decode(testCase.value)
require.NoError(t, err)
require.Equal(t, testCase.expected, zerolog.Level(level))
}

// Test error case
var level logger.LevelDecoder
err := level.Decode("notalevel")
require.EqualError(t, err, `unknown log level "notalevel"`)
}

func TestUnmarshaler(t *testing.T) {
type Config struct {
Level logger.LevelDecoder
}

var jsonConf Config
err := json.Unmarshal([]byte(`{"level": "panic"}`), &jsonConf)
require.NoError(t, err, "could not unmarshal level decoder in json file")
require.Equal(t, zerolog.PanicLevel, zerolog.Level(jsonConf.Level))
}

func TestMarshaler(t *testing.T) {
confs := []struct {
Level logger.LevelDecoder `yaml:"level" json:"level"`
}{
{logger.LevelDecoder(zerolog.PanicLevel)},
{logger.LevelDecoder(zerolog.FatalLevel)},
{logger.LevelDecoder(zerolog.ErrorLevel)},
{logger.LevelDecoder(zerolog.WarnLevel)},
{logger.LevelDecoder(zerolog.InfoLevel)},
{logger.LevelDecoder(zerolog.DebugLevel)},
{logger.LevelDecoder(zerolog.TraceLevel)},
}

for _, conf := range confs {
data, err := json.Marshal(conf)
require.NoError(t, err, "could not marshal data into json")
require.Equal(t, []byte(fmt.Sprintf(`{"level":%q}`, &conf.Level)), data)
}

}
40 changes: 40 additions & 0 deletions pkg/logger/logger.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package logger

import "github.com/rs/zerolog"

type severityGCP string

const (
GCPAlertLevel severityGCP = "ALERT"
GCPCriticalLevel severityGCP = "CRITICAL"
GCPErrorLevel severityGCP = "ERROR"
GCPWarningLevel severityGCP = "WARNING"
GCPInfoLevel severityGCP = "INFO"
GCPDebugLevel severityGCP = "DEBUG"

GCPFieldKeySeverity = "severity"
GCPFieldKeyMsg = "message"
GCPFieldKeyTime = "time"
)

var (
zerologToGCPLevel = map[zerolog.Level]severityGCP{
zerolog.PanicLevel: GCPAlertLevel,
zerolog.FatalLevel: GCPCriticalLevel,
zerolog.ErrorLevel: GCPErrorLevel,
zerolog.WarnLevel: GCPWarningLevel,
zerolog.InfoLevel: GCPInfoLevel,
zerolog.DebugLevel: GCPDebugLevel,
zerolog.TraceLevel: GCPDebugLevel,
}
)

// SeverityHook adds GCP severity levels to zerolog output log messages.
type SeverityHook struct{}

// Run implements the zerolog.Hook interface.
func (h SeverityHook) Run(e *zerolog.Event, level zerolog.Level, msg string) {
if level != zerolog.NoLevel {
e.Str(GCPFieldKeySeverity, string(zerologToGCPLevel[level]))
}
}
Loading

0 comments on commit d8b08ad

Please sign in to comment.