1
1
package fluent
2
2
3
3
import (
4
+ "context"
4
5
"encoding/json"
5
6
"errors"
6
7
"fmt"
@@ -78,17 +79,24 @@ func NewErrUnknownNetwork(network string) error {
78
79
}
79
80
80
81
type msgToSend struct {
82
+ ctx context.Context
81
83
data []byte
82
84
ack string
83
85
}
84
86
87
+ type bufferInput struct {
88
+ msg * msgToSend
89
+ result chan <- error
90
+ }
91
+
85
92
type Fluent struct {
86
93
Config
87
94
88
95
dialer dialer
89
96
stopRunning chan bool
90
- pending chan * msgToSend
97
+ pending chan bufferInput
91
98
wg sync.WaitGroup
99
+ resultPool sync.Pool
92
100
93
101
muconn sync.Mutex
94
102
conn net.Conn
@@ -108,6 +116,10 @@ type dialer interface {
108
116
Dial (string , string ) (net.Conn , error )
109
117
}
110
118
119
+ type dialerWithContext interface {
120
+ DialContext (context.Context , string , string ) (net.Conn , error )
121
+ }
122
+
111
123
func newWithDialer (config Config , d dialer ) (f * Fluent , err error ) {
112
124
if config .FluentNetwork == "" {
113
125
config .FluentNetwork = defaultNetwork
@@ -140,22 +152,24 @@ func newWithDialer(config Config, d dialer) (f *Fluent, err error) {
140
152
fmt .Fprintf (os .Stderr , "fluent#New: AsyncConnect is now deprecated, please use Async instead" )
141
153
config .Async = config .Async || config .AsyncConnect
142
154
}
143
-
144
- if config . Async {
145
- f = & Fluent {
146
- Config : config ,
147
- dialer : d ,
148
- pending : make ( chan * msgToSend , config . BufferLimit ),
149
- }
150
- f . wg . Add ( 1 )
151
- go f . run ()
152
- } else {
153
- f = & Fluent {
154
- Config : config ,
155
- dialer : d ,
155
+ f = & Fluent {
156
+ Config : config ,
157
+ dialer : d ,
158
+ pending : make ( chan bufferInput , config . BufferLimit ) ,
159
+ resultPool : sync. Pool {
160
+ New : func () interface {} {
161
+ return make ( chan error , 1 )
162
+ },
163
+ },
164
+ }
165
+ if ! config . Async {
166
+ if err = f . connect ( context . Background ()); err != nil {
167
+ return
156
168
}
157
- err = f .connect ()
158
169
}
170
+
171
+ f .wg .Add (1 )
172
+ go f .run ()
159
173
return
160
174
}
161
175
@@ -185,17 +199,25 @@ func newWithDialer(config Config, d dialer) (f *Fluent, err error) {
185
199
// f.Post("tag_name", structData)
186
200
//
187
201
func (f * Fluent ) Post (tag string , message interface {}) error {
202
+ return f .PostWithContext (context .Background (), tag , message )
203
+ }
204
+
205
+ func (f * Fluent ) PostWithContext (ctx context.Context , tag string , message interface {}) error {
188
206
timeNow := time .Now ()
189
- return f .PostWithTime ( tag , timeNow , message )
207
+ return f .PostWithTimeAndContext ( ctx , tag , timeNow , message )
190
208
}
191
209
192
210
func (f * Fluent ) PostWithTime (tag string , tm time.Time , message interface {}) error {
211
+ return f .PostWithTimeAndContext (context .Background (), tag , tm , message )
212
+ }
213
+
214
+ func (f * Fluent ) PostWithTimeAndContext (ctx context.Context , tag string , tm time.Time , message interface {}) error {
193
215
if len (f .TagPrefix ) > 0 {
194
216
tag = f .TagPrefix + "." + tag
195
217
}
196
218
197
219
if m , ok := message .(msgp.Marshaler ); ok {
198
- return f .EncodeAndPostData ( tag , tm , m )
220
+ return f .EncodeAndPostDataWithContext ( ctx , tag , tm , m )
199
221
}
200
222
201
223
msg := reflect .ValueOf (message )
@@ -215,7 +237,7 @@ func (f *Fluent) PostWithTime(tag string, tm time.Time, message interface{}) err
215
237
}
216
238
kv [name ] = msg .FieldByIndex (field .Index ).Interface ()
217
239
}
218
- return f .EncodeAndPostData ( tag , tm , kv )
240
+ return f .EncodeAndPostDataWithContext ( ctx , tag , tm , kv )
219
241
}
220
242
221
243
if msgtype .Kind () != reflect .Map {
@@ -229,13 +251,17 @@ func (f *Fluent) PostWithTime(tag string, tm time.Time, message interface{}) err
229
251
kv [k .String ()] = msg .MapIndex (k ).Interface ()
230
252
}
231
253
232
- return f .EncodeAndPostData ( tag , tm , kv )
254
+ return f .EncodeAndPostDataWithContext ( ctx , tag , tm , kv )
233
255
}
234
256
235
257
func (f * Fluent ) EncodeAndPostData (tag string , tm time.Time , message interface {}) error {
258
+ return f .EncodeAndPostDataWithContext (context .Background (), tag , tm , message )
259
+ }
260
+
261
+ func (f * Fluent ) EncodeAndPostDataWithContext (ctx context.Context , tag string , tm time.Time , message interface {}) error {
236
262
var msg * msgToSend
237
263
var err error
238
- if msg , err = f .EncodeData ( tag , tm , message ); err != nil {
264
+ if msg , err = f .EncodeDataWithContext ( ctx , tag , tm , message ); err != nil {
239
265
return fmt .Errorf ("fluent#EncodeAndPostData: can't convert '%#v' to msgpack:%v" , message , err )
240
266
}
241
267
return f .postRawData (msg )
@@ -251,7 +277,7 @@ func (f *Fluent) postRawData(msg *msgToSend) error {
251
277
return f .appendBuffer (msg )
252
278
}
253
279
// Synchronous write
254
- return f .write (msg )
280
+ return f .appendBufferBlocking (msg )
255
281
}
256
282
257
283
// For sending forward protocol adopted JSON
@@ -296,8 +322,12 @@ func getUniqueID(timeUnix int64) (string, error) {
296
322
}
297
323
298
324
func (f * Fluent ) EncodeData (tag string , tm time.Time , message interface {}) (msg * msgToSend , err error ) {
325
+ return f .EncodeDataWithContext (context .Background (), tag , tm , message )
326
+ }
327
+
328
+ func (f * Fluent ) EncodeDataWithContext (ctx context.Context , tag string , tm time.Time , message interface {}) (msg * msgToSend , err error ) {
299
329
option := make (map [string ]string )
300
- msg = & msgToSend {}
330
+ msg = & msgToSend {ctx : ctx }
301
331
timeUnix := tm .Unix ()
302
332
if f .Config .RequestAck {
303
333
var err error
@@ -338,13 +368,37 @@ func (f *Fluent) Close() (err error) {
338
368
// appendBuffer appends data to buffer with lock.
339
369
func (f * Fluent ) appendBuffer (msg * msgToSend ) error {
340
370
select {
341
- case f .pending <- msg :
371
+ case f .pending <- bufferInput { msg : msg } :
342
372
default :
343
373
return fmt .Errorf ("fluent#appendBuffer: Buffer full, limit %v" , f .Config .BufferLimit )
344
374
}
345
375
return nil
346
376
}
347
377
378
+ // appendBufferWithFeedback appends data to buffer and waits for the result
379
+ func (f * Fluent ) appendBufferBlocking (msg * msgToSend ) error {
380
+ result := f .resultPool .Get ().(chan error )
381
+ // write the data to the buffer and block if the buffer is full
382
+ select {
383
+ case f .pending <- bufferInput {msg : msg , result : result }:
384
+ // don't do anything
385
+ case <- msg .ctx .Done ():
386
+ // because the result channel is not used, it can safely be returned to the sync pool.
387
+ f .resultPool .Put (result )
388
+ return msg .ctx .Err ()
389
+ }
390
+
391
+ select {
392
+ case err := <- result :
393
+ f .resultPool .Put (result )
394
+ return err
395
+ case <- msg .ctx .Done ():
396
+ // the context deadline has exceeded, but there is no result yet. So the result channel cannot be returned to
397
+ // the pool, as it might be written later.
398
+ return msg .ctx .Err ()
399
+ }
400
+ }
401
+
348
402
// close closes the connection.
349
403
func (f * Fluent ) close (c net.Conn ) {
350
404
f .muconn .Lock ()
@@ -356,19 +410,23 @@ func (f *Fluent) close(c net.Conn) {
356
410
}
357
411
358
412
// connect establishes a new connection using the specified transport.
359
- func (f * Fluent ) connect () (err error ) {
413
+ func (f * Fluent ) connect (ctx context.Context ) (err error ) {
414
+ var address string
360
415
switch f .Config .FluentNetwork {
361
416
case "tcp" :
362
- f .conn , err = f .dialer .Dial (
363
- f .Config .FluentNetwork ,
364
- f .Config .FluentHost + ":" + strconv .Itoa (f .Config .FluentPort ))
417
+ address = f .Config .FluentHost + ":" + strconv .Itoa (f .Config .FluentPort )
365
418
case "unix" :
366
- f .conn , err = f .dialer .Dial (
367
- f .Config .FluentNetwork ,
368
- f .Config .FluentSocketPath )
419
+ address = f .Config .FluentSocketPath
369
420
default :
370
421
err = NewErrUnknownNetwork (f .Config .FluentNetwork )
422
+ return
371
423
}
424
+ if d , ok := f .dialer .(dialerWithContext ); ok {
425
+ f .conn , err = d .DialContext (ctx , f .Config .FluentNetwork , address )
426
+ } else {
427
+ f .conn , err = f .dialer .Dial (f .Config .FluentNetwork , address )
428
+ }
429
+
372
430
return err
373
431
}
374
432
@@ -386,7 +444,11 @@ func (f *Fluent) run() {
386
444
emitEventDrainMsg .Do (func () { fmt .Fprintf (os .Stderr , "[%s] Discarding queued events...\n " , time .Now ().Format (time .RFC3339 )) })
387
445
continue
388
446
}
389
- err := f .write (entry )
447
+ err := f .write (entry .msg )
448
+ if entry .result != nil {
449
+ entry .result <- err
450
+ continue
451
+ }
390
452
if err != nil {
391
453
fmt .Fprintf (os .Stderr , "[%s] Unable to send logs to fluentd, reconnecting...\n " , time .Now ().Format (time .RFC3339 ))
392
454
}
@@ -413,7 +475,7 @@ func (f *Fluent) write(msg *msgToSend) error {
413
475
if c == nil {
414
476
f .muconn .Lock ()
415
477
if f .conn == nil {
416
- err := f .connect ()
478
+ err := f .connect (msg . ctx )
417
479
if err != nil {
418
480
f .muconn .Unlock ()
419
481
@@ -425,7 +487,13 @@ func (f *Fluent) write(msg *msgToSend) error {
425
487
if waitTime > f .Config .MaxRetryWait {
426
488
waitTime = f .Config .MaxRetryWait
427
489
}
428
- time .Sleep (time .Duration (waitTime ) * time .Millisecond )
490
+ waitDuration := time .Duration (waitTime ) * time .Millisecond
491
+ if deadline , hasDeadLine := msg .ctx .Deadline (); hasDeadLine && deadline .Before (time .Now ().Add (waitDuration )) {
492
+ // the context deadline is within the wait time, so after the sleep the deadline will have been
493
+ // exceeded. It is a waste of time to wait on that.
494
+ return context .DeadlineExceeded
495
+ }
496
+ time .Sleep (waitDuration )
429
497
continue
430
498
}
431
499
}
@@ -435,11 +503,14 @@ func (f *Fluent) write(msg *msgToSend) error {
435
503
436
504
// We're connected, write msg
437
505
t := f .Config .WriteTimeout
506
+ var deadline time.Time
438
507
if time .Duration (0 ) < t {
439
- c .SetWriteDeadline (time .Now ().Add (t ))
440
- } else {
441
- c .SetWriteDeadline (time.Time {})
508
+ deadline = time .Now ().Add (t )
509
+ }
510
+ if ctxDeadline , hasDeadline := msg .ctx .Deadline (); hasDeadline && (deadline .IsZero () || ctxDeadline .Before (deadline )) {
511
+ deadline = ctxDeadline
442
512
}
513
+ c .SetWriteDeadline (deadline )
443
514
_ , err := c .Write (msg .data )
444
515
if err != nil {
445
516
f .close (c )
0 commit comments