/
logging.go
151 lines (132 loc) · 4.55 KB
/
logging.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
package config
import (
"fmt"
"io"
"os"
"github.com/xeipuuv/gojsonschema"
"gopkg.in/natefinch/lumberjack.v2"
"gopkg.in/sensorbee/sensorbee.v0/data"
)
// Logging has configuration parameters for logging.
type Logging struct {
// Target is a logging target. It can be one of followings:
//
// - stdout
// - stderr
// - file path
Target string `json:"target" yaml:"target"`
// MinLogLevel specifies the minimum level of log entries which should
// actually be written to the target. Possible levels are "debug", "info",
// "warn"/"warning", "error", or "fatal".
MinLogLevel string `json:"min_log_level" yaml:"min_log_level"`
// LogDroppedTuples controls logging of dropped tuples. If this parameter
// is true, dropped tuples are logged as JSON objects in logs. It might
// affect the overall performance of the server.
//
// Setting this option true doesn't enable destinationless tuple logging.
LogDroppedTuples bool `json:"log_dropped_tuples" yaml:"log_dropped_tuples"`
// LogDestinationlessTuples controls logging of dropped tuples.
// A destinationless tuple is a kind of dropped tuples that is generated
// when a source or a stream does not have any destination and a tuple
// emitted from it is dropped.
//
// To log destinationless tuples, both log_dropped_tuples and
// log_destinationless_tuples need to be true.
LogDestinationlessTuples bool `json:"log_destinationless_tuples" yaml:"log_destinationless_tuples"`
// SummarizeDroppedTuples controls summarization of dropped tuples. If this
// parameter is true, only a portion of a dropped tuple is logged. The data
// logged looks like a JSON object but might not be able to be parsed by
// JSON parsers. This parameter only works when LogDroppedTuples is true.
SummarizeDroppedTuples bool `json:"summarize_dropped_tuples" yaml:"summarize_dropped_tuples"`
// TODO: add log rotation
// TODO: add log formatting
}
var (
loggingSchemaString = `{
"type": "object",
"properties": {
"target": {
"type": "string"
},
"min_log_level": {
"enum": ["debug", "info", "warn", "warning", "error", "fatal"]
},
"log_dropped_tuples": {
"type": "boolean"
},
"log_destinationless_tuples": {
"type": "boolean"
},
"summarize_dropped_tuples": {
"type": "boolean"
}
},
"additionalProperties": false
}`
loggingSchema *gojsonschema.Schema
)
func init() {
s, err := gojsonschema.NewSchema(gojsonschema.NewStringLoader(loggingSchemaString))
if err != nil {
panic(err)
}
loggingSchema = s
}
// NewLogging create a Logging config parameters from a given map.
func NewLogging(m data.Map) (*Logging, error) {
if err := validate(loggingSchema, m); err != nil {
return nil, err
}
return newLogging(m), nil
}
func newLogging(m data.Map) *Logging {
return &Logging{
Target: mustAsString(getWithDefault(m, "target", data.String("stderr"))),
MinLogLevel: mustAsString(getWithDefault(m, "min_log_level", data.String("info"))),
LogDroppedTuples: mustToBool(getWithDefault(m, "log_dropped_tuples", data.False)),
LogDestinationlessTuples: mustToBool(getWithDefault(m, "log_destinationless_tuples", data.False)),
SummarizeDroppedTuples: mustToBool(getWithDefault(m, "summarize_dropped_tuples", data.False)),
}
}
type nopCloser struct {
w io.Writer
}
func (n *nopCloser) Write(p []byte) (int, error) {
return n.w.Write(p)
}
func (n *nopCloser) Close() error {
return nil
}
// CreateWriter creates io.Writer for loggers. When Target is a file, the writer
// supports log rotation using lumberjack.
func (l *Logging) CreateWriter() (io.WriteCloser, error) {
// TODO: config package should probably concentrate on parsing and validating
// config files and this should be moved to the server.
switch l.Target {
case "stdout":
return &nopCloser{os.Stdout}, nil
case "stderr":
return &nopCloser{os.Stderr}, nil
default:
// Currently, only file path is supported
f, err := os.OpenFile(l.Target, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644)
if err != nil {
return nil, fmt.Errorf("cannot open the file %v: %v", l.Target, err)
}
f.Close()
return &lumberjack.Logger{
Filename: l.Target,
// TODO: set rotation options
}, nil
}
}
// ToMap returns logging config information as data.Map.
func (l *Logging) ToMap() data.Map {
return data.Map{
"target": data.String(l.Target),
"min_log_level": data.String(l.MinLogLevel),
"log_dropped_tuples": data.Bool(l.LogDroppedTuples),
"log_destinationless_tuples": data.Bool(l.LogDestinationlessTuples),
"summarize_dropped_tuples": data.Bool(l.SummarizeDroppedTuples),
}
}