PageRenderTime 45ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/log4go.go

http://github.com/moovweb/log4go
Go | 505 lines | 305 code | 46 blank | 154 comment | 32 complexity | 8e52c97bed37eb0fa2abee0b309660a6 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. // Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>. All rights reserved.
  2. // Enhanced Logging
  3. //
  4. // This is inspired by the logging functionality in Java. Essentially, you create a Logger
  5. // object and create output filters for it. You can send whatever you want to the Logger,
  6. // and it will filter that based on your settings and send it to the outputs. This way, you
  7. // can put as much debug code in your program as you want, and when you're done you can filter
  8. // out the mundane messages so only the important ones show up.
  9. //
  10. // Utility functions are provided to make life easier. Here is some example code to get started:
  11. //
  12. // log := log4go.NewLogger()
  13. // log.AddFilter("stdout", log4go.DEBUG, log4go.NewConsoleLogWriter())
  14. // log.AddFilter("log", log4go.FINE, log4go.NewFileLogWriter("example.log", true))
  15. // log.Info("The time is now: %s", time.LocalTime().Format("15:04:05 MST 2006/01/02"))
  16. //
  17. // The first two lines can be combined with the utility NewDefaultLogger:
  18. //
  19. // log := log4go.NewDefaultLogger(log4go.DEBUG)
  20. // log.AddFilter("log", log4go.FINE, log4go.NewFileLogWriter("example.log", true))
  21. // log.Info("The time is now: %s", time.LocalTime().Format("15:04:05 MST 2006/01/02"))
  22. //
  23. // Usage notes:
  24. // - The ConsoleLogWriter does not display the source of the message to standard
  25. // output, but the FileLogWriter does.
  26. // - The utility functions (Info, Debug, Warn, etc) derive their source from the
  27. // calling function, and this incurs extra overhead.
  28. //
  29. // Changes from 2.0:
  30. // - The external interface has remained mostly stable, but a lot of the
  31. // internals have been changed, so if you depended on any of this or created
  32. // your own LogWriter, then you will probably have to update your code. In
  33. // particular, Logger is now a map and ConsoleLogWriter is now a channel
  34. // behind-the-scenes, and the LogWrite method no longer has return values.
  35. //
  36. // Future work: (please let me know if you think I should work on any of these particularly)
  37. // - Log file rotation
  38. // - Logging configuration files ala log4j
  39. // - Have the ability to remove filters?
  40. // - Have GetInfoChannel, GetDebugChannel, etc return a chan string that allows
  41. // for another method of logging
  42. // - Add an XML filter type
  43. package log4go
  44. import (
  45. "errors"
  46. "fmt"
  47. "os"
  48. "runtime"
  49. "strings"
  50. "time"
  51. )
  52. // Version information
  53. const (
  54. L4G_VERSION = "log4go-v3.0.1"
  55. L4G_MAJOR = 3
  56. L4G_MINOR = 0
  57. L4G_BUILD = 1
  58. )
  59. /****** Constants ******/
  60. // These are the integer logging levels used by the logger
  61. type LogLevel int
  62. const INGORE LogLevel = -1
  63. const (
  64. EMERGENCY LogLevel = iota
  65. ALERT
  66. CRITICAL
  67. ERROR
  68. WARNING
  69. NOTICE
  70. INFO
  71. DEBUG
  72. )
  73. // Logging level strings
  74. var (
  75. levelStrings = [...]string{"EMEG", "ALRT", "CRIT", "EROR", "WARN", "NOTC", "INFO", "DEBG"}
  76. levelFullStrings = [...]string{"EMERGENCY", "ALERT", "CRITICAL", "ERROR", "WARNING", "NOTICE", "INFO", "DEBUG"}
  77. )
  78. func (l LogLevel) String() string {
  79. if l < 0 || int(l) > len(levelStrings) {
  80. return "IGNORE"
  81. }
  82. return levelStrings[int(l)]
  83. }
  84. func LevelStringToLevel(level string) int {
  85. level = strings.ToUpper(level)
  86. for i, val := range levelFullStrings {
  87. if val == level {
  88. return i
  89. }
  90. }
  91. return int(INGORE)
  92. }
  93. /****** Variables ******/
  94. var (
  95. // LogBufferLength specifies how many log messages a particular log4go
  96. // logger can buffer at a time before writing them.
  97. LogBufferLength = 32
  98. )
  99. /****** LogRecord ******/
  100. // A LogRecord contains all of the pertinent information for each message
  101. type LogRecord struct {
  102. Level LogLevel // The log level
  103. Created time.Time // The time at which the log message was created (nanoseconds)
  104. Source string // The message source
  105. Prefix string // The log message
  106. Message string // The log message
  107. }
  108. /****** LogWriter ******/
  109. // This is an interface for anything that should be able to write logs
  110. type LogWriter interface {
  111. // This will be called to log a LogRecord message.
  112. LogWrite(rec *LogRecord)
  113. // This should clean up anything lingering about the LogWriter, as it is called before
  114. // the LogWriter is removed. LogWrite should not be called after Close.
  115. Close()
  116. }
  117. /****** Logger ******/
  118. // A Filter represents the log level below which no log records are written to
  119. // the associated LogWriter.
  120. type Filter struct {
  121. Level LogLevel
  122. Prefix string
  123. LogWriter
  124. }
  125. // A Logger represents a collection of Filters through which log messages are
  126. // written.
  127. type Logger map[string]*Filter
  128. // Create a new logger.
  129. //
  130. // DEPRECATED: Use make(Logger) instead.
  131. func NewLogger() Logger {
  132. os.Stderr.WriteString("warning: use of deprecated NewLogger\n")
  133. return make(Logger)
  134. }
  135. // Create a new logger with a "stdout" filter configured to send log messages at
  136. // or above lvl to standard output.
  137. //
  138. // DEPRECATED: use NewDefaultLogger instead.
  139. func NewConsoleLogger(lvl LogLevel) Logger {
  140. os.Stderr.WriteString("warning: use of deprecated NewConsoleLogger\n")
  141. return Logger{
  142. "stdout": &Filter{lvl, "", NewConsoleLogWriter()},
  143. }
  144. }
  145. // Create a new logger with a "stdout" filter configured to send log messages at
  146. // or above lvl to standard output.
  147. func NewDefaultLogger(lvl LogLevel) Logger {
  148. return Logger{
  149. "stdout": &Filter{lvl, "", NewConsoleLogWriter()},
  150. }
  151. }
  152. // Closes all log writers in preparation for exiting the program or a
  153. // reconfiguration of logging. Calling this is not really imperative, unless
  154. // you want to guarantee that all log messages are written. Close removes
  155. // all filters (and thus all LogWriters) from the logger.
  156. func (log Logger) Close() {
  157. // Close all open loggers
  158. for name, filt := range log {
  159. filt.Close()
  160. delete(log, name)
  161. }
  162. }
  163. // Add a new LogWriter to the Logger which will only log messages at lvl or
  164. // higher. This function should not be called from multiple goroutines.
  165. // Returns the logger for chaining.
  166. func (log Logger) AddFilter(name string, lvl LogLevel, writer LogWriter) Logger {
  167. log[name] = &Filter{lvl, "", writer}
  168. return log
  169. }
  170. /******* Logging *******/
  171. // Send a formatted log message internally
  172. func (log Logger) intLogf(lvl LogLevel, format string, args ...interface{}) {
  173. skip := true
  174. prefix := ""
  175. // Determine if any logging will be done
  176. for _, filt := range log {
  177. if lvl <= filt.Level {
  178. skip = false
  179. prefix = filt.Prefix
  180. break
  181. }
  182. }
  183. if skip {
  184. return
  185. }
  186. // Determine caller func
  187. pc, _, lineno, ok := runtime.Caller(2)
  188. src := ""
  189. if ok {
  190. src = fmt.Sprintf("%s:%d", runtime.FuncForPC(pc).Name(), lineno)
  191. }
  192. msg := format
  193. if len(args) > 0 {
  194. msg = fmt.Sprintf(format, args...)
  195. }
  196. // Make the log record
  197. rec := &LogRecord{
  198. Level: lvl,
  199. Created: time.Now(),
  200. Source: src,
  201. Prefix: prefix,
  202. Message: msg,
  203. }
  204. // Dispatch the logs
  205. for _, filt := range log {
  206. if lvl > filt.Level {
  207. continue
  208. }
  209. filt.LogWrite(rec)
  210. }
  211. }
  212. // Send a closure log message internally
  213. func (log Logger) intLogc(lvl LogLevel, closure func() string) {
  214. skip := true
  215. // Determine if any logging will be done
  216. for _, filt := range log {
  217. if lvl <= filt.Level {
  218. skip = false
  219. break
  220. }
  221. }
  222. if skip {
  223. return
  224. }
  225. // Determine caller func
  226. pc, _, lineno, ok := runtime.Caller(2)
  227. src := ""
  228. if ok {
  229. src = fmt.Sprintf("%s:%d", runtime.FuncForPC(pc).Name(), lineno)
  230. }
  231. // Make the log record
  232. rec := &LogRecord{
  233. Level: lvl,
  234. Created: time.Now(),
  235. Source: src,
  236. Message: closure(),
  237. }
  238. // Dispatch the logs
  239. for _, filt := range log {
  240. if lvl > filt.Level {
  241. continue
  242. }
  243. filt.LogWrite(rec)
  244. }
  245. }
  246. // Send a log message with manual level, source, and message.
  247. func (log Logger) Log(lvl LogLevel, source, message string) {
  248. skip := true
  249. // Determine if any logging will be done
  250. for _, filt := range log {
  251. if lvl <= filt.Level {
  252. skip = false
  253. break
  254. }
  255. }
  256. if skip {
  257. return
  258. }
  259. // Make the log record
  260. rec := &LogRecord{
  261. Level: lvl,
  262. Created: time.Now(),
  263. Source: source,
  264. Message: message,
  265. }
  266. // Dispatch the logs
  267. for _, filt := range log {
  268. if lvl > filt.Level {
  269. continue
  270. }
  271. filt.LogWrite(rec)
  272. }
  273. }
  274. // Logf logs a formatted log message at the given log level, using the caller as
  275. // its source.
  276. func (log Logger) Logf(lvl LogLevel, format string, args ...interface{}) {
  277. log.intLogf(lvl, format, args...)
  278. }
  279. // Logc logs a string returned by the closure at the given log level, using the caller as
  280. // its source. If no log message would be written, the closure is never called.
  281. func (log Logger) Logc(lvl LogLevel, closure func() string) {
  282. log.intLogc(lvl, closure)
  283. }
  284. // Debug is a utility method for debug log messages.
  285. // The behavior of Debug depends on the first argument:
  286. // - arg0 is a string
  287. // When given a string as the first argument, this behaves like Logf but with
  288. // the DEBUG log level: the first argument is interpreted as a format for the
  289. // latter arguments.
  290. // - arg0 is a func()string
  291. // When given a closure of type func()string, this logs the string returned by
  292. // the closure iff it will be logged. The closure runs at most one time.
  293. // - arg0 is interface{}
  294. // When given anything else, the log message will be each of the arguments
  295. // formatted with %v and separated by spaces (ala Sprint).
  296. func (log Logger) Debug(arg0 interface{}, args ...interface{}) {
  297. const (
  298. lvl = DEBUG
  299. )
  300. switch first := arg0.(type) {
  301. case string:
  302. // Use the string as a format string
  303. log.intLogf(lvl, first, args...)
  304. case func() string:
  305. // Log the closure (no other arguments used)
  306. log.intLogc(lvl, first)
  307. default:
  308. // Build a format string so that it will be similar to Sprint
  309. log.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
  310. }
  311. }
  312. // Info logs a message at the info log level.
  313. // See Debug for an explanation of the arguments.
  314. func (log Logger) Info(arg0 interface{}, args ...interface{}) {
  315. const (
  316. lvl = INFO
  317. )
  318. switch first := arg0.(type) {
  319. case string:
  320. // Use the string as a format string
  321. log.intLogf(lvl, first, args...)
  322. case func() string:
  323. // Log the closure (no other arguments used)
  324. log.intLogc(lvl, first)
  325. default:
  326. // Build a format string so that it will be similar to Sprint
  327. log.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
  328. }
  329. }
  330. // Notice logs a message at the trace log level.
  331. // See Debug for an explanation of the arguments.
  332. func (log Logger) Notice(arg0 interface{}, args ...interface{}) {
  333. const (
  334. lvl = NOTICE
  335. )
  336. switch first := arg0.(type) {
  337. case string:
  338. // Use the string as a format string
  339. log.intLogf(lvl, first, args...)
  340. case func() string:
  341. // Log the closure (no other arguments used)
  342. log.intLogc(lvl, first)
  343. default:
  344. // Build a format string so that it will be similar to Sprint
  345. log.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
  346. }
  347. }
  348. // Warn logs a message at the warning log level and returns the formatted error.
  349. // At the warning level and higher, there is no performance benefit if the
  350. // message is not actually logged, because all formats are processed and all
  351. // closures are executed to format the error message.
  352. // See Debug for further explanation of the arguments.
  353. func (log Logger) Warn(arg0 interface{}, args ...interface{}) error {
  354. const (
  355. lvl = WARNING
  356. )
  357. var msg string
  358. switch first := arg0.(type) {
  359. case string:
  360. // Use the string as a format string
  361. msg = fmt.Sprintf(first, args...)
  362. case func() string:
  363. // Log the closure (no other arguments used)
  364. msg = first()
  365. default:
  366. // Build a format string so that it will be similar to Sprint
  367. msg = fmt.Sprintf(fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...)
  368. }
  369. log.intLogf(lvl, msg)
  370. return errors.New(msg)
  371. }
  372. // Error logs a message at the error log level and returns the formatted error,
  373. // See Warn for an explanation of the performance and Debug for an explanation
  374. // of the parameters.
  375. func (log Logger) Error(arg0 interface{}, args ...interface{}) error {
  376. const (
  377. lvl = ERROR
  378. )
  379. var msg string
  380. switch first := arg0.(type) {
  381. case string:
  382. // Use the string as a format string
  383. msg = fmt.Sprintf(first, args...)
  384. case func() string:
  385. // Log the closure (no other arguments used)
  386. msg = first()
  387. default:
  388. // Build a format string so that it will be similar to Sprint
  389. msg = fmt.Sprintf(fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...)
  390. }
  391. log.intLogf(lvl, msg)
  392. return errors.New(msg)
  393. }
  394. // Critical logs a message at the critical log level and returns the formatted error,
  395. // See Warn for an explanation of the performance and Debug for an explanation
  396. // of the parameters.
  397. func (log Logger) Critical(arg0 interface{}, args ...interface{}) error {
  398. const (
  399. lvl = CRITICAL
  400. )
  401. var msg string
  402. switch first := arg0.(type) {
  403. case string:
  404. // Use the string as a format string
  405. msg = fmt.Sprintf(first, args...)
  406. case func() string:
  407. // Log the closure (no other arguments used)
  408. msg = first()
  409. default:
  410. // Build a format string so that it will be similar to Sprint
  411. msg = fmt.Sprintf(fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...)
  412. }
  413. log.intLogf(lvl, msg)
  414. return errors.New(msg)
  415. }
  416. // Alert logs a message at the critical log level and returns the formatted error,
  417. // See Warn for an explanation of the performance and Debug for an explanation
  418. // of the parameters.
  419. func (log Logger) Alert(arg0 interface{}, args ...interface{}) error {
  420. const (
  421. lvl = ALERT
  422. )
  423. var msg string
  424. switch first := arg0.(type) {
  425. case string:
  426. // Use the string as a format string
  427. msg = fmt.Sprintf(first, args...)
  428. case func() string:
  429. // Log the closure (no other arguments used)
  430. msg = first()
  431. default:
  432. // Build a format string so that it will be similar to Sprint
  433. msg = fmt.Sprintf(fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...)
  434. }
  435. log.intLogf(lvl, msg)
  436. return errors.New(msg)
  437. }
  438. // Emergency logs a message at the critical log level and returns the formatted error,
  439. // See Warn for an explanation of the performance and Debug for an explanation
  440. // of the parameters.
  441. func (log Logger) Emergency(arg0 interface{}, args ...interface{}) error {
  442. const (
  443. lvl = EMERGENCY
  444. )
  445. var msg string
  446. switch first := arg0.(type) {
  447. case string:
  448. // Use the string as a format string
  449. msg = fmt.Sprintf(first, args...)
  450. case func() string:
  451. // Log the closure (no other arguments used)
  452. msg = first()
  453. default:
  454. // Build a format string so that it will be similar to Sprint
  455. msg = fmt.Sprintf(fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...)
  456. }
  457. log.intLogf(lvl, msg)
  458. return errors.New(msg)
  459. }