collection.go 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  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. "regexp"
  17. "runtime"
  18. "strings"
  19. "sync"
  20. "text/template"
  21. )
  22. var (
  23. // If set, these prefixes will be stripped out of automatic logger names.
  24. IgnoredPrefixes []string
  25. badChars = regexp.MustCompile("[^a-zA-Z0-9_.-]")
  26. slashes = regexp.MustCompile("[/]")
  27. )
  28. func callerName() string {
  29. pc, _, _, ok := runtime.Caller(2)
  30. if !ok {
  31. return "unknown.unknown"
  32. }
  33. f := runtime.FuncForPC(pc)
  34. if f == nil {
  35. return "unknown.unknown"
  36. }
  37. name := f.Name()
  38. for _, prefix := range IgnoredPrefixes {
  39. name = strings.TrimPrefix(name, prefix)
  40. }
  41. return badChars.ReplaceAllLiteralString(
  42. slashes.ReplaceAllLiteralString(name, "."), "_")
  43. }
  44. // LoggerCollections contain all of the loggers a program might use. Typically
  45. // a codebase will just use the default logger collection.
  46. type LoggerCollection struct {
  47. mtx sync.Mutex
  48. loggers map[string]*Logger
  49. level LogLevel
  50. handler Handler
  51. }
  52. // NewLoggerCollection creates a new logger collection. It's unlikely you will
  53. // ever practically need this method. Use the DefaultLoggerCollection instead.
  54. func NewLoggerCollection() *LoggerCollection {
  55. return &LoggerCollection{
  56. loggers: make(map[string]*Logger),
  57. level: DefaultLevel,
  58. handler: defaultHandler}
  59. }
  60. // GetLogger returns a new Logger with a name automatically generated using
  61. // the callstack. If you want to avoid automatic name generation check out
  62. // GetLoggerNamed
  63. func (c *LoggerCollection) GetLogger() *Logger {
  64. return c.GetLoggerNamed(callerName())
  65. }
  66. func (c *LoggerCollection) getLogger(name string, level LogLevel,
  67. handler Handler) *Logger {
  68. c.mtx.Lock()
  69. defer c.mtx.Unlock()
  70. logger, exists := c.loggers[name]
  71. if !exists {
  72. logger = &Logger{level: level,
  73. collection: c,
  74. name: name,
  75. handler: handler}
  76. c.loggers[name] = logger
  77. }
  78. return logger
  79. }
  80. // ConfigureLoggers configures loggers according to the given string
  81. // specification, which specifies a set of loggers and their associated
  82. // logging levels. Loggers are semicolon-separated; each
  83. // configuration is specified as <logger>=<level>. White space outside of
  84. // logger names and levels is ignored. The default level is specified
  85. // with the name "DEFAULT".
  86. //
  87. // An example specification:
  88. // `DEFAULT=ERROR; foo.bar=WARNING`
  89. func (c *LoggerCollection) ConfigureLoggers(specification string) error {
  90. confs := strings.Split(strings.TrimSpace(specification), ";")
  91. for i := range confs {
  92. conf := strings.SplitN(confs[i], "=", 2)
  93. levelstr := strings.TrimSpace(conf[1])
  94. name := strings.TrimSpace(conf[0])
  95. level, err := LevelFromString(levelstr)
  96. if err != nil {
  97. return err
  98. }
  99. if name == "DEFAULT" {
  100. c.level = level
  101. continue
  102. }
  103. logger := c.GetLoggerNamed(name)
  104. logger.level = level
  105. }
  106. return nil
  107. }
  108. // GetLoggerNamed returns a new Logger with the provided name. GetLogger is
  109. // more frequently used.
  110. func (c *LoggerCollection) GetLoggerNamed(name string) *Logger {
  111. c.mtx.Lock()
  112. defer c.mtx.Unlock()
  113. logger, exists := c.loggers[name]
  114. if !exists {
  115. logger = &Logger{level: c.level,
  116. collection: c,
  117. name: name,
  118. handler: c.handler}
  119. c.loggers[name] = logger
  120. }
  121. return logger
  122. }
  123. // SetLevel will set the current log level for all loggers with names that
  124. // match a provided regular expression. If the regular expression is nil, then
  125. // all loggers match.
  126. func (c *LoggerCollection) SetLevel(re *regexp.Regexp, level LogLevel) {
  127. c.mtx.Lock()
  128. defer c.mtx.Unlock()
  129. if re == nil {
  130. c.level = level
  131. }
  132. for name, logger := range c.loggers {
  133. if re == nil || re.MatchString(name) {
  134. logger.setLevel(level)
  135. }
  136. }
  137. }
  138. // SetHandler will set the current log handler for all loggers with names that
  139. // match a provided regular expression. If the regular expression is nil, then
  140. // all loggers match.
  141. func (c *LoggerCollection) SetHandler(re *regexp.Regexp, handler Handler) {
  142. c.mtx.Lock()
  143. defer c.mtx.Unlock()
  144. if re == nil {
  145. c.handler = handler
  146. }
  147. for name, logger := range c.loggers {
  148. if re == nil || re.MatchString(name) {
  149. logger.setHandler(handler)
  150. }
  151. }
  152. }
  153. // SetTextTemplate will set the current text template for all loggers with
  154. // names that match a provided regular expression. If the regular expression
  155. // is nil, then all loggers match. Note that not every handler is guaranteed
  156. // to support text templates and a text template will only apply to
  157. // text-oriented and unstructured handlers.
  158. func (c *LoggerCollection) SetTextTemplate(re *regexp.Regexp,
  159. t *template.Template) {
  160. c.mtx.Lock()
  161. defer c.mtx.Unlock()
  162. if re == nil {
  163. c.handler.SetTextTemplate(t)
  164. }
  165. for name, logger := range c.loggers {
  166. if re == nil || re.MatchString(name) {
  167. logger.getHandler().SetTextTemplate(t)
  168. }
  169. }
  170. }
  171. // SetTextOutput will set the current output interface for all loggers with
  172. // names that match a provided regular expression. If the regular expression
  173. // is nil, then all loggers match. Note that not every handler is guaranteed
  174. // to support text output and a text output interface will only apply to
  175. // text-oriented and unstructured handlers.
  176. func (c *LoggerCollection) SetTextOutput(re *regexp.Regexp,
  177. output TextOutput) {
  178. c.mtx.Lock()
  179. defer c.mtx.Unlock()
  180. if re == nil {
  181. c.handler.SetTextOutput(output)
  182. }
  183. for name, logger := range c.loggers {
  184. if re == nil || re.MatchString(name) {
  185. logger.getHandler().SetTextOutput(output)
  186. }
  187. }
  188. }
  189. var (
  190. // It's unlikely you'll need to use this directly
  191. DefaultLoggerCollection = NewLoggerCollection()
  192. )
  193. // GetLogger returns an automatically-named logger on the default logger
  194. // collection.
  195. func GetLogger() *Logger {
  196. return DefaultLoggerCollection.GetLoggerNamed(callerName())
  197. }
  198. // GetLoggerNamed returns a new Logger with the provided name on the default
  199. // logger collection. GetLogger is more frequently used.
  200. func GetLoggerNamed(name string) *Logger {
  201. return DefaultLoggerCollection.GetLoggerNamed(name)
  202. }
  203. // ConfigureLoggers configures loggers according to the given string
  204. // specification, which specifies a set of loggers and their associated
  205. // logging levels. Loggers are colon- or semicolon-separated; each
  206. // configuration is specified as <logger>=<level>. White space outside of
  207. // logger names and levels is ignored. The DEFAULT module is specified
  208. // with the name "DEFAULT".
  209. //
  210. // An example specification:
  211. // `DEFAULT=ERROR; foo.bar=WARNING`
  212. func ConfigureLoggers(specification string) error {
  213. return DefaultLoggerCollection.ConfigureLoggers(specification)
  214. }
  215. // SetLevel will set the current log level for all loggers on the default
  216. // collection with names that match a provided regular expression. If the
  217. // regular expression is nil, then all loggers match.
  218. func SetLevel(re *regexp.Regexp, level LogLevel) {
  219. DefaultLoggerCollection.SetLevel(re, level)
  220. }
  221. // SetHandler will set the current log handler for all loggers on the default
  222. // collection with names that match a provided regular expression. If the
  223. // regular expression is nil, then all loggers match.
  224. func SetHandler(re *regexp.Regexp, handler Handler) {
  225. DefaultLoggerCollection.SetHandler(re, handler)
  226. }
  227. // SetTextTemplate will set the current text template for all loggers on the
  228. // default collection with names that match a provided regular expression. If
  229. // the regular expression is nil, then all loggers match. Note that not every
  230. // handler is guaranteed to support text templates and a text template will
  231. // only apply to text-oriented and unstructured handlers.
  232. func SetTextTemplate(re *regexp.Regexp, t *template.Template) {
  233. DefaultLoggerCollection.SetTextTemplate(re, t)
  234. }
  235. // SetTextOutput will set the current output interface for all loggers on the
  236. // default collection with names that match a provided regular expression. If
  237. // the regular expression is nil, then all loggers match. Note that not every
  238. // handler is guaranteed to support text output and a text output interface
  239. // will only apply to text-oriented and unstructured handlers.
  240. func SetTextOutput(re *regexp.Regexp, output TextOutput) {
  241. DefaultLoggerCollection.SetTextOutput(re, output)
  242. }