66 "fmt"
77 "math"
88 "net"
9+ "os"
910 "reflect"
1011 "strconv"
1112 "sync"
@@ -21,8 +22,9 @@ const (
2122 defaultPort = 24224
2223 defaultTimeout = 3 * time .Second
2324 defaultWriteTimeout = time .Duration (0 ) // Write() will not time out
24- defaultBufferLimit = 8 * 1024 * 1024
25+ defaultBufferLimit = 8 * 1024
2526 defaultRetryWait = 500
27+ defaultMaxRetryWait = 60000
2628 defaultMaxRetry = 13
2729 defaultReconnectWaitIncreRate = 1.5
2830 // Default sub-second precision value to false since it is only compatible
@@ -40,9 +42,12 @@ type Config struct {
4042 BufferLimit int `json:"buffer_limit"`
4143 RetryWait int `json:"retry_wait"`
4244 MaxRetry int `json:"max_retry"`
45+ MaxRetryWait int `json:"max_retry_wait"`
4346 TagPrefix string `json:"tag_prefix"`
44- AsyncConnect bool `json:"async_connect"`
45- MarshalAsJSON bool `json:"marshal_as_json"`
47+ Async bool `json:"async"`
48+ // Deprecated: Use Async instead
49+ AsyncConnect bool `json:"async_connect"`
50+ MarshalAsJSON bool `json:"marshal_as_json"`
4651
4752 // Sub-second precision timestamps are only possible for those using fluentd
4853 // v0.14+ and serializing their messages with msgpack.
@@ -52,12 +57,11 @@ type Config struct {
5257type Fluent struct {
5358 Config
5459
55- mubuff sync. Mutex
56- pending [] byte
60+ pending chan [] byte
61+ wg sync. WaitGroup
5762
58- muconn sync.Mutex
59- conn net.Conn
60- reconnecting bool
63+ muconn sync.Mutex
64+ conn net.Conn
6165}
6266
6367// New creates a new Logger.
@@ -89,11 +93,22 @@ func New(config Config) (f *Fluent, err error) {
8993 if config .MaxRetry == 0 {
9094 config .MaxRetry = defaultMaxRetry
9195 }
96+ if config .MaxRetryWait == 0 {
97+ config .MaxRetryWait = defaultMaxRetryWait
98+ }
9299 if config .AsyncConnect {
93- f = & Fluent {Config : config , reconnecting : true }
94- go f .reconnect ()
100+ fmt .Fprintf (os .Stderr , "fluent#New: AsyncConnect is now deprecated, please use Async instead" )
101+ config .Async = config .Async || config .AsyncConnect
102+ }
103+ if config .Async {
104+ f = & Fluent {
105+ Config : config ,
106+ pending : make (chan []byte , config .BufferLimit ),
107+ }
108+ f .wg .Add (1 )
109+ go f .run ()
95110 } else {
96- f = & Fluent {Config : config , reconnecting : false }
111+ f = & Fluent {Config : config }
97112 err = f .connect ()
98113 }
99114 return
@@ -187,14 +202,11 @@ func (f *Fluent) PostRawData(data []byte) {
187202}
188203
189204func (f * Fluent ) postRawData (data []byte ) error {
190- if err := f .appendBuffer (data ); err != nil {
191- return err
192- }
193- if err := f .send (); err != nil {
194- f .close ()
195- return err
205+ if f .Config .Async {
206+ return f .appendBuffer (data )
196207 }
197- return nil
208+ // Synchronous write
209+ return f .write (data )
198210}
199211
200212// For sending forward protocol adopted JSON
@@ -227,23 +239,23 @@ func (f *Fluent) EncodeData(tag string, tm time.Time, message interface{}) (data
227239 return
228240}
229241
230- // Close closes the connection.
242+ // Close closes the connection, waiting for pending logs to be sent
231243func (f * Fluent ) Close () (err error ) {
232- if len (f .pending ) > 0 {
233- err = f .send ()
244+ if f .Config .Async {
245+ close (f .pending )
246+ f .wg .Wait ()
234247 }
235248 f .close ()
236249 return
237250}
238251
239252// appendBuffer appends data to buffer with lock.
240253func (f * Fluent ) appendBuffer (data []byte ) error {
241- f . mubuff . Lock ()
242- defer f .mubuff . Unlock ()
243- if len ( f . pending ) + len ( data ) > f . Config . BufferLimit {
244- return errors . New ( fmt .Sprintf ("fluent#appendBuffer: Buffer full, limit %v" , f .Config .BufferLimit ) )
254+ select {
255+ case f .pending <- data :
256+ default :
257+ return fmt .Errorf ("fluent#appendBuffer: Buffer full, limit %v" , f .Config .BufferLimit )
245258 }
246- f .pending = append (f .pending , data ... )
247259 return nil
248260}
249261
@@ -259,8 +271,6 @@ func (f *Fluent) close() {
259271
260272// connect establishes a new connection using the specified transport.
261273func (f * Fluent ) connect () (err error ) {
262- f .muconn .Lock ()
263- defer f .muconn .Unlock ()
264274
265275 switch f .Config .FluentNetwork {
266276 case "tcp" :
@@ -270,63 +280,63 @@ func (f *Fluent) connect() (err error) {
270280 default :
271281 err = net .UnknownNetworkError (f .Config .FluentNetwork )
272282 }
283+ return err
284+ }
273285
274- if err == nil {
275- f .reconnecting = false
286+ func (f * Fluent ) run () {
287+ for {
288+ select {
289+ case entry , ok := <- f .pending :
290+ if ! ok {
291+ f .wg .Done ()
292+ return
293+ }
294+ err := f .write (entry )
295+ if err != nil {
296+ fmt .Fprintf (os .Stderr , "[%s] Unable to send logs to fluentd, reconnecting...\n " , time .Now ().Format (time .RFC3339 ))
297+ }
298+ }
276299 }
277- return
278300}
279301
280302func e (x , y float64 ) int {
281303 return int (math .Pow (x , y ))
282304}
283305
284- func (f * Fluent ) reconnect () {
285- for i := 0 ; ; i ++ {
286- err := f .connect ()
287- if err == nil {
288- f .send ()
289- return
290- }
291- if i == f .Config .MaxRetry {
292- // TODO: What we can do when connection failed MaxRetry times?
293- panic ("fluent#reconnect: failed to reconnect!" )
294- }
295- waitTime := f .Config .RetryWait * e (defaultReconnectWaitIncreRate , float64 (i - 1 ))
296- time .Sleep (time .Duration (waitTime ) * time .Millisecond )
297- }
298- }
299-
300- func (f * Fluent ) send () error {
301- f .muconn .Lock ()
302- defer f .muconn .Unlock ()
303-
304- if f .conn == nil {
305- if f .reconnecting == false {
306- f .reconnecting = true
307- go f .reconnect ()
306+ func (f * Fluent ) write (data []byte ) error {
307+
308+ for i := 0 ; i < f .Config .MaxRetry ; i ++ {
309+
310+ // Connect if needed
311+ f .muconn .Lock ()
312+ if f .conn == nil {
313+ err := f .connect ()
314+ if err != nil {
315+ f .muconn .Unlock ()
316+ waitTime := f .Config .RetryWait * e (defaultReconnectWaitIncreRate , float64 (i - 1 ))
317+ if waitTime > f .Config .MaxRetryWait {
318+ waitTime = f .Config .MaxRetryWait
319+ }
320+ time .Sleep (time .Duration (waitTime ) * time .Millisecond )
321+ continue
322+ }
308323 }
309- return errors .New ("fluent#send: can't send logs, client is reconnecting" )
310- }
311-
312- f .mubuff .Lock ()
313- defer f .mubuff .Unlock ()
324+ f .muconn .Unlock ()
314325
315- var err error
316- if len (f .pending ) > 0 {
326+ // We're connected, write data
317327 t := f .Config .WriteTimeout
318328 if time .Duration (0 ) < t {
319329 f .conn .SetWriteDeadline (time .Now ().Add (t ))
320330 } else {
321331 f .conn .SetWriteDeadline (time.Time {})
322332 }
323- _ , err = f .conn .Write (f . pending )
333+ _ , err : = f .conn .Write (data )
324334 if err != nil {
325- f .conn .Close ()
326- f .conn = nil
335+ f .close ()
327336 } else {
328- f . pending = f . pending [: 0 ]
337+ return err
329338 }
330339 }
331- return err
340+
341+ return fmt .Errorf ("fluent#write: failed to reconnect, max retry: %v" , f .Config .MaxRetry )
332342}
0 commit comments