1package lagerflags
2
3import (
4	"errors"
5	"flag"
6	"fmt"
7	"os"
8
9	"code.cloudfoundry.org/lager"
10)
11
12const (
13	DEBUG = "debug"
14	INFO  = "info"
15	ERROR = "error"
16	FATAL = "fatal"
17)
18
19type TimeFormat int
20
21const (
22	FormatUnixEpoch TimeFormat = iota
23	FormatRFC3339
24)
25
26func (t TimeFormat) MarshalJSON() ([]byte, error) {
27	if FormatUnixEpoch <= t && t <= FormatRFC3339 {
28		return []byte(`"` + t.String() + `"`), nil
29	}
30	return nil, fmt.Errorf("invalid TimeFormat: %d", t)
31}
32
33// Set implements the flag.Getter interface
34func (t TimeFormat) Get(s string) interface{} { return t }
35
36// Set implements the flag.Value interface
37func (t *TimeFormat) Set(s string) error {
38	switch s {
39	case "unix-epoch", "0":
40		*t = FormatUnixEpoch
41	case "rfc3339", "1":
42		*t = FormatRFC3339
43	default:
44		return errors.New(`invalid TimeFormat: "` + s + `"`)
45	}
46	return nil
47}
48
49func (t *TimeFormat) UnmarshalJSON(data []byte) error {
50	if string(data) == "null" {
51		return nil
52	}
53	// unqote
54	if len(data) >= 2 && data[0] == '"' && data[len(data)-1] == '"' {
55		data = data[1 : len(data)-1]
56	}
57	return t.Set(string(data))
58}
59
60func (t TimeFormat) String() string {
61	switch t {
62	case FormatUnixEpoch:
63		return "unix-epoch"
64	case FormatRFC3339:
65		return "rfc3339"
66	}
67	return "invalid"
68}
69
70type LagerConfig struct {
71	LogLevel      string     `json:"log_level,omitempty"`
72	RedactSecrets bool       `json:"redact_secrets,omitempty"`
73	TimeFormat    TimeFormat `json:"time_format"`
74}
75
76func DefaultLagerConfig() LagerConfig {
77	return LagerConfig{
78		LogLevel:      string(INFO),
79		RedactSecrets: false,
80		TimeFormat:    FormatUnixEpoch,
81	}
82}
83
84var minLogLevel string
85var redactSecrets bool
86var timeFormat TimeFormat
87
88func AddFlags(flagSet *flag.FlagSet) {
89	flagSet.StringVar(
90		&minLogLevel,
91		"logLevel",
92		string(INFO),
93		"log level: debug, info, error or fatal",
94	)
95	flagSet.BoolVar(
96		&redactSecrets,
97		"redactSecrets",
98		false,
99		"use a redacting log sink to scrub sensitive values from data being logged",
100	)
101	flagSet.Var(
102		&timeFormat,
103		"timeFormat",
104		`Format for timestamp in component logs. Valid values are "unix-epoch" and "rfc3339".`,
105	)
106}
107
108func ConfigFromFlags() LagerConfig {
109	return LagerConfig{
110		LogLevel:      minLogLevel,
111		RedactSecrets: redactSecrets,
112		TimeFormat:    timeFormat,
113	}
114}
115
116func New(component string) (lager.Logger, *lager.ReconfigurableSink) {
117	return newLogger(component, minLogLevel, lager.NewWriterSink(os.Stdout, lager.DEBUG))
118}
119
120func NewFromSink(component string, sink lager.Sink) (lager.Logger, *lager.ReconfigurableSink) {
121	return newLogger(component, minLogLevel, sink)
122}
123
124func NewFromConfig(component string, config LagerConfig) (lager.Logger, *lager.ReconfigurableSink) {
125	var sink lager.Sink
126
127	if config.TimeFormat == FormatRFC3339 {
128		sink = lager.NewPrettySink(os.Stdout, lager.DEBUG)
129	} else {
130		sink = lager.NewWriterSink(os.Stdout, lager.DEBUG)
131	}
132
133	if config.RedactSecrets {
134		var err error
135		sink, err = lager.NewRedactingSink(sink, nil, nil)
136		if err != nil {
137			panic(err)
138		}
139
140	}
141
142	return newLogger(component, config.LogLevel, sink)
143}
144
145func newLogger(component, minLogLevel string, inSink lager.Sink) (lager.Logger, *lager.ReconfigurableSink) {
146	var minLagerLogLevel lager.LogLevel
147
148	switch minLogLevel {
149	case DEBUG:
150		minLagerLogLevel = lager.DEBUG
151	case INFO:
152		minLagerLogLevel = lager.INFO
153	case ERROR:
154		minLagerLogLevel = lager.ERROR
155	case FATAL:
156		minLagerLogLevel = lager.FATAL
157	default:
158		panic(fmt.Errorf("unknown log level: %s", minLogLevel))
159	}
160
161	logger := lager.NewLogger(component)
162
163	sink := lager.NewReconfigurableSink(inSink, minLagerLogLevel)
164	logger.RegisterSink(sink)
165
166	return logger, sink
167}
168