logmap.go 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. // Copyright 2015 CoreOS, 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 capnslog
  15. import (
  16. "errors"
  17. "strings"
  18. "sync"
  19. )
  20. // LogLevel is the set of all log levels.
  21. type LogLevel int8
  22. const (
  23. // CRITICAL is the lowest log level; only errors which will end the program will be propagated.
  24. CRITICAL LogLevel = iota - 1
  25. // ERROR is for errors that are not fatal but lead to troubling behavior.
  26. ERROR
  27. // WARNING is for errors which are not fatal and not errors, but are unusual. Often sourced from misconfigurations.
  28. WARNING
  29. // NOTICE is for normal but significant conditions.
  30. NOTICE
  31. // INFO is a log level for common, everyday log updates.
  32. INFO
  33. // DEBUG is the default hidden level for more verbose updates about internal processes.
  34. DEBUG
  35. // TRACE is for (potentially) call by call tracing of programs.
  36. TRACE
  37. )
  38. // Char returns a single-character representation of the log level.
  39. func (l LogLevel) Char() string {
  40. switch l {
  41. case CRITICAL:
  42. return "C"
  43. case ERROR:
  44. return "E"
  45. case WARNING:
  46. return "W"
  47. case NOTICE:
  48. return "N"
  49. case INFO:
  50. return "I"
  51. case DEBUG:
  52. return "D"
  53. case TRACE:
  54. return "T"
  55. default:
  56. panic("Unhandled loglevel")
  57. }
  58. }
  59. // String returns a multi-character representation of the log level.
  60. func (l LogLevel) String() string {
  61. switch l {
  62. case CRITICAL:
  63. return "CRITICAL"
  64. case ERROR:
  65. return "ERROR"
  66. case WARNING:
  67. return "WARNING"
  68. case NOTICE:
  69. return "NOTICE"
  70. case INFO:
  71. return "INFO"
  72. case DEBUG:
  73. return "DEBUG"
  74. case TRACE:
  75. return "TRACE"
  76. default:
  77. panic("Unhandled loglevel")
  78. }
  79. }
  80. // Update using the given string value. Fulfills the flag.Value interface.
  81. func (l *LogLevel) Set(s string) error {
  82. value, err := ParseLevel(s)
  83. if err != nil {
  84. return err
  85. }
  86. *l = value
  87. return nil
  88. }
  89. // ParseLevel translates some potential loglevel strings into their corresponding levels.
  90. func ParseLevel(s string) (LogLevel, error) {
  91. switch s {
  92. case "CRITICAL", "C":
  93. return CRITICAL, nil
  94. case "ERROR", "0", "E":
  95. return ERROR, nil
  96. case "WARNING", "1", "W":
  97. return WARNING, nil
  98. case "NOTICE", "2", "N":
  99. return NOTICE, nil
  100. case "INFO", "3", "I":
  101. return INFO, nil
  102. case "DEBUG", "4", "D":
  103. return DEBUG, nil
  104. case "TRACE", "5", "T":
  105. return TRACE, nil
  106. }
  107. return CRITICAL, errors.New("couldn't parse log level " + s)
  108. }
  109. type RepoLogger map[string]*PackageLogger
  110. type loggerStruct struct {
  111. sync.Mutex
  112. repoMap map[string]RepoLogger
  113. formatter Formatter
  114. }
  115. // logger is the global logger
  116. var logger = new(loggerStruct)
  117. // SetGlobalLogLevel sets the log level for all packages in all repositories
  118. // registered with capnslog.
  119. func SetGlobalLogLevel(l LogLevel) {
  120. logger.Lock()
  121. defer logger.Unlock()
  122. for _, r := range logger.repoMap {
  123. r.setRepoLogLevelInternal(l)
  124. }
  125. }
  126. // GetRepoLogger may return the handle to the repository's set of packages' loggers.
  127. func GetRepoLogger(repo string) (RepoLogger, error) {
  128. logger.Lock()
  129. defer logger.Unlock()
  130. r, ok := logger.repoMap[repo]
  131. if !ok {
  132. return nil, errors.New("no packages registered for repo " + repo)
  133. }
  134. return r, nil
  135. }
  136. // MustRepoLogger returns the handle to the repository's packages' loggers.
  137. func MustRepoLogger(repo string) RepoLogger {
  138. r, err := GetRepoLogger(repo)
  139. if err != nil {
  140. panic(err)
  141. }
  142. return r
  143. }
  144. // SetRepoLogLevel sets the log level for all packages in the repository.
  145. func (r RepoLogger) SetRepoLogLevel(l LogLevel) {
  146. logger.Lock()
  147. defer logger.Unlock()
  148. r.setRepoLogLevelInternal(l)
  149. }
  150. func (r RepoLogger) setRepoLogLevelInternal(l LogLevel) {
  151. for _, v := range r {
  152. v.level = l
  153. }
  154. }
  155. // ParseLogLevelConfig parses a comma-separated string of "package=loglevel", in
  156. // order, and returns a map of the results, for use in SetLogLevel.
  157. func (r RepoLogger) ParseLogLevelConfig(conf string) (map[string]LogLevel, error) {
  158. setlist := strings.Split(conf, ",")
  159. out := make(map[string]LogLevel)
  160. for _, setstring := range setlist {
  161. setting := strings.Split(setstring, "=")
  162. if len(setting) != 2 {
  163. return nil, errors.New("oddly structured `pkg=level` option: " + setstring)
  164. }
  165. l, err := ParseLevel(setting[1])
  166. if err != nil {
  167. return nil, err
  168. }
  169. out[setting[0]] = l
  170. }
  171. return out, nil
  172. }
  173. // SetLogLevel takes a map of package names within a repository to their desired
  174. // loglevel, and sets the levels appropriately. Unknown packages are ignored.
  175. // "*" is a special package name that corresponds to all packages, and will be
  176. // processed first.
  177. func (r RepoLogger) SetLogLevel(m map[string]LogLevel) {
  178. logger.Lock()
  179. defer logger.Unlock()
  180. if l, ok := m["*"]; ok {
  181. r.setRepoLogLevelInternal(l)
  182. }
  183. for k, v := range m {
  184. l, ok := r[k]
  185. if !ok {
  186. continue
  187. }
  188. l.level = v
  189. }
  190. }
  191. // SetFormatter sets the formatting function for all logs.
  192. func SetFormatter(f Formatter) {
  193. logger.Lock()
  194. defer logger.Unlock()
  195. logger.formatter = f
  196. }
  197. // NewPackageLogger creates a package logger object.
  198. // This should be defined as a global var in your package, referencing your repo.
  199. func NewPackageLogger(repo string, pkg string) (p *PackageLogger) {
  200. logger.Lock()
  201. defer logger.Unlock()
  202. if logger.repoMap == nil {
  203. logger.repoMap = make(map[string]RepoLogger)
  204. }
  205. r, rok := logger.repoMap[repo]
  206. if !rok {
  207. logger.repoMap[repo] = make(RepoLogger)
  208. r = logger.repoMap[repo]
  209. }
  210. p, pok := r[pkg]
  211. if !pok {
  212. r[pkg] = &PackageLogger{
  213. pkg: pkg,
  214. level: INFO,
  215. }
  216. p = r[pkg]
  217. }
  218. return
  219. }