status-go/vendor/github.com/spacemonkeygo/spacelog/setup.go

190 lines
5.6 KiB
Go

// Copyright (C) 2014 Space Monkey, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package spacelog
import (
"bytes"
"fmt"
"log"
"math"
"os"
"os/signal"
"regexp"
"strings"
"text/template"
)
// SetupConfig is a configuration struct meant to be used with
// github.com/spacemonkeygo/flagfile/utils.Setup
// but can be used independently.
type SetupConfig struct {
Output string `default:"stderr" usage:"log output. can be stdout, stderr, syslog, or a path"`
Level string `default:"" usage:"base logger level"`
Filter string `default:"" usage:"sets loggers matching this regular expression to the lowest level"`
Format string `default:"" usage:"format string to use"`
Stdlevel string `default:"warn" usage:"logger level for stdlib log integration"`
Subproc string `default:"" usage:"process to run for stdout/stderr-captured logging. The command is first processed as a Go template that supports {{.Facility}}, {{.Level}}, and {{.Name}} fields, and then passed to sh. If set, will redirect stdout and stderr to the given process. A good default is 'setsid logger --priority {{.Facility}}.{{.Level}} --tag {{.Name}}'"`
Buffer int `default:"0" usage:"the number of messages to buffer. 0 for no buffer"`
// Facility defaults to syslog.LOG_USER (which is 8)
Facility int `default:"8" usage:"the syslog facility to use if syslog output is configured"`
HupRotate bool `default:"false" usage:"if true, sending a HUP signal will reopen log files"`
Config string `default:"" usage:"a semicolon separated list of logger=level; sets each log to the corresponding level"`
}
var (
stdlog = GetLoggerNamed("stdlog")
funcmap = template.FuncMap{"ColorizeLevel": ColorizeLevel}
)
// SetFormatMethod adds functions to the template function map, such that
// command-line and Setup provided templates can call methods added to the map
// via this method. The map comes prepopulated with ColorizeLevel, but can be
// overridden. SetFormatMethod should be called (if at all) before one of
// this package's Setup methods.
func SetFormatMethod(name string, fn interface{}) {
funcmap[name] = fn
}
// MustSetup is the same as Setup, but panics instead of returning an error
func MustSetup(procname string, config SetupConfig) {
err := Setup(procname, config)
if err != nil {
panic(err)
}
}
type subprocInfo struct {
Facility string
Level string
Name string
}
// Setup takes a given procname and sets spacelog up with the given
// configuration. Setup supports:
// * capturing stdout and stderr to a subprocess
// * configuring the default level
// * configuring log filters (enabling only some loggers)
// * configuring the logging template
// * configuring the output (a file, syslog, stdout, stderr)
// * configuring log event buffering
// * capturing all standard library logging with configurable log level
// It is expected that this method will be called once at process start.
func Setup(procname string, config SetupConfig) error {
if config.Subproc != "" {
t, err := template.New("subproc").Parse(config.Subproc)
if err != nil {
return err
}
var buf bytes.Buffer
err = t.Execute(&buf, &subprocInfo{
Facility: fmt.Sprintf("%d", config.Facility),
Level: fmt.Sprintf("%d", 2), // syslog.LOG_CRIT
Name: procname})
if err != nil {
return err
}
err = CaptureOutputToProcess("sh", "-c", string(buf.Bytes()))
if err != nil {
return err
}
}
if config.Config != "" {
err := ConfigureLoggers(config.Config)
if err != nil {
return err
}
}
if config.Level != "" {
level_val, err := LevelFromString(config.Level)
if err != nil {
return err
}
if level_val != DefaultLevel {
SetLevel(nil, level_val)
}
}
if config.Filter != "" {
re, err := regexp.Compile(config.Filter)
if err != nil {
return err
}
SetLevel(re, LogLevel(math.MinInt32))
}
var t *template.Template
if config.Format != "" {
var err error
t, err = template.New("user").Funcs(funcmap).Parse(config.Format)
if err != nil {
return err
}
}
var textout TextOutput
switch strings.ToLower(config.Output) {
case "syslog":
w, err := NewSyslogOutput(SyslogPriority(config.Facility), procname)
if err != nil {
return err
}
if t == nil {
t = SyslogTemplate
}
textout = w
case "stdout":
if t == nil {
t = DefaultTemplate
}
textout = NewWriterOutput(os.Stdout)
case "stderr", "":
if t == nil {
t = DefaultTemplate
}
textout = NewWriterOutput(os.Stderr)
default:
if t == nil {
t = StandardTemplate
}
var err error
textout, err = NewFileWriterOutput(config.Output)
if err != nil {
return err
}
}
if config.HupRotate {
if hh, ok := textout.(HupHandlingTextOutput); ok {
sigchan := make(chan os.Signal)
signal.Notify(sigchan, sigHUP)
go func() {
for _ = range sigchan {
hh.OnHup()
}
}()
}
}
if config.Buffer > 0 {
textout = NewBufferedOutput(textout, config.Buffer)
}
SetHandler(nil, NewTextHandler(t, textout))
log.SetFlags(log.Lshortfile)
if config.Stdlevel == "" {
config.Stdlevel = "warn"
}
stdlog_level_val, err := LevelFromString(config.Stdlevel)
if err != nil {
return err
}
log.SetOutput(stdlog.WriterWithoutCaller(stdlog_level_val))
return nil
}