mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-25 19:15:34 +00:00
b4ba492701
Buffer.Write has the exact same signature of io.Writer.Write. The latter requires that implementations to never retain the provided input buffer, which is an expectation that most users will have when they see a Write signature. The current behavior of Buffer.Write where it does retain the input buffer is a risky precedent to set. Switch the behavior to match io.Writer.Write. There are only two implementations of Buffer in existence: * logtail.memBuffer * filch.Filch The former can be fixed by cloning the input to Write. This will cause an extra allocation in every Write, but we can fix that will pooling on the caller side in a follow-up PR. The latter only passes the input to os.File.Write, which does respect the io.Writer.Write requirements. Updates #cleanup Updates tailscale/corp#18514 Signed-off-by: Joe Tsai <joetsai@digital-static.net>
84 lines
1.6 KiB
Go
84 lines
1.6 KiB
Go
// Copyright (c) Tailscale Inc & AUTHORS
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
package logtail
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"fmt"
|
|
"sync"
|
|
)
|
|
|
|
type Buffer interface {
|
|
// TryReadLine tries to read a log line from the ring buffer.
|
|
// If no line is available it returns a nil slice.
|
|
// If the ring buffer is closed it returns io.EOF.
|
|
//
|
|
// The returned slice may point to data that will be overwritten
|
|
// by a subsequent call to TryReadLine.
|
|
TryReadLine() ([]byte, error)
|
|
|
|
// Write writes a log line into the ring buffer.
|
|
// Implementations must not retain the provided buffer.
|
|
Write([]byte) (int, error)
|
|
}
|
|
|
|
func NewMemoryBuffer(numEntries int) Buffer {
|
|
return &memBuffer{
|
|
pending: make(chan qentry, numEntries),
|
|
}
|
|
}
|
|
|
|
type memBuffer struct {
|
|
next []byte
|
|
pending chan qentry
|
|
|
|
dropMu sync.Mutex
|
|
dropCount int
|
|
}
|
|
|
|
func (m *memBuffer) TryReadLine() ([]byte, error) {
|
|
if m.next != nil {
|
|
msg := m.next
|
|
m.next = nil
|
|
return msg, nil
|
|
}
|
|
|
|
select {
|
|
case ent := <-m.pending:
|
|
if ent.dropCount > 0 {
|
|
m.next = ent.msg
|
|
return fmt.Appendf(nil, "----------- %d logs dropped ----------", ent.dropCount), nil
|
|
}
|
|
return ent.msg, nil
|
|
default:
|
|
return nil, nil
|
|
}
|
|
}
|
|
|
|
func (m *memBuffer) Write(b []byte) (int, error) {
|
|
m.dropMu.Lock()
|
|
defer m.dropMu.Unlock()
|
|
|
|
ent := qentry{
|
|
msg: bytes.Clone(b),
|
|
dropCount: m.dropCount,
|
|
}
|
|
select {
|
|
case m.pending <- ent:
|
|
m.dropCount = 0
|
|
return len(b), nil
|
|
default:
|
|
m.dropCount++
|
|
return 0, errBufferFull
|
|
}
|
|
}
|
|
|
|
type qentry struct {
|
|
msg []byte
|
|
dropCount int
|
|
}
|
|
|
|
var errBufferFull = errors.New("logtail: buffer full")
|