| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189 |
- // 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
- }
|