Skip to content

Commit 9bad597

Browse files
authored
Merge pull request #86 from netlify/add-root-args
RootArg structure for easier config
2 parents 4dd3ade + f6dc2b6 commit 9bad597

File tree

5 files changed

+203
-37
lines changed

5 files changed

+203
-37
lines changed

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ require (
3131
github.com/spf13/afero v1.2.2 // indirect
3232
github.com/spf13/cobra v0.0.4-0.20190321000552-67fc4837d267
3333
github.com/spf13/jwalterweatherman v1.1.0 // indirect
34+
github.com/spf13/pflag v1.0.3
3435
github.com/spf13/viper v1.3.2
3536
github.com/stretchr/testify v1.3.0
3637
github.com/tinylib/msgp v1.1.0 // indirect

nconf/args.go

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
package nconf
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/pkg/errors"
7+
"github.com/sirupsen/logrus"
8+
"github.com/spf13/pflag"
9+
)
10+
11+
type RootArgs struct {
12+
Prefix string
13+
EnvFile string
14+
}
15+
16+
func (args *RootArgs) Setup(config interface{}, version string) (logrus.FieldLogger, error) {
17+
// first load the logger
18+
logConfig := &struct {
19+
Log *LoggingConfig
20+
}{}
21+
if err := LoadFromEnv(args.Prefix, args.EnvFile, logConfig); err != nil {
22+
return nil, errors.Wrap(err, "Failed to load the logging configuration")
23+
}
24+
25+
log, err := ConfigureLogging(logConfig.Log)
26+
if err != nil {
27+
return nil, errors.Wrap(err, "Failed to create the logger")
28+
}
29+
if version == "" {
30+
version = "unknown"
31+
}
32+
log = log.WithField("version", version)
33+
34+
if config != nil {
35+
// second load the config for this project
36+
if err := LoadFromEnv(args.Prefix, args.EnvFile, config); err != nil {
37+
return log, errors.Wrap(err, "Failed to load the config object")
38+
}
39+
log.Debug("Loaded configuration")
40+
}
41+
return log, nil
42+
}
43+
44+
func (args *RootArgs) MustSetup(config interface{}, version string) logrus.FieldLogger {
45+
logger, err := args.Setup(config, version)
46+
if err != nil {
47+
if logger != nil {
48+
logger.WithError(err).Fatal("Failed to setup configuration")
49+
} else {
50+
panic(fmt.Sprintf("Failed to setup configuratio: %s", err.Error()))
51+
}
52+
}
53+
54+
return logger
55+
}
56+
57+
func (args *RootArgs) ConfigFlag() *pflag.Flag {
58+
return &pflag.Flag{
59+
Name: "config",
60+
Shorthand: "c",
61+
Usage: "A .env file to load configuration from",
62+
Value: newStringValue("", &args.EnvFile),
63+
}
64+
}
65+
66+
func (args *RootArgs) PrefixFlag() *pflag.Flag {
67+
return &pflag.Flag{
68+
Name: "prefix",
69+
Shorthand: "p",
70+
Usage: "A prefix to search for when looking for env vars",
71+
Value: newStringValue("", &args.Prefix),
72+
}
73+
}
74+
75+
type stringValue string
76+
77+
func newStringValue(val string, p *string) *stringValue {
78+
*p = val
79+
return (*stringValue)(p)
80+
}
81+
82+
func (s *stringValue) Set(val string) error {
83+
*s = stringValue(val)
84+
return nil
85+
}
86+
func (s *stringValue) Type() string { return "string" }
87+
func (s *stringValue) String() string { return string(*s) }

nconf/args_test.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package nconf
2+
3+
import (
4+
"io/ioutil"
5+
"testing"
6+
7+
"github.com/spf13/cobra"
8+
9+
"github.com/sirupsen/logrus"
10+
"github.com/stretchr/testify/assert"
11+
12+
"github.com/stretchr/testify/require"
13+
)
14+
15+
func TestArgsLoad(t *testing.T) {
16+
cfg := &struct {
17+
Something string
18+
Other int
19+
Overridden string
20+
}{
21+
Something: "default",
22+
Overridden: "this should change",
23+
}
24+
25+
tmp, err := ioutil.TempFile("", "something")
26+
require.NoError(t, err)
27+
cfgStr := `
28+
PF_OTHER=10
29+
PF_OVERRIDDEN=not-that
30+
PF_LOG_LEVEL=debug
31+
PF_LOG_QUOTE_EMPTY_FIELDS=true
32+
`
33+
require.NoError(t, ioutil.WriteFile(tmp.Name(), []byte(cfgStr), 0644))
34+
35+
args := RootArgs{
36+
Prefix: "pf",
37+
EnvFile: tmp.Name(),
38+
}
39+
40+
log, err := args.Setup(cfg, "")
41+
require.NoError(t, err)
42+
43+
// check that we did call configure the logger
44+
assert.NotNil(t, log)
45+
entry := log.(*logrus.Entry)
46+
assert.Equal(t, logrus.DebugLevel, entry.Logger.Level)
47+
assert.True(t, entry.Logger.Formatter.(*logrus.TextFormatter).QuoteEmptyFields)
48+
49+
assert.Equal(t, "default", cfg.Something)
50+
assert.Equal(t, 10, cfg.Other)
51+
assert.Equal(t, "not-that", cfg.Overridden)
52+
}
53+
54+
func TestArgsAddToCmd(t *testing.T) {
55+
args := new(RootArgs)
56+
var called int
57+
cmd := cobra.Command{
58+
Run: func(_ *cobra.Command, _ []string) {
59+
assert.Equal(t, "PF", args.Prefix)
60+
assert.Equal(t, "file.env", args.EnvFile)
61+
called++
62+
},
63+
}
64+
cmd.PersistentFlags().AddFlag(args.ConfigFlag())
65+
cmd.PersistentFlags().AddFlag(args.PrefixFlag())
66+
cmd.SetArgs([]string{"--config", "file.env", "--prefix", "PF"})
67+
require.NoError(t, cmd.Execute())
68+
assert.Equal(t, 1, called)
69+
}

