buffer.go 1.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. //go:build !ts_omit_logtail
  4. package logtail
  5. import (
  6. "bytes"
  7. "errors"
  8. "fmt"
  9. "tailscale.com/syncs"
  10. )
  11. type Buffer interface {
  12. // TryReadLine tries to read a log line from the ring buffer.
  13. // If no line is available it returns a nil slice.
  14. // If the ring buffer is closed it returns io.EOF.
  15. //
  16. // The returned slice may point to data that will be overwritten
  17. // by a subsequent call to TryReadLine.
  18. TryReadLine() ([]byte, error)
  19. // Write writes a log line into the ring buffer.
  20. // Implementations must not retain the provided buffer.
  21. Write([]byte) (int, error)
  22. }
  23. func NewMemoryBuffer(numEntries int) Buffer {
  24. return &memBuffer{
  25. pending: make(chan qentry, numEntries),
  26. }
  27. }
  28. type memBuffer struct {
  29. next []byte
  30. pending chan qentry
  31. dropMu syncs.Mutex
  32. dropCount int
  33. }
  34. func (m *memBuffer) TryReadLine() ([]byte, error) {
  35. if m.next != nil {
  36. msg := m.next
  37. m.next = nil
  38. return msg, nil
  39. }
  40. select {
  41. case ent := <-m.pending:
  42. if ent.dropCount > 0 {
  43. m.next = ent.msg
  44. return fmt.Appendf(nil, "----------- %d logs dropped ----------", ent.dropCount), nil
  45. }
  46. return ent.msg, nil
  47. default:
  48. return nil, nil
  49. }
  50. }
  51. func (m *memBuffer) Write(b []byte) (int, error) {
  52. m.dropMu.Lock()
  53. defer m.dropMu.Unlock()
  54. ent := qentry{
  55. msg: bytes.Clone(b),
  56. dropCount: m.dropCount,
  57. }
  58. select {
  59. case m.pending <- ent:
  60. m.dropCount = 0
  61. return len(b), nil
  62. default:
  63. m.dropCount++
  64. return 0, errBufferFull
  65. }
  66. }
  67. type qentry struct {
  68. msg []byte
  69. dropCount int
  70. }
  71. var errBufferFull = errors.New("logtail: buffer full")