@@ -6,9 +6,11 @@ import (
66 "errors"
77 "fmt"
88 "io"
9+ "log"
910 "net/http"
1011 "net/url"
1112 "regexp"
13+ "runtime/debug"
1214 "strings"
1315 "sync"
1416 "sync/atomic"
@@ -193,7 +195,7 @@ func (logger *WebsocketLiveloggerWithFallback) SendLog(wrapper *protocol.Timelin
193195 if currentLogger == nil {
194196 currentLogger = logger .initialize ()
195197 if currentLogger == nil {
196- return errors .New ("SendLog failure" )
198+ return errors .New ("initialize failure" )
197199 }
198200 }
199201 err := currentLogger .SendLog (wrapper )
@@ -237,18 +239,47 @@ func (logger *WebsocketLiveloggerWithFallback) SendLog(wrapper *protocol.Timelin
237239
238240type internalBufferedLiveLoggerData struct {
239241 logchan chan * protocol.TimelineRecordFeedLinesWrapper
242+ logdrain chan struct {}
240243 logfinished chan struct {}
241244}
242245
246+ // IsZero returns true if the struct is closed
247+ // * internalBufferedLiveLoggerData is replaced by a zero struct instead of nil to not auto restart
248+ func (i * internalBufferedLiveLoggerData ) IsZero () bool {
249+ return i == nil || i .logchan == nil || i .logdrain == nil || i .logfinished == nil
250+ }
251+
243252type BufferedLiveLogger struct {
244253 LiveLogger
245254 data atomic.Pointer [internalBufferedLiveLoggerData ]
246255}
247256
248- func (logger * BufferedLiveLogger ) sendLogs (logchan chan * protocol.TimelineRecordFeedLinesWrapper , logfinished chan struct {}) {
257+ func (logger * BufferedLiveLogger ) sendLogs (logchan chan * protocol.TimelineRecordFeedLinesWrapper , logdrain chan struct {}, logfinished chan struct {}) {
258+ defer func () {
259+ if r := recover (); r != nil {
260+ log .Printf ("panic recovered: %v\n %s" , r , debug .Stack ())
261+ }
262+ }()
249263 defer close (logfinished )
264+ var shouldDrain bool
250265 for {
251- lines , ok := <- logchan
266+ var lines * protocol.TimelineRecordFeedLinesWrapper
267+ var ok bool
268+ if ! shouldDrain {
269+ select {
270+ case lines , ok = <- logchan :
271+ case <- logdrain :
272+ shouldDrain = true
273+ }
274+ }
275+ if shouldDrain {
276+ // Now try read
277+ select {
278+ case lines , ok = <- logchan :
279+ default :
280+ ok = false
281+ }
282+ }
252283 if ! ok {
253284 return
254285 }
@@ -291,26 +322,36 @@ func (logger *BufferedLiveLogger) sendLogs(logchan chan *protocol.TimelineRecord
291322}
292323
293324func (logger * BufferedLiveLogger ) Close () error {
294- if data := logger .data .Swap (nil ); data != nil {
295- close (data .logchan )
325+ if data := logger .data .Swap (& internalBufferedLiveLoggerData {}); ! data .IsZero () {
326+ // Keep logchan open to avoid races and just let it be freed
327+ close (data .logdrain )
296328 <- data .logfinished
297329 }
298330 return nil
299331}
300332
301333func (logger * BufferedLiveLogger ) SendLog (wrapper * protocol.TimelineRecordFeedLinesWrapper ) error {
302334 if data := logger .data .Load (); data != nil {
303- data .logchan <- wrapper
335+ if data .IsZero () {
336+ return errors .New ("buffered live logger is closed" )
337+ }
338+ select {
339+ case <- data .logdrain :
340+ return errors .New ("buffered live logger closing" )
341+ default :
342+ data .logchan <- wrapper
343+ }
304344 } else {
305345 logchan := make (chan * protocol.TimelineRecordFeedLinesWrapper , websocketPingSize )
306346 logfinished := make (chan struct {})
307347 ndata := internalBufferedLiveLoggerData {
308348 logchan : logchan ,
349+ logdrain : make (chan struct {}),
309350 logfinished : logfinished ,
310351 }
311352 if logger .data .CompareAndSwap (data , & ndata ) {
312353 ndata .logchan <- wrapper
313- go logger .sendLogs (logchan , logfinished )
354+ go logger .sendLogs (logchan , ndata . logdrain , logfinished )
314355 } else {
315356 close (ndata .logchan )
316357 close (ndata .logfinished )
0 commit comments