forked from flashmob/go-guerrilla
-
Notifications
You must be signed in to change notification settings - Fork 0
/
api.go
241 lines (218 loc) · 5.88 KB
/
api.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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
package guerrilla
import (
"encoding/json"
"errors"
"fmt"
"github.com/flashmob/go-guerrilla/backends"
"github.com/flashmob/go-guerrilla/log"
"io/ioutil"
"time"
)
// Daemon provides a convenient API when using go-guerrilla as a package in your Go project.
// Is's facade for Guerrilla, AppConfig, backends.Backend and log.Logger
type Daemon struct {
Config *AppConfig
Logger log.Logger
Backend backends.Backend
// Guerrilla will be managed through the API
g Guerrilla
configLoadTime time.Time
subs []deferredSub
}
type deferredSub struct {
topic Event
fn interface{}
}
// AddProcessor adds a processor constructor to the backend.
// name is the identifier to be used in the config. See backends docs for more info.
func (d *Daemon) AddProcessor(name string, pc backends.ProcessorConstructor) {
backends.Svc.AddProcessor(name, pc)
}
// Starts the daemon, initializing d.Config, d.Logger and d.Backend with defaults
// can only be called once through the lifetime of the program
func (d *Daemon) Start() (err error) {
if d.g == nil {
if d.Config == nil {
d.Config = &AppConfig{}
}
if err = d.configureDefaults(); err != nil {
return err
}
if d.Logger == nil {
d.Logger, err = log.GetLogger(d.Config.LogFile, d.Config.LogLevel)
if err != nil {
return err
}
}
if d.Backend == nil {
d.Backend, err = backends.New(d.Config.BackendConfig, d.Logger)
if err != nil {
return err
}
}
d.g, err = New(d.Config, d.Backend, d.Logger)
if err != nil {
return err
}
for i := range d.subs {
_ = d.Subscribe(d.subs[i].topic, d.subs[i].fn)
}
d.subs = make([]deferredSub, 0)
}
err = d.g.Start()
if err == nil {
if err := d.resetLogger(); err == nil {
d.Log().Infof("main log configured to %s", d.Config.LogFile)
}
}
return err
}
// Shuts down the daemon, including servers and backend.
// Do not call Start on it again, use a new server.
func (d *Daemon) Shutdown() {
if d.g != nil {
d.g.Shutdown()
}
}
// LoadConfig reads in the config from a JSON file.
// Note: if d.Config is nil, the sets d.Config with the unmarshalled AppConfig which will be returned
func (d *Daemon) LoadConfig(path string) (AppConfig, error) {
var ac AppConfig
data, err := ioutil.ReadFile(path)
if err != nil {
return ac, fmt.Errorf("could not read config file: %s", err.Error())
}
err = ac.Load(data)
if err != nil {
return ac, err
}
if d.Config == nil {
d.Config = &ac
}
return ac, nil
}
// SetConfig is same as LoadConfig, except you can pass AppConfig directly
// does not emit any change events, instead use ReloadConfig after daemon has started
func (d *Daemon) SetConfig(c AppConfig) error {
// need to call c.Load, thus need to convert the config
// d.load takes json bytes, marshal it
data, err := json.Marshal(&c)
if err != nil {
return err
}
err = c.Load(data)
if err != nil {
return err
}
d.Config = &c
return nil
}
// Reload a config using the passed in AppConfig and emit config change events
func (d *Daemon) ReloadConfig(c AppConfig) error {
oldConfig := *d.Config
err := d.SetConfig(c)
if err != nil {
d.Log().WithError(err).Error("Error while reloading config")
return err
}
d.Log().Infof("Configuration was reloaded at %s", d.configLoadTime)
d.Config.EmitChangeEvents(&oldConfig, d.g)
return nil
}
// Reload a config from a file and emit config change events
func (d *Daemon) ReloadConfigFile(path string) error {
ac, err := d.LoadConfig(path)
if err != nil {
d.Log().WithError(err).Error("Error while reloading config from file")
return err
} else if d.Config != nil {
oldConfig := *d.Config
d.Config = &ac
d.Log().Infof("Configuration was reloaded at %s", d.configLoadTime)
d.Config.EmitChangeEvents(&oldConfig, d.g)
}
return nil
}
// ReopenLogs send events to re-opens all log files.
// Typically, one would call this after rotating logs
func (d *Daemon) ReopenLogs() error {
if d.Config == nil {
return errors.New("d.Config nil")
}
d.Config.EmitLogReopenEvents(d.g)
return nil
}
// Subscribe for subscribing to config change events
func (d *Daemon) Subscribe(topic Event, fn interface{}) error {
if d.g == nil {
// defer the subscription until the daemon is started
d.subs = append(d.subs, deferredSub{topic, fn})
return nil
}
return d.g.Subscribe(topic, fn)
}
// for publishing config change events
func (d *Daemon) Publish(topic Event, args ...interface{}) {
if d.g == nil {
return
}
d.g.Publish(topic, args...)
}
// for unsubscribing from config change events
func (d *Daemon) Unsubscribe(topic Event, handler interface{}) error {
if d.g == nil {
for i := range d.subs {
if d.subs[i].topic == topic && d.subs[i].fn == handler {
d.subs = append(d.subs[:i], d.subs[i+1:]...)
}
}
return nil
}
return d.g.Unsubscribe(topic, handler)
}
// log returns a logger that implements our log.Logger interface.
// level is set to "info" by default
func (d *Daemon) Log() log.Logger {
if d.Logger != nil {
return d.Logger
}
out := log.OutputStderr.String()
level := log.InfoLevel.String()
if d.Config != nil {
if len(d.Config.LogFile) > 0 {
out = d.Config.LogFile
}
if len(d.Config.LogLevel) > 0 {
level = d.Config.LogLevel
}
}
l, _ := log.GetLogger(out, level)
return l
}
// set the default values for the servers and backend config options
func (d *Daemon) configureDefaults() error {
err := d.Config.setDefaults()
if err != nil {
return err
}
if d.Backend == nil {
err = d.Config.setBackendDefaults()
if err != nil {
return err
}
}
return err
}
// resetLogger sets the logger to the one specified in the config.
// This is because at the start, the daemon may be logging to stderr,
// then attaches to the logs once the config is loaded.
// This will propagate down to the servers / backend too.
func (d *Daemon) resetLogger() error {
l, err := log.GetLogger(d.Config.LogFile, d.Config.LogLevel)
if err != nil {
return err
}
d.Logger = l
d.g.SetLogger(d.Logger)
return nil
}