Skip to content

Commit 369ec04

Browse files
committed
Merge pull request #11 from mattkanwisher/master
Add structured logging to json and support for syslog
2 parents ffe929a + a01ae36 commit 369ec04

File tree

2 files changed

+145
-1
lines changed

2 files changed

+145
-1
lines changed

log/log.go

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ package log
1616
import (
1717
"flag"
1818
"fmt"
19+
"net/url"
20+
"os"
1921
"runtime"
2022
"strings"
2123

@@ -39,10 +41,68 @@ func (f levelFlag) Set(level string) error {
3941
return nil
4042
}
4143

44+
func setSyslogFormatter(appname, facility string) error {
45+
if appname == "" || facility == "" {
46+
return fmt.Errorf("your missing a appname(%s) or local(%s)", appname, facility)
47+
}
48+
49+
fmter, err := newSyslogger(appname, facility, origLogger.Formatter)
50+
if err != nil {
51+
fmt.Fprintf(os.Stderr, "error creating syslog formatter: %v\n", err)
52+
origLogger.Errorf("can't connect logger to syslog: %v", err)
53+
return err
54+
}
55+
origLogger.Formatter = fmter
56+
return nil
57+
}
58+
59+
func setJSONFormatter() {
60+
origLogger.Formatter = &logrus.JSONFormatter{}
61+
}
62+
63+
type logFormatFlag struct{ uri string }
64+
65+
// String implements flag.Value.
66+
func (f logFormatFlag) String() string {
67+
return f.uri
68+
}
69+
70+
// Set implements flag.Value.
71+
func (f logFormatFlag) Set(format string) error {
72+
f.uri = format
73+
u, err := url.Parse(format)
74+
if err != nil {
75+
return err
76+
}
77+
if u.Scheme != "logger" {
78+
return fmt.Errorf("invalid scheme %s", u.Scheme)
79+
}
80+
jsonq := u.Query().Get("json")
81+
if jsonq == "true" {
82+
setJSONFormatter()
83+
}
84+
85+
switch u.Opaque {
86+
case "syslog":
87+
appname := u.Query().Get("appname")
88+
facility := u.Query().Get("local")
89+
return setSyslogFormatter(appname, facility)
90+
case "stdout":
91+
origLogger.Out = os.Stdout
92+
case "stderr":
93+
origLogger.Out = os.Stderr
94+
95+
default:
96+
return fmt.Errorf("unsupported logger %s", u.Opaque)
97+
}
98+
return nil
99+
}
100+
42101
func init() {
43-
// In order for this flag to take effect, the user of the package must call
102+
// In order for these flags to take effect, the user of the package must call
44103
// flag.Parse() before logging anything.
45104
flag.Var(levelFlag{}, "log.level", "Only log messages with the given severity or above. Valid levels: [debug, info, warn, error, fatal].")
105+
flag.Var(logFormatFlag{}, "log.format", "If set use a syslog logger or JSON logging. Example: logger:syslog?appname=bob&local=7 or logger:stdout?json=true. Defaults to stderr.")
46106
}
47107

48108
type Logger interface {

log/syslog_formatter.go

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package log
2+
3+
import (
4+
"fmt"
5+
"log/syslog"
6+
"os"
7+
8+
"github.com/Sirupsen/logrus"
9+
)
10+
11+
var ceeTag = []byte("@cee:")
12+
13+
type syslogger struct {
14+
wrap logrus.Formatter
15+
out *syslog.Writer
16+
}
17+
18+
func newSyslogger(appname string, facility string, fmter logrus.Formatter) (*syslogger, error) {
19+
priority, err := getFacility(facility)
20+
if err != nil {
21+
return nil, err
22+
}
23+
out, err := syslog.New(priority, appname)
24+
return &syslogger{
25+
out: out,
26+
wrap: fmter,
27+
}, err
28+
}
29+
30+
func getFacility(facility string) (syslog.Priority, error) {
31+
switch facility {
32+
case "0":
33+
return syslog.LOG_LOCAL0, nil
34+
case "1":
35+
return syslog.LOG_LOCAL1, nil
36+
case "2":
37+
return syslog.LOG_LOCAL2, nil
38+
case "3":
39+
return syslog.LOG_LOCAL3, nil
40+
case "4":
41+
return syslog.LOG_LOCAL4, nil
42+
case "5":
43+
return syslog.LOG_LOCAL5, nil
44+
case "6":
45+
return syslog.LOG_LOCAL6, nil
46+
case "7":
47+
return syslog.LOG_LOCAL7, nil
48+
}
49+
return syslog.LOG_LOCAL0, fmt.Errorf("invalid local(%s) for syslog", facility)
50+
}
51+
52+
func (s *syslogger) Format(e *logrus.Entry) ([]byte, error) {
53+
data, err := s.wrap.Format(e)
54+
if err != nil {
55+
fmt.Fprintf(os.Stderr, "syslogger: can't format entry: %v\n", err)
56+
return data, err
57+
}
58+
// only append tag to data sent to syslog (line), not to what
59+
// is returned
60+
line := string(append(ceeTag, data...))
61+
62+
switch e.Level {
63+
case logrus.PanicLevel:
64+
err = s.out.Crit(line)
65+
case logrus.FatalLevel:
66+
err = s.out.Crit(line)
67+
case logrus.ErrorLevel:
68+
err = s.out.Err(line)
69+
case logrus.WarnLevel:
70+
err = s.out.Warning(line)
71+
case logrus.InfoLevel:
72+
err = s.out.Info(line)
73+
case logrus.DebugLevel:
74+
err = s.out.Debug(line)
75+
default:
76+
err = s.out.Notice(line)
77+
}
78+
79+
if err != nil {
80+
fmt.Fprintf(os.Stderr, "syslogger: can't send log to syslog: %v\n", err)
81+
}
82+
83+
return data, err
84+
}

0 commit comments

Comments
 (0)