Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add advanced logging #11

Merged
merged 1 commit into from
Jan 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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