forked from thanapolr/sdhook
-
Notifications
You must be signed in to change notification settings - Fork 1
/
opts.go
313 lines (275 loc) · 8.32 KB
/
opts.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
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
package sdhook
import (
"errors"
"fmt"
"io/ioutil"
"net/http"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
"cloud.google.com/go/compute/metadata"
"github.com/fluent/fluent-logger-golang/fluent"
"github.com/knq/jwt/gserviceaccount"
"github.com/sirupsen/logrus"
errorReporting "google.golang.org/api/clouderrorreporting/v1beta1"
logging "google.golang.org/api/logging/v2"
)
// Option represents an option that modifies the Stackdriver hook settings.
type Option func(*StackdriverHook) error
// Levels is an option that sets the logrus levels that the StackdriverHook
// will create log entries for.
func Levels(levels ...logrus.Level) Option {
return func(sh *StackdriverHook) error {
sh.levels = levels
return nil
}
}
// ProjectID is an option that sets the project ID which is needed for the log
// name.
func ProjectID(projectID string) Option {
return func(sh *StackdriverHook) error {
sh.projectID = projectID
return nil
}
}
// EntriesService is an option that sets the Google API entry service to use
// with Stackdriver.
func EntriesService(service *logging.EntriesService) Option {
return func(sh *StackdriverHook) error {
sh.service = service
return nil
}
}
// LoggingService is an option that sets the Google API logging service to use.
func LoggingService(service *logging.Service) Option {
return func(sh *StackdriverHook) error {
sh.service = service.Entries
return nil
}
}
// ErrorService is an option that sets the Google API error reporting service to use.
func ErrorService(errorService *errorReporting.Service) Option {
return func(sh *StackdriverHook) error {
sh.errorService = errorService
return nil
}
}
// HTTPClient is an option that sets the http.Client to be used when creating
// the Stackdriver service.
func HTTPClient(client *http.Client) Option {
return func(sh *StackdriverHook) error {
// create logging service
l, err := logging.New(client)
if err != nil {
return err
}
// create error reporting service
e, err := errorReporting.New(client)
if err != nil {
return err
}
err = ErrorService(e)(sh)
if err != nil {
return err
}
return LoggingService(l)(sh)
}
}
// MonitoredResource is an option that sets the monitored resource to send with
// each log entry.
func MonitoredResource(resource *logging.MonitoredResource) Option {
return func(sh *StackdriverHook) error {
sh.resource = resource
return nil
}
}
// Resource is an option that sets the resource information to send with each
// log entry.
//
// Please see https://cloud.google.com/logging/docs/api/v2/resource-list for
// the list of labels required per ResType.
func Resource(typ ResType, labels map[string]string) Option {
return func(sh *StackdriverHook) error {
return MonitoredResource(&logging.MonitoredResource{
Type: string(typ),
Labels: labels,
})(sh)
}
}
// LogName is an option that sets the log name to send with each log entry.
//
// Log names are specified as "projects/{projectID}/logs/{logName}"
// if the projectID is set. Otherwise, it's just "{logName}"
func LogName(name string) Option {
return func(sh *StackdriverHook) error {
if sh.projectID == "" {
sh.logName = name
} else {
sh.logName = fmt.Sprintf("projects/%s/logs/%s", sh.projectID, name)
}
return nil
}
}
// ErrorReportingLogName is an option that sets the log name to send
// with each error message for error reporting.
// Only used when ErrorReportingService has been set.
func ErrorReportingLogName(name string) Option {
return func(sh *StackdriverHook) error {
sh.errorReportingLogName = name
return nil
}
}
// Labels is an option that sets the labels to send with each log entry.
func Labels(labels map[string]string) Option {
return func(sh *StackdriverHook) error {
sh.labels = labels
return nil
}
}
// PartialSuccess is an option that toggles whether or not to write partial log
// entries.
func PartialSuccess(enabled bool) Option {
return func(sh *StackdriverHook) error {
sh.partialSuccess = enabled
return nil
}
}
// ErrorReportingService is an option that defines the name of the service
// being tracked for Stackdriver error reporting.
// See:
// https://cloud.google.com/error-reporting/docs/formatting-error-messages
func ErrorReportingService(service string) Option {
return func(sh *StackdriverHook) error {
sh.errorReportingServiceName = service
return nil
}
}
// LimiterBurst is an option that sets x events at once
func LimiterBurst(limiterBurst int) Option {
return func(sh *StackdriverHook) error {
sh.limiterBurst = limiterBurst
return nil
}
}
// LimiterLimit is an option that sets x events per second
func LimiterLimit(limiterLimit int) Option {
return func(sh *StackdriverHook) error {
sh.limiterLimit = limiterLimit
return nil
}
}
// requiredScopes are the oauth2 scopes required for stackdriver logging.
var requiredScopes = []string{
logging.CloudPlatformScope,
}
// GoogleServiceAccountCredentialsJSON is an option that creates the
// Stackdriver logging service using the supplied Google service account
// credentials.
//
// Google Service Account credentials can be downloaded from the Google Cloud
// console: https://console.cloud.google.com/iam-admin/serviceaccounts/
func GoogleServiceAccountCredentialsJSON(buf []byte) Option {
return func(sh *StackdriverHook) error {
var err error
// load credentials
gsa, err := gserviceaccount.FromJSON(buf)
if err != nil {
return err
}
// check project id
if gsa.ProjectID == "" {
return errors.New("google service account credentials missing project_id")
}
// set project id
err = ProjectID(gsa.ProjectID)(sh)
if err != nil {
return err
}
// set resource type
err = Resource(ResTypeProject, map[string]string{
"project_id": gsa.ProjectID,
})(sh)
if err != nil {
return err
}
// create token source
ts, err := gsa.TokenSource(nil, requiredScopes...)
if err != nil {
return err
}
// set client
return HTTPClient(&http.Client{
Transport: &oauth2.Transport{
Source: oauth2.ReuseTokenSource(nil, ts),
},
})(sh)
}
}
// GoogleServiceAccountCredentialsFile is an option that loads Google Service
// Account credentials for use with the StackdriverHook from the specified
// file.
//
// Google Service Account credentials can be downloaded from the Google Cloud
// console: https://console.cloud.google.com/iam-admin/serviceaccounts/
func GoogleServiceAccountCredentialsFile(path string) Option {
return func(sh *StackdriverHook) error {
buf, err := ioutil.ReadFile(path)
if err != nil {
return err
}
return GoogleServiceAccountCredentialsJSON(buf)(sh)
}
}
// GoogleComputeCredentials is an option that loads the Google Service Account
// credentials from the GCE metadata associated with the GCE compute instance.
// If serviceAccount is empty, then the default service account credentials
// associated with the GCE instance will be used.
func GoogleComputeCredentials(serviceAccount string) Option {
return func(sh *StackdriverHook) error {
// get compute metadata scopes associated with the service account
scopes, err := metadata.Scopes(serviceAccount)
if err != nil {
return err
}
// check if all the necessary scopes are provided
for _, s := range requiredScopes {
if !sliceContains(scopes, s) {
// NOTE: if you are seeing this error, you probably need to
// recreate your compute instance with the correct scope
//
// as of August 2016, there is not a way to add a scope to an
// existing compute instance
return fmt.Errorf("missing required scope %s in compute metadata", s)
}
}
return HTTPClient(&http.Client{
Transport: &oauth2.Transport{
Source: google.ComputeTokenSource(serviceAccount),
},
})(sh)
}
}
// sliceContains returns true if haystack contains needle.
func sliceContains(haystack []string, needle string) bool {
for _, s := range haystack {
if s == needle {
return true
}
}
return false
}
func GoogleLoggingAgent() Option {
return func(sh *StackdriverHook) error {
var err error
// set agent client. It expects that the forward input fluentd plugin
// is properly configured by the Google logging agent, which is by default.
// See more at:
// https://cloud.google.com/error-reporting/docs/setup/ec2
sh.agentClient, err = fluent.New(fluent.Config{
Async: true,
})
if err != nil {
return fmt.Errorf("could not find fluentd agent on 127.0.0.1:24224: %v", err)
}
return nil
}
}