logpolicy: add some docs

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
pull/10/head
Brad Fitzpatrick 5 years ago committed by Brad Fitzpatrick
parent de0239375a
commit fbfe474492

@ -2,6 +2,9 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// Package logpolicy manages the creation or reuse of logtail loggers,
// caching collection instance state on disk for use on future runs of
// programs on the same machine.
package logpolicy package logpolicy
import ( import (
@ -21,17 +24,22 @@ import (
"tailscale.com/version" "tailscale.com/version"
) )
// Config represents an instance of logs in a collection.
type Config struct { type Config struct {
Collection string Collection string
PrivateID logtail.PrivateID PrivateID logtail.PrivateID
PublicID logtail.PublicID PublicID logtail.PublicID
} }
// Policy is a logger and its public ID.
type Policy struct { type Policy struct {
Logtail logtail.Logger // Logtail is the logger.
Logtail logtail.Logger
// PublicID is the logger's instance identifier.
PublicID logtail.PublicID PublicID logtail.PublicID
} }
// ToBytes returns the JSON representation of c.
func (c *Config) ToBytes() []byte { func (c *Config) ToBytes() []byte {
data, err := json.MarshalIndent(c, "", "\t") data, err := json.MarshalIndent(c, "", "\t")
if err != nil { if err != nil {
@ -40,28 +48,34 @@ func (c *Config) ToBytes() []byte {
return data return data
} }
func (c *Config) Save(statefile string) { // Save writes the JSON representation of c to stateFile.
func (c *Config) Save(stateFile string) error {
c.PublicID = c.PrivateID.Public() c.PublicID = c.PrivateID.Public()
os.MkdirAll(filepath.Dir(statefile), 0777) if err := os.MkdirAll(filepath.Dir(stateFile), 0777); err != nil {
return err
}
data := c.ToBytes() data := c.ToBytes()
if err := atomicfile.WriteFile(statefile, data, 0600); err != nil { if err := atomicfile.WriteFile(stateFile, data, 0600); err != nil {
log.Printf("logpolicy.Config write: %v\n", err) return err
} }
return nil
} }
func ConfigFromBytes(b []byte) (*Config, error) { // ConfigFromBytes parses a a Config from its JSON encoding.
func ConfigFromBytes(jsonEnc []byte) (*Config, error) {
c := &Config{} c := &Config{}
if err := json.Unmarshal(b, c); err != nil { if err := json.Unmarshal(jsonEnc, c); err != nil {
return nil, err return nil, err
} }
return c, nil return c, nil
} }
// stderrWriter is an io.Writer that always writes to the latest
// os.Stderr, even if os.Stderr changes during the lifetime of the
// stderrWriter value.
type stderrWriter struct{} type stderrWriter struct{}
// Always writes to the latest os.Stderr, even if os.Stderr changes func (stderrWriter) Write(buf []byte) (int, error) {
// during the lifetime of this object.
func (l *stderrWriter) Write(buf []byte) (int, error) {
return os.Stderr.Write(buf) return os.Stderr.Write(buf)
} }
@ -69,25 +83,31 @@ type logWriter struct {
logger *log.Logger logger *log.Logger
} }
func (l *logWriter) Write(buf []byte) (int, error) { func (l logWriter) Write(buf []byte) (int, error) {
l.logger.Print(string(buf)) l.logger.Printf("%s", buf)
return len(buf), nil return len(buf), nil
} }
func New(collection string, filePrefix string) *Policy { // New returns a new log policy (a logger and its instance ID) for a
statefile := filePrefix + ".log.conf" // given collection name. The provided filePrefix is used as a
// filename prefix for both for the logger's state file, as well as
// temporary log entries themselves.
//
// TODO: the state and the logs locations should perhaps be separated.
func New(collection, filePrefix string) *Policy {
stateFile := filePrefix + ".log.conf"
var lflags int var lflags int
if terminal.IsTerminal(2) || runtime.GOOS == "windows" { if terminal.IsTerminal(2) || runtime.GOOS == "windows" {
lflags = 0 lflags = 0
} else { } else {
lflags = log.LstdFlags lflags = log.LstdFlags
} }
console := log.New(&stderrWriter{}, "", lflags) console := log.New(stderrWriter{}, "", lflags)
var oldc *Config var oldc *Config
data, err := ioutil.ReadFile(statefile) data, err := ioutil.ReadFile(stateFile)
if err != nil { if err != nil {
log.Printf("logpolicy.Read %v: %v\n", statefile, err) log.Printf("logpolicy.Read %v: %v\n", stateFile, err)
oldc = &Config{} oldc = &Config{}
oldc.Collection = collection oldc.Collection = collection
} else { } else {
@ -114,13 +134,15 @@ func New(collection string, filePrefix string) *Policy {
} }
newc.PublicID = newc.PrivateID.Public() newc.PublicID = newc.PrivateID.Public()
if newc != *oldc { if newc != *oldc {
newc.Save(statefile) if err := newc.Save(stateFile); err != nil {
log.Printf("logpolicy.Config.Save: %v\n", err)
}
} }
c := logtail.Config{ c := logtail.Config{
Collection: newc.Collection, Collection: newc.Collection,
PrivateID: newc.PrivateID, PrivateID: newc.PrivateID,
Stderr: &logWriter{console}, Stderr: logWriter{console},
NewZstdEncoder: func() logtail.Encoder { NewZstdEncoder: func() logtail.Encoder {
w, err := zstd.NewWriter(nil) w, err := zstd.NewWriter(nil)
if err != nil { if err != nil {
@ -163,8 +185,8 @@ func (p *Policy) Close() {
// Shutdown gracefully shuts down the logger, finishing any current // Shutdown gracefully shuts down the logger, finishing any current
// log upload if it can be done before ctx is canceled. // log upload if it can be done before ctx is canceled.
func (p *Policy) Shutdown(ctx context.Context) error { func (p *Policy) Shutdown(ctx context.Context) error {
log.Printf("flushing log.\n")
if p.Logtail != nil { if p.Logtail != nil {
log.Printf("flushing log.\n")
return p.Logtail.Shutdown(ctx) return p.Logtail.Shutdown(ctx)
} }
return nil return nil

Loading…
Cancel
Save