Documentation
¶
Overview ¶
Package logstore keeps container logs as per-day text files. Standard library only.
Index ¶
- Constants
- Variables
- func AppendEvent(root string, e Event) error
- func DayFile(root, app, service, container string, day time.Time) string
- func EventsFile(root string, day time.Time) string
- func FormatLine(at time.Time, stream, text string) []byte
- func GuessLevel(stream, text string) string
- func LastTimestamp(root, app, service, container string) (time.Time, bool)
- func ParseLine(b []byte) (at time.Time, stream, text string, ok bool)
- func PlanPurge(files []FileInfo, now time.Time, retentionDays int, maxBytes int64) []string
- func Purge(root string, now time.Time, retentionDays int, maxBytes int64) (removed int64, err error)
- func SafeName(s string) string
- func Truncate(text string) string
- func Usage(root string) (bytes int64, oldest time.Time, err error)
- type AppSummary
- type Cursor
- type Event
- type EventQuery
- type FileInfo
- type Filter
- type Hub
- type Line
- type Matcher
- type Message
- type Page
- type Query
- type ServiceSummary
- type Store
- func (s *Store) Apps() ([]AppSummary, error)
- func (s *Store) Close() error
- func (s *Store) CloseWriter(container string) error
- func (s *Store) Events(q EventQuery) ([]Event, error)
- func (s *Store) Purge(now time.Time, retentionDays int, maxBytes int64) (int64, error)
- func (s *Store) Read(ctx context.Context, q Query) (Page, error)
- func (s *Store) Record(e Event) error
- func (s *Store) Subscribe(m *Matcher) (<-chan Message, func())
- func (s *Store) Usage() (int64, time.Time, error)
- func (s *Store) Writer(app, service, container string) *Writer
- type Writer
Constants ¶
const MaxLineBytes = 16 << 10
const MaxPageBytes = 2 << 20
const QueueSize = 10_000
QueueSize is the per-subscriber backlog; beyond it the oldest message is dropped.
Variables ¶
var ( ErrBadFilter = errors.New("bad filter") ErrBothDirections = errors.New("since and before do not combine") )
var Kinds = []string{"created", "started", "stopped", "died", "oom-killed", "restarting", "unhealthy", "healthy", "installed", "uninstalled", "updated", "image-pulled"}
Functions ¶
func AppendEvent ¶
AppendEvent writes the event as one line in a single Write call, so concurrent appenders do not interleave.
func FormatLine ¶
FormatLine returns "<RFC3339Nano> <o|e> <text>\n" with \r and \n in text replaced by spaces.
func GuessLevel ¶
func LastTimestamp ¶
LastTimestamp returns the time of the last parsable line of the container's newest day file.
func PlanPurge ¶
PlanPurge returns the paths to remove: days older than the retention window, then whole oldest days until the rest fits maxBytes. Files with a zero Day are never planned. retentionDays<=0 and maxBytes<=0 disable their rule.
Types ¶
type AppSummary ¶
type AppSummary struct {
App string `json:"app"`
Services []ServiceSummary `json:"services"`
}
func ListApps ¶
func ListApps(root string) ([]AppSummary, error)
type Cursor ¶
type Cursor struct {
At time.Time
File string // path relative to root
Seq int64 // byte offset of the line's start in File: ordered like the lines, stable under appends, seekable
}
func ParseCursor ¶
ParseCursor wraps its error in ErrBadFilter: a cursor comes from the client like the filter does.
type Event ¶
type Event struct {
At time.Time `json:"at"`
App string `json:"app"`
Service string `json:"service"`
Container string `json:"container"`
Kind string `json:"kind"`
ExitCode string `json:"exit_code,omitempty"`
FromImage string `json:"from_image,omitempty"`
ToImage string `json:"to_image,omitempty"`
By string `json:"by"` // "operation" | "docker"
Detail string `json:"detail,omitempty"`
}
func ReadEvents ¶
func ReadEvents(root string, q EventQuery) ([]Event, error)
ReadEvents returns events newest first. Day files outside the Since/Until days are never opened.
type EventQuery ¶
type FileInfo ¶
type Filter ¶
type Hub ¶
type Hub struct {
// contains filtered or unexported fields
}
Hub fans messages out to subscribers. Publish never blocks: each subscriber has its own bounded queue.
type Line ¶
type Line struct {
At time.Time `json:"at"`
App string `json:"app"`
Service string `json:"service"`
Container string `json:"container"` // 12 hex chars
Stream string `json:"stream"` // "o" or "e"
Text string `json:"text"`
Cursor string `json:"cursor,omitempty"` // set by Read only: where the line is, to pass as since or before
}
type Matcher ¶
type Matcher struct {
// contains filtered or unexported fields
}
func (*Matcher) MatchEvent ¶
type Page ¶
type Page struct {
Lines []Line `json:"lines"`
Events []Event `json:"events"`
Next string `json:"next,omitempty"` // "" when exhausted
}
type Query ¶
type Query struct {
Filter
Since time.Time // with After: lower bound of the window; otherwise zero
Until time.Time
Before *Cursor // backwards (newest→oldest) strictly before; nil = from the end
After *Cursor // forwards (oldest→newest) strictly after; nil = backwards mode
Limit int // clamped to [1,1000], default 500
}
type ServiceSummary ¶
type Store ¶
type Store struct {
Root string
// OnError, if set before use, receives every writer's flush error (nil = ignored).
OnError func(error)
// contains filtered or unexported fields
}
Store is the single entry point of the package: it owns the writers, the hub and the root directory.
func Open ¶
Open creates root if needed, but never its grandparent (/DATA/AppData for /DATA/AppData/casaos/logs): while the data disk is missing nothing is written to the system disk, and the caller retries. An existing but unmounted mount point cannot be told apart from the disk itself. A symlinked root is resolved, so the purge walks the files it points to. ctx bounds the writers' flush loops.
func (*Store) Apps ¶
func (s *Store) Apps() ([]AppSummary, error)
func (*Store) CloseWriter ¶
CloseWriter flushes, closes and forgets the container's writer; unknown ids are a no-op.
func (*Store) Purge ¶
Purge makes every writer drop its open file afterwards: a removed file still held open would keep taking lines no reader sees, on space never freed; the next flush creates the file again. The files are also dropped before, so that a system refusing to remove an open file removes it.
func (*Store) Read ¶
Read flushes every writer first: any line's cursor may start a forwards poll, which must not pass a line still in a buffer.
type Writer ¶
type Writer struct {
// OnError, if set before use, receives every flush error (nil = ignored). The failed buffer is dropped.
OnError func(error)
// contains filtered or unexported fields
}
Writer appends one container's lines to its per-day files through a buffer.