Skip to content

Commit

Permalink
Merge pull request #86 from netlify/add-root-args
Browse files Browse the repository at this point in the history
RootArg structure for easier config
  • Loading branch information
smoya authored Aug 20, 2019
2 parents 4dd3ade + f6dc2b6 commit 9bad597
Show file tree
Hide file tree
Showing 5 changed files with 203 additions and 37 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ require (
github.com/spf13/afero v1.2.2 // indirect
github.com/spf13/cobra v0.0.4-0.20190321000552-67fc4837d267
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.3
github.com/spf13/viper v1.3.2
github.com/stretchr/testify v1.3.0
github.com/tinylib/msgp v1.1.0 // indirect
Expand Down
87 changes: 87 additions & 0 deletions nconf/args.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package nconf

import (
"fmt"

"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/pflag"
)

type RootArgs struct {
Prefix string
EnvFile string
}

func (args *RootArgs) Setup(config interface{}, version string) (logrus.FieldLogger, error) {
// first load the logger
logConfig := &struct {
Log *LoggingConfig
}{}
if err := LoadFromEnv(args.Prefix, args.EnvFile, logConfig); err != nil {
return nil, errors.Wrap(err, "Failed to load the logging configuration")
}

log, err := ConfigureLogging(logConfig.Log)
if err != nil {
return nil, errors.Wrap(err, "Failed to create the logger")
}
if version == "" {
version = "unknown"
}
log = log.WithField("version", version)

if config != nil {
// second load the config for this project
if err := LoadFromEnv(args.Prefix, args.EnvFile, config); err != nil {
return log, errors.Wrap(err, "Failed to load the config object")
}
log.Debug("Loaded configuration")
}
return log, nil
}

func (args *RootArgs) MustSetup(config interface{}, version string) logrus.FieldLogger {
logger, err := args.Setup(config, version)
if err != nil {
if logger != nil {
logger.WithError(err).Fatal("Failed to setup configuration")
} else {
panic(fmt.Sprintf("Failed to setup configuratio: %s", err.Error()))
}
}

return logger
}

func (args *RootArgs) ConfigFlag() *pflag.Flag {
return &pflag.Flag{
Name: "config",
Shorthand: "c",
Usage: "A .env file to load configuration from",
Value: newStringValue("", &args.EnvFile),
}
}

func (args *RootArgs) PrefixFlag() *pflag.Flag {
return &pflag.Flag{
Name: "prefix",
Shorthand: "p",
Usage: "A prefix to search for when looking for env vars",
Value: newStringValue("", &args.Prefix),
}
}

type stringValue string

func newStringValue(val string, p *string) *stringValue {
*p = val
return (*stringValue)(p)
}

func (s *stringValue) Set(val string) error {
*s = stringValue(val)
return nil
}
func (s *stringValue) Type() string { return "string" }
func (s *stringValue) String() string { return string(*s) }
69 changes: 69 additions & 0 deletions nconf/args_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package nconf

import (
"io/ioutil"
"testing"

"github.com/spf13/cobra"

"github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"

"github.com/stretchr/testify/require"
)

func TestArgsLoad(t *testing.T) {
cfg := &struct {
Something string
Other int
Overridden string
}{
Something: "default",
Overridden: "this should change",
}

tmp, err := ioutil.TempFile("", "something")
require.NoError(t, err)
cfgStr := `
PF_OTHER=10
PF_OVERRIDDEN=not-that
PF_LOG_LEVEL=debug
PF_LOG_QUOTE_EMPTY_FIELDS=true
`
require.NoError(t, ioutil.WriteFile(tmp.Name(), []byte(cfgStr), 0644))

args := RootArgs{
Prefix: "pf",
EnvFile: tmp.Name(),
}

log, err := args.Setup(cfg, "")
require.NoError(t, err)

// check that we did call configure the logger
assert.NotNil(t, log)
entry := log.(*logrus.Entry)
assert.Equal(t, logrus.DebugLevel, entry.Logger.Level)
assert.True(t, entry.Logger.Formatter.(*logrus.TextFormatter).QuoteEmptyFields)

assert.Equal(t, "default", cfg.Something)
assert.Equal(t, 10, cfg.Other)
assert.Equal(t, "not-that", cfg.Overridden)
}

func TestArgsAddToCmd(t *testing.T) {
args := new(RootArgs)
var called int
cmd := cobra.Command{
Run: func(_ *cobra.Command, _ []string) {
assert.Equal(t, "PF", args.Prefix)
assert.Equal(t, "file.env", args.EnvFile)
called++
},
}
cmd.PersistentFlags().AddFlag(args.ConfigFlag())
cmd.PersistentFlags().AddFlag(args.PrefixFlag())
cmd.SetArgs([]string{"--config", "file.env", "--prefix", "PF"})
require.NoError(t, cmd.Execute())
assert.Equal(t, 1, called)
}
31 changes: 31 additions & 0 deletions nconf/bugsnag.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package nconf

import (
"github.com/bugsnag/bugsnag-go"
logrus_bugsnag "github.com/shopify/logrus-bugsnag"
"github.com/sirupsen/logrus"
)

type BugSnagConfig struct {
Environment string
APIKey string `envconfig:"api_key"`
}

func AddBugSnagHook(config *BugSnagConfig) error {
if config == nil || config.APIKey == "" {
return nil
}

bugsnag.Configure(bugsnag.Configuration{
APIKey: config.APIKey,
ReleaseStage: config.Environment,
PanicHandler: func() {}, // this is to disable panic handling. The lib was forking and restarting the process (causing races)
})
hook, err := logrus_bugsnag.NewBugsnagHook()
if err != nil {
return err
}
logrus.AddHook(hook)
logrus.Debug("Added bugsnag hook")
return nil
}
52 changes: 15 additions & 37 deletions nconf/logging.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,31 @@ import (
"os"
"time"

bugsnag "github.com/bugsnag/bugsnag-go"
"github.com/pkg/errors"
"github.com/shopify/logrus-bugsnag"
"github.com/sirupsen/logrus"
)

type LoggingConfig struct {
Level string `mapstructure:"log_level" json:"log_level"`
File string `mapstructure:"log_file" json:"log_file"`
DisableColors bool `mapstructure:"disable_colors" json:"disable_colors"`
QuoteEmptyFields bool `mapstructure:"quote_empty_fields" json:"quote_empty_fields"`
TSFormat string `mapstructure:"ts_format" json:"ts_format"`
BugSnag *BugSnagConfig
Level string `mapstructure:"log_level" json:"log_level"`
File string `mapstructure:"log_file" json:"log_file"`
DisableColors bool `mapstructure:"disable_colors" split_words:"true" json:"disable_colors"`
QuoteEmptyFields bool `mapstructure:"quote_empty_fields" split_words:"true" json:"quote_empty_fields"`
TSFormat string `mapstructure:"ts_format" json:"ts_format"`
Fields map[string]interface{} `mapstructure:"fields" json:"fields"`
}
UseNewLogger bool `mapstructure:"use_new_logger",split_words:"true"`

type BugSnagConfig struct {
Environment string
APIKey string `envconfig:"api_key"`
BugSnag *BugSnagConfig
}

func ConfigureLogging(config *LoggingConfig) (*logrus.Entry, error) {
logger := logrus.New()

tsFormat := time.RFC3339Nano
if config.TSFormat != "" {
tsFormat = config.TSFormat
}
// always use the full timestamp
logrus.SetFormatter(&logrus.TextFormatter{
logger.SetFormatter(&logrus.TextFormatter{
FullTimestamp: true,
DisableTimestamp: false,
TimestampFormat: tsFormat,
Expand All @@ -45,17 +42,17 @@ func ConfigureLogging(config *LoggingConfig) (*logrus.Entry, error) {
if errOpen != nil {
return nil, errOpen
}
logrus.SetOutput(f)
logrus.Infof("Set output file to %s", config.File)
logger.SetOutput(f)
logger.Infof("Set output file to %s", config.File)
}

if config.Level != "" {
level, err := logrus.ParseLevel(config.Level)
if err != nil {
return nil, err
}
logrus.SetLevel(level)
logrus.Debug("Set log level to: " + logrus.GetLevel().String())
logger.SetLevel(level)
logger.Debug("Set log level to: " + logger.GetLevel().String())
}

if err := AddBugSnagHook(config.BugSnag); err != nil {
Expand All @@ -67,24 +64,5 @@ func ConfigureLogging(config *LoggingConfig) (*logrus.Entry, error) {
f[k] = v
}

return logrus.WithFields(f), nil
}

func AddBugSnagHook(config *BugSnagConfig) error {
if config == nil || config.APIKey == "" {
return nil
}

bugsnag.Configure(bugsnag.Configuration{
APIKey: config.APIKey,
ReleaseStage: config.Environment,
PanicHandler: func() {}, // this is to disable panic handling. The lib was forking and restarting the process (causing races)
})
hook, err := logrus_bugsnag.NewBugsnagHook()
if err != nil {
return err
}
logrus.AddHook(hook)
logrus.Debug("Added bugsnag hook")
return nil
return logger.WithFields(f), nil
}

0 comments on commit 9bad597

Please sign in to comment.