forked from jirs5/libtrace-go
-
Notifications
You must be signed in to change notification settings - Fork 0
/
client.go
275 lines (241 loc) · 8.13 KB
/
client.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
package libtrace
import (
"github.com/opsramp/libtrace-go/logger"
"sync"
"github.com/opsramp/libtrace-go/transmission"
)
// Client represents an object that can create new builders and events and send
// them somewhere. It maintains its own sending queue for events, distinct from
// both the package-level libhoney queue and any other client. Clients should be
// created with NewClient(config). A manually created Client{} will function as
// a nil output and drop everything handed to it (so can be safely used in dev
// and tests). For more complete testing you can create a Client with a
// MockOutput transmission then inspect the events it would have sent.
type Client struct {
transmission transmission.Sender
logger logger.Logger
builder *Builder
oneTx sync.Once
oneLogger sync.Once
oneBuilder sync.Once
}
// ClientConfig is a subset of the global libhoney config that focuses on the
// configuration of the client itself. The other config options are specific to
// a given transmission Sender and should be specified there if the defaults
// need to be overridden.
type ClientConfig struct {
// APIKey is the opsramp authentication token. If it is specified during
// libhoney initialization, it will be used as the default API key for all
// events. If absent, API key must be explicitly set on a builder or
// event. Find your team's API keys at https://ui.opsramp.io/account
//APIKey string
// Dataset is the name of the opsramp dataset to which to send these events.
// If it is specified during libhoney initialization, it will be used as the
// default dataset for all events. If absent, dataset must be explicitly set
// on a builder or event.
Dataset string
// SampleRate is the rate at which to sample this event. Default is 1,
// meaning no sampling. If you want to send one event out of every 250 times
// Send() is called, you would specify 250 here.
SampleRate uint
// APIHost is the hostname for the Opsramp API server to which to send this
// event. default: https://api.opsramp.io/
APIHost string
// Transmission allows you to override what happens to events after you call
// Send() on them. By default, events are asynchronously sent to the
// Opsramp API. You can use the MockOutput included in this package in
// unit tests, or use the transmission.WriterSender to write events to
// STDOUT or to a file when developing locally.
Transmission transmission.Sender
// Logger defaults to nil and the SDK is silent. If you supply a logger here
// (or set it to &DefaultLogger{}), some debugging output will be emitted.
// Intended for human consumption during development to understand what the
// SDK is doing and diagnose trouble emitting events.
Logger logger.Logger
}
// NewClient creates a Client with defaults correctly set
func NewClient(conf ClientConfig) (*Client, error) {
if conf.SampleRate == 0 {
conf.SampleRate = defaultSampleRate
}
if conf.APIHost == "" {
conf.APIHost = defaultAPIHost
}
if conf.Dataset == "" {
conf.Dataset = defaultDataset
}
c := &Client{
logger: conf.Logger,
}
c.ensureLogger()
if conf.Transmission == nil {
c.transmission = &transmission.TraceProxy{
MaxBatchSize: DefaultMaxBatchSize,
BatchTimeout: DefaultBatchTimeout,
MaxConcurrentBatches: DefaultMaxConcurrentBatches,
PendingWorkCapacity: DefaultPendingWorkCapacity,
UserAgentAddition: UserAgentAddition,
Logger: c.logger,
Metrics: sd,
}
} else {
c.transmission = conf.Transmission
}
if err := c.transmission.Start(); err != nil {
c.logger.Errorf("transmission client failed to start: %s", err.Error())
return nil, err
}
c.builder = &Builder{
//WriteKey: conf.APIKey,
Dataset: conf.Dataset,
SampleRate: conf.SampleRate,
APIHost: conf.APIHost,
dynFields: make([]dynamicField, 0, 0),
fieldHolder: fieldHolder{
data: make(map[string]interface{}),
},
client: c,
}
return c, nil
}
func (c *Client) ensureTransmission() {
c.oneTx.Do(func() {
if c.transmission == nil {
c.transmission = &transmission.DiscardSender{}
c.transmission.Start()
}
})
}
func (c *Client) ensureLogger() {
c.oneLogger.Do(func() {
if c.logger == nil {
c.logger = &logger.NullLogger{}
}
})
}
func (c *Client) ensureBuilder() {
c.oneBuilder.Do(func() {
if c.builder == nil {
c.builder = &Builder{
SampleRate: 1,
dynFields: make([]dynamicField, 0, 0),
fieldHolder: fieldHolder{
data: make(map[string]interface{}),
},
client: c,
}
}
})
}
// Close waits for all in-flight messages to be sent. You should
// call Close() before app termination.
func (c *Client) Close() {
c.ensureLogger()
c.logger.Infof("closing libhoney client")
if c.transmission != nil {
c.transmission.Stop()
}
}
// Flush closes and reopens the Output interface, ensuring events
// are sent without waiting on the batch to be sent asyncronously.
// Generally, it is more efficient to rely on asyncronous batches than to
// call Flush, but certain scenarios may require Flush if asynchronous sends
// are not guaranteed to run (i.e. running in AWS Lambda)
func (c *Client) Flush() {
c.ensureLogger()
c.logger.Infof("flushing libhoney client")
if c.transmission != nil {
if err := c.transmission.Flush(); err != nil {
c.logger.Errorf("unable to flush: %v", err)
}
}
}
// TxResponses returns the channel from which the caller can read the responses
// to sent events.
func (c *Client) TxResponses() chan transmission.Response {
c.ensureTransmission()
return c.transmission.TxResponses()
}
// AddDynamicField takes a field name and a function that will generate values
// for that metric. The function is called once every time a NewEvent() is
// created and added as a field (with name as the key) to the newly created
// event.
//func (c *Client) AddDynamicField(name string, fn func() interface{}) error {
// c.ensureTransmission()
// c.ensureBuilder()
// return c.builder.AddDynamicField(name, fn)
//}
// AddField adds a Field to the Client's scope. This metric will be inherited by
// all builders and events.
func (c *Client) AddField(name string, val interface{}) {
c.ensureTransmission()
c.ensureBuilder()
c.builder.AddField(name, val)
}
func (c *Client) CheckField(key string) bool {
c.ensureTransmission()
c.ensureBuilder()
return c.builder.CheckField(key)
}
func (c *Client) AddResourceField(name string, val interface{}) {
c.ensureTransmission()
c.ensureBuilder()
c.builder.AddResourceField(name, val)
}
func (c *Client) CheckResourceField(key string) bool {
c.ensureTransmission()
c.ensureBuilder()
return c.builder.CheckResourceField(key)
}
func (c *Client) AddSpanField(name string, val interface{}) {
c.ensureTransmission()
c.ensureBuilder()
c.builder.AddSpanField(name, val)
}
func (c *Client) CheckSpanField(key string) bool {
c.ensureTransmission()
c.ensureBuilder()
return c.builder.CheckSpanField(key)
}
func (c *Client) AddEventField(name string, val interface{}) {
c.ensureTransmission()
c.ensureBuilder()
c.builder.AddEventField(name, val)
}
func (c *Client) CheckEventField(key string) bool {
c.ensureTransmission()
c.ensureBuilder()
return c.builder.CheckEventField(key)
}
// Add adds its data to the Client's scope. It adds all fields in a struct or
// all keys in a map as individual Fields. These metrics will be inherited by
// all builders and events.
//func (c *Client) Add(data interface{}) error {
// c.ensureTransmission()
// c.ensureBuilder()
// return c.builder.Add(data)
//}
// NewEvent creates a new event prepopulated with any Fields present in the
// Client's scope.
func (c *Client) NewEvent() *Event {
c.ensureTransmission()
c.ensureBuilder()
return c.builder.NewEvent()
}
// NewBuilder creates a new event builder. The builder inherits any Dynamic or
// Static Fields present in the Client's scope.
func (c *Client) NewBuilder() *Builder {
c.ensureTransmission()
c.ensureBuilder()
return c.builder.Clone()
}
// sendResponse sends a dropped event response down the response channel
//func (c *Client) sendDroppedResponse(e *Event, message string) {
// c.ensureTransmission()
// r := transmission.Response{
// Err: errors.New(message),
// Metadata: e.Metadata,
// }
// c.transmission.SendResponse(r)
//
//}