setup.go 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. // Copyright (C) 2014 Space Monkey, Inc.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package spacelog
  15. import (
  16. "bytes"
  17. "fmt"
  18. "log"
  19. "math"
  20. "os"
  21. "os/signal"
  22. "regexp"
  23. "strings"
  24. "text/template"
  25. )
  26. // SetupConfig is a configuration struct meant to be used with
  27. // github.com/spacemonkeygo/flagfile/utils.Setup
  28. // but can be used independently.
  29. type SetupConfig struct {
  30. Output string `default:"stderr" usage:"log output. can be stdout, stderr, syslog, or a path"`
  31. Level string `default:"" usage:"base logger level"`
  32. Filter string `default:"" usage:"sets loggers matching this regular expression to the lowest level"`
  33. Format string `default:"" usage:"format string to use"`
  34. Stdlevel string `default:"warn" usage:"logger level for stdlib log integration"`
  35. 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}}'"`
  36. Buffer int `default:"0" usage:"the number of messages to buffer. 0 for no buffer"`
  37. // Facility defaults to syslog.LOG_USER (which is 8)
  38. Facility int `default:"8" usage:"the syslog facility to use if syslog output is configured"`
  39. HupRotate bool `default:"false" usage:"if true, sending a HUP signal will reopen log files"`
  40. Config string `default:"" usage:"a semicolon separated list of logger=level; sets each log to the corresponding level"`
  41. }
  42. var (
  43. stdlog = GetLoggerNamed("stdlog")
  44. funcmap = template.FuncMap{"ColorizeLevel": ColorizeLevel}
  45. )
  46. // SetFormatMethod adds functions to the template function map, such that
  47. // command-line and Setup provided templates can call methods added to the map
  48. // via this method. The map comes prepopulated with ColorizeLevel, but can be
  49. // overridden. SetFormatMethod should be called (if at all) before one of
  50. // this package's Setup methods.
  51. func SetFormatMethod(name string, fn interface{}) {
  52. funcmap[name] = fn
  53. }
  54. // MustSetup is the same as Setup, but panics instead of returning an error
  55. func MustSetup(procname string, config SetupConfig) {
  56. err := Setup(procname, config)
  57. if err != nil {
  58. panic(err)
  59. }
  60. }
  61. type subprocInfo struct {
  62. Facility string
  63. Level string
  64. Name string
  65. }
  66. // Setup takes a given procname and sets spacelog up with the given
  67. // configuration. Setup supports:
  68. // * capturing stdout and stderr to a subprocess
  69. // * configuring the default level
  70. // * configuring log filters (enabling only some loggers)
  71. // * configuring the logging template
  72. // * configuring the output (a file, syslog, stdout, stderr)
  73. // * configuring log event buffering
  74. // * capturing all standard library logging with configurable log level
  75. // It is expected that this method will be called once at process start.
  76. func Setup(procname string, config SetupConfig) error {
  77. if config.Subproc != "" {
  78. t, err := template.New("subproc").Parse(config.Subproc)
  79. if err != nil {
  80. return err
  81. }
  82. var buf bytes.Buffer
  83. err = t.Execute(&buf, &subprocInfo{
  84. Facility: fmt.Sprintf("%d", config.Facility),
  85. Level: fmt.Sprintf("%d", 2), // syslog.LOG_CRIT
  86. Name: procname})
  87. if err != nil {
  88. return err
  89. }
  90. err = CaptureOutputToProcess("sh", "-c", string(buf.Bytes()))
  91. if err != nil {
  92. return err
  93. }
  94. }
  95. if config.Config != "" {
  96. err := ConfigureLoggers(config.Config)
  97. if err != nil {
  98. return err
  99. }
  100. }
  101. if config.Level != "" {
  102. level_val, err := LevelFromString(config.Level)
  103. if err != nil {
  104. return err
  105. }
  106. if level_val != DefaultLevel {
  107. SetLevel(nil, level_val)
  108. }
  109. }
  110. if config.Filter != "" {
  111. re, err := regexp.Compile(config.Filter)
  112. if err != nil {
  113. return err
  114. }
  115. SetLevel(re, LogLevel(math.MinInt32))
  116. }
  117. var t *template.Template
  118. if config.Format != "" {
  119. var err error
  120. t, err = template.New("user").Funcs(funcmap).Parse(config.Format)
  121. if err != nil {
  122. return err
  123. }
  124. }
  125. var textout TextOutput
  126. switch strings.ToLower(config.Output) {
  127. case "syslog":
  128. w, err := NewSyslogOutput(SyslogPriority(config.Facility), procname)
  129. if err != nil {
  130. return err
  131. }
  132. if t == nil {
  133. t = SyslogTemplate
  134. }
  135. textout = w
  136. case "stdout":
  137. if t == nil {
  138. t = DefaultTemplate
  139. }
  140. textout = NewWriterOutput(os.Stdout)
  141. case "stderr", "":
  142. if t == nil {
  143. t = DefaultTemplate
  144. }
  145. textout = NewWriterOutput(os.Stderr)
  146. default:
  147. if t == nil {
  148. t = StandardTemplate
  149. }
  150. var err error
  151. textout, err = NewFileWriterOutput(config.Output)
  152. if err != nil {
  153. return err
  154. }
  155. }
  156. if config.HupRotate {
  157. if hh, ok := textout.(HupHandlingTextOutput); ok {
  158. sigchan := make(chan os.Signal)
  159. signal.Notify(sigchan, sigHUP)
  160. go func() {
  161. for _ = range sigchan {
  162. hh.OnHup()
  163. }
  164. }()
  165. }
  166. }
  167. if config.Buffer > 0 {
  168. textout = NewBufferedOutput(textout, config.Buffer)
  169. }
  170. SetHandler(nil, NewTextHandler(t, textout))
  171. log.SetFlags(log.Lshortfile)
  172. if config.Stdlevel == "" {
  173. config.Stdlevel = "warn"
  174. }
  175. stdlog_level_val, err := LevelFromString(config.Stdlevel)
  176. if err != nil {
  177. return err
  178. }
  179. log.SetOutput(stdlog.WriterWithoutCaller(stdlog_level_val))
  180. return nil
  181. }