mirror of
https://github.com/tailscale/tailscale.git
synced 2025-10-09 08:01:31 +00:00
Move Linux client & common packages into a public repo.
This commit is contained in:
171
logpolicy/logpolicy.go
Normal file
171
logpolicy/logpolicy.go
Normal file
@@ -0,0 +1,171 @@
|
||||
// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package logpolicy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
|
||||
"github.com/klauspost/compress/zstd"
|
||||
"golang.org/x/crypto/ssh/terminal"
|
||||
"tailscale.com/atomicfile"
|
||||
"tailscale.com/logtail"
|
||||
"tailscale.com/logtail/filch"
|
||||
"tailscale.com/version"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Collection string
|
||||
PrivateID logtail.PrivateID
|
||||
PublicID logtail.PublicID
|
||||
}
|
||||
|
||||
type Policy struct {
|
||||
Logtail logtail.Logger
|
||||
PublicID logtail.PublicID
|
||||
}
|
||||
|
||||
func (c *Config) ToBytes() []byte {
|
||||
data, err := json.MarshalIndent(c, "", "\t")
|
||||
if err != nil {
|
||||
log.Fatalf("logpolicy.Config marshal: %v\n", err)
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
func (c *Config) Save(statefile string) {
|
||||
c.PublicID = c.PrivateID.Public()
|
||||
os.MkdirAll(filepath.Dir(statefile), 0777)
|
||||
data := c.ToBytes()
|
||||
if err := atomicfile.WriteFile(statefile, data, 0600); err != nil {
|
||||
log.Printf("logpolicy.Config write: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
func ConfigFromBytes(b []byte) (*Config, error) {
|
||||
c := &Config{}
|
||||
if err := json.Unmarshal(b, c); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
type stderrWriter struct{}
|
||||
|
||||
// Always writes to the latest os.Stderr, even if os.Stderr changes
|
||||
// during the lifetime of this object.
|
||||
func (l *stderrWriter) Write(buf []byte) (int, error) {
|
||||
return os.Stderr.Write(buf)
|
||||
}
|
||||
|
||||
type logWriter struct {
|
||||
logger *log.Logger
|
||||
}
|
||||
|
||||
func (l *logWriter) Write(buf []byte) (int, error) {
|
||||
l.logger.Print(string(buf))
|
||||
return len(buf), nil
|
||||
}
|
||||
|
||||
func New(collection string, filePrefix string) *Policy {
|
||||
statefile := filePrefix + ".log.conf"
|
||||
var lflags int
|
||||
if terminal.IsTerminal(2) || runtime.GOOS == "windows" {
|
||||
lflags = 0
|
||||
} else {
|
||||
lflags = log.LstdFlags
|
||||
}
|
||||
console := log.New(&stderrWriter{}, "", lflags)
|
||||
|
||||
var oldc *Config
|
||||
data, err := ioutil.ReadFile(statefile)
|
||||
if err != nil {
|
||||
log.Printf("logpolicy.Read %v: %v\n", statefile, err)
|
||||
oldc = &Config{}
|
||||
oldc.Collection = collection
|
||||
} else {
|
||||
oldc, err = ConfigFromBytes(data)
|
||||
if err != nil {
|
||||
log.Printf("logpolicy.Config unmarshal: %v\n", err)
|
||||
oldc = &Config{}
|
||||
}
|
||||
}
|
||||
|
||||
newc := *oldc
|
||||
if newc.Collection != collection {
|
||||
log.Printf("logpolicy.Config: config collection %q does not match %q", newc.Collection, collection)
|
||||
// We picked up an incompatible config file.
|
||||
// Regenerate the private ID.
|
||||
newc.PrivateID = logtail.PrivateID{}
|
||||
newc.Collection = collection
|
||||
}
|
||||
if newc.PrivateID == (logtail.PrivateID{}) {
|
||||
newc.PrivateID, err = logtail.NewPrivateID()
|
||||
if err != nil {
|
||||
log.Fatalf("logpolicy: NewPrivateID() should never fail")
|
||||
}
|
||||
}
|
||||
newc.PublicID = newc.PrivateID.Public()
|
||||
if newc != *oldc {
|
||||
newc.Save(statefile)
|
||||
}
|
||||
|
||||
c := logtail.Config{
|
||||
Collection: newc.Collection,
|
||||
PrivateID: newc.PrivateID,
|
||||
Stderr: &logWriter{console},
|
||||
NewZstdEncoder: func() logtail.Encoder {
|
||||
w, err := zstd.NewWriter(nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return w
|
||||
},
|
||||
}
|
||||
|
||||
// TODO(crawshaw): filePrefix is a place meant to store configuration.
|
||||
// OS policies usually have other preferred places to
|
||||
// store logs. Use one of them?
|
||||
filchBuf, filchErr := filch.New(filePrefix, filch.Options{})
|
||||
if filchBuf != nil {
|
||||
c.Buffer = filchBuf
|
||||
}
|
||||
lw := logtail.Log(c)
|
||||
log.SetFlags(0) // other logflags are set on console, not here
|
||||
log.SetOutput(lw)
|
||||
|
||||
log.Printf("Program starting: v%v: %#v\n", version.LONG, os.Args)
|
||||
log.Printf("LogID: %v\n", newc.PublicID)
|
||||
if filchErr != nil {
|
||||
log.Printf("filch failed: %v", err)
|
||||
}
|
||||
|
||||
return &Policy{
|
||||
Logtail: lw,
|
||||
PublicID: newc.PublicID,
|
||||
}
|
||||
}
|
||||
|
||||
// Close immediately shuts down the logger.
|
||||
func (p *Policy) Close() {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel()
|
||||
p.Shutdown(ctx)
|
||||
}
|
||||
|
||||
// Shutdown gracefully shuts down the logger, finishing any current
|
||||
// log upload if it can be done before ctx is canceled.
|
||||
func (p *Policy) Shutdown(ctx context.Context) error {
|
||||
log.Printf("flushing log.\n")
|
||||
if p.Logtail != nil {
|
||||
return p.Logtail.Shutdown(ctx)
|
||||
}
|
||||
return nil
|
||||
}
|
Reference in New Issue
Block a user