nconf/bugsnag.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package nconf
2+
3+
import (
4+
"github.com/bugsnag/bugsnag-go"
5+
logrus_bugsnag "github.com/shopify/logrus-bugsnag"
6+
"github.com/sirupsen/logrus"
7+
)
8+
9+
type BugSnagConfig struct {
10+
Environment string
11+
APIKey string `envconfig:"api_key"`
12+
}
13+
14+
func AddBugSnagHook(config *BugSnagConfig) error {
15+
if config == nil || config.APIKey == "" {
16+
return nil
17+
}
18+
19+
bugsnag.Configure(bugsnag.Configuration{
20+
APIKey: config.APIKey,
21+
ReleaseStage: config.Environment,
22+
PanicHandler: func() {}, // this is to disable panic handling. The lib was forking and restarting the process (causing races)
23+
})
24+
hook, err := logrus_bugsnag.NewBugsnagHook()
25+
if err != nil {
26+
return err
27+
}
28+
logrus.AddHook(hook)
29+
logrus.Debug("Added bugsnag hook")
30+
return nil
31+
}

nconf/logging.go

Lines changed: 15 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -4,34 +4,31 @@ import (
44
"os"
55
"time"
66

7-
bugsnag "github.com/bugsnag/bugsnag-go"
87
"github.com/pkg/errors"
9-
"github.com/shopify/logrus-bugsnag"
108
"github.com/sirupsen/logrus"
119
)
1210

1311
type LoggingConfig struct {
14-
Level string `mapstructure:"log_level" json:"log_level"`
15-
File string `mapstructure:"log_file" json:"log_file"`
16-
DisableColors bool `mapstructure:"disable_colors" json:"disable_colors"`
17-
QuoteEmptyFields bool `mapstructure:"quote_empty_fields" json:"quote_empty_fields"`
18-
TSFormat string `mapstructure:"ts_format" json:"ts_format"`
19-
BugSnag *BugSnagConfig
12+
Level string `mapstructure:"log_level" json:"log_level"`
13+
File string `mapstructure:"log_file" json:"log_file"`
14+
DisableColors bool `mapstructure:"disable_colors" split_words:"true" json:"disable_colors"`
15+
QuoteEmptyFields bool `mapstructure:"quote_empty_fields" split_words:"true" json:"quote_empty_fields"`
16+
TSFormat string `mapstructure:"ts_format" json:"ts_format"`
2017
Fields map[string]interface{} `mapstructure:"fields" json:"fields"`
21-
}
18+
UseNewLogger bool `mapstructure:"use_new_logger",split_words:"true"`
2219

23-
type BugSnagConfig struct {
24-
Environment string
25-
APIKey string `envconfig:"api_key"`
20+
BugSnag *BugSnagConfig
2621
}
2722

2823
func ConfigureLogging(config *LoggingConfig) (*logrus.Entry, error) {
24+
logger := logrus.New()
25+
2926
tsFormat := time.RFC3339Nano
3027
if config.TSFormat != "" {
3128
tsFormat = config.TSFormat
3229
}
3330
// always use the full timestamp
34-
logrus.SetFormatter(&logrus.TextFormatter{
31+
logger.SetFormatter(&logrus.TextFormatter{
3532
FullTimestamp: true,
3633
DisableTimestamp: false,
3734
TimestampFormat: tsFormat,
@@ -45,17 +42,17 @@ func ConfigureLogging(config *LoggingConfig) (*logrus.Entry, error) {
4542
if errOpen != nil {
4643
return nil, errOpen
4744
}
48-
logrus.SetOutput(f)
49-
logrus.Infof("Set output file to %s", config.File)
45+
logger.SetOutput(f)
46+
logger.Infof("Set output file to %s", config.File)
5047
}
5148

5249
if config.Level != "" {
5350
level, err := logrus.ParseLevel(config.Level)
5451
if err != nil {
5552
return nil, err
5653
}
57-
logrus.SetLevel(level)
58-
logrus.Debug("Set log level to: " + logrus.GetLevel().String())
54+
logger.SetLevel(level)
55+
logger.Debug("Set log level to: " + logger.GetLevel().String())
5956
}
6057

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

70-
return logrus.WithFields(f), nil
71-
}
72-
73-
func AddBugSnagHook(config *BugSnagConfig) error {
74-
if config == nil || config.APIKey == "" {
75-
return nil
76-
}
77-
78-
bugsnag.Configure(bugsnag.Configuration{
79-
APIKey: config.APIKey,
80-
ReleaseStage: config.Environment,
81-
PanicHandler: func() {}, // this is to disable panic handling. The lib was forking and restarting the process (causing races)
82-
})
83-
hook, err := logrus_bugsnag.NewBugsnagHook()
84-
if err != nil {
85-
return err
86-
}
87-
logrus.AddHook(hook)
88-
logrus.Debug("Added bugsnag hook")
89-
return nil
67+
return logger.WithFields(f), nil
9068
}

0 commit comments

Comments
 (0)