PageRenderTime 66ms CodeModel.GetById 6ms RepoModel.GetById 1ms app.codeStats 0ms

/log4go.go

http://github.com/welterde/log4go
Go | 428 lines | 260 code | 43 blank | 125 comment | 27 complexity | 8865874f9ed15a5d09cc81e015c7d7e3 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 import ones show up.
  9. //
  10. // Utility functions are provided to make life easier. Here is some example code to get started:
  11. //
  12. // log := elog.NewLogger()
  13. // log.AddFilter("stdout", elog.DEBUG, new(elog.ConsoleLogWriter))
  14. // log.AddFilter("log", elog.FINE, elog.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 NewConsoleLogger:
  18. //
  19. // log := elog.NewConsoleLogger(elog.DEBUG)
  20. // log.AddFilter("log", elog.FINE, elog.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 to standard output, but the FileLogWriter does.
  25. // - The utility functions (Info, Debug, Warn, etc) derive their source from the calling function
  26. //
  27. // Future work: (please let me know if you think I should work on any of these particularly)
  28. // - Log file rotation
  29. // - Logging configuration files ala log4j
  30. // - Have the ability to remove filters?
  31. // - Have GetInfoChannel, GetDebugChannel, etc return a chan string that allows for another method of logging
  32. // - Add an XML filter type
  33. package log4go
  34. import (
  35. "os"
  36. "fmt"
  37. "time"
  38. "strings"
  39. "runtime"
  40. "container/vector"
  41. )
  42. // Version information
  43. const (
  44. L4G_VERSION = "log4go-v2.0.2"
  45. L4G_MAJOR = 2
  46. L4G_MINOR = 0
  47. L4G_BUILD = 2
  48. )
  49. /****** Constants ******/
  50. // These are the integer logging levels used by the logger
  51. const (
  52. FINEST = iota
  53. FINE
  54. DEBUG
  55. TRACE
  56. INFO
  57. WARNING
  58. ERROR
  59. CRITICAL
  60. )
  61. // Logging level strings
  62. var (
  63. levelStrings = [...]string{"FNST", "FINE", "DEBG", "TRAC", "INFO", "WARN", "EROR", "CRIT"}
  64. )
  65. /****** LogRecord ******/
  66. // This is the lifeblood of the package; it contains all of the pertinent information for each message
  67. type LogRecord struct {
  68. Level int // The log level
  69. Created *time.Time // The time at which the log message was created
  70. Source string // The message source
  71. Message string // The log message
  72. }
  73. func newLogRecord(lv int, src string, msg string) *LogRecord {
  74. lr := new(LogRecord)
  75. lr.Created = time.LocalTime()
  76. lr.Level = lv
  77. lr.Source = src
  78. lr.Message = msg
  79. return lr
  80. }
  81. /****** LogWriter ******/
  82. // This is an interface for anything that should be able to write logs
  83. type LogWriter interface {
  84. // This will be called to log a LogRecord message.
  85. // If necessary. this function should be *INTERNALLY* synchronzied,
  86. // and should spawn a separate goroutine if it could hang the program or take a long time.
  87. // TODO: This may be changed to have an Init() call that returns a
  88. // channel similar to <-chan *LogRecord for a more go-like internal setup
  89. LogWrite(rec *LogRecord) (n int, err os.Error)
  90. // This should return, at any given time, if the LogWriter is still in a good state.
  91. // A good state is defined as having the ability to dispatch a log message immediately.
  92. // if a LogWriter is not in a good state, the log message is simply not dispatched.
  93. Good() bool
  94. // This should clean up anything lingering about the LogWriter, as it is called before
  95. // the LogWriter is removed. If possible, this should guarantee that all LogWrites
  96. // have been completed.
  97. Close()
  98. }
  99. /****** Logger ******/
  100. // If LogRecord is the blood of the package, is the heart.
  101. type Logger struct {
  102. // All filters have an entry in each of the following
  103. filterLevels map[string]int
  104. filterLogWriters map[string]LogWriter
  105. }
  106. // Create a new logger
  107. func NewLogger() *Logger {
  108. log := new(Logger)
  109. log.filterLevels = make(map[string]int)
  110. log.filterLogWriters = make(map[string]LogWriter)
  111. return log
  112. }
  113. // Closes all log writers in preparation for exiting the program.
  114. // Calling this is not really imperative, unless you want to guarantee that all log messages are written.
  115. func (log *Logger) Close() {
  116. // Close all open loggers
  117. for key := range log.filterLogWriters {
  118. log.filterLogWriters[key].Close()
  119. log.filterLogWriters[key] = nil, false
  120. log.filterLevels[key] = 0, false
  121. }
  122. }
  123. // Add the standard filter.
  124. // This function is NOT INTERNALLY THREAD SAFE. If you plan on
  125. // calling this function from multiple goroutines, you will want
  126. // to synchronize it yourself somehow.
  127. // Returns self for chaining
  128. func (log *Logger) AddFilter(name string, level int, writer LogWriter) *Logger {
  129. if writer == nil || !writer.Good() {
  130. return nil
  131. }
  132. log.filterLevels[name] = level
  133. log.filterLogWriters[name] = writer
  134. return log
  135. }
  136. // Create a new logger with the standard stdout
  137. func NewConsoleLogger(level int) *Logger {
  138. log := NewLogger()
  139. log.AddFilter("stdout", level, new(ConsoleLogWriter))
  140. return log
  141. }
  142. /******* Logging *******/
  143. // Send a formatted log message internally
  144. func (log *Logger) intLogf(level int, format string, args ...interface{}) {
  145. // Create a vector long enough to not require resizing
  146. var logto vector.StringVector
  147. logto.Resize(0, len(log.filterLevels))
  148. // Determine if any logging will be done
  149. for filt := range log.filterLevels {
  150. if level >= log.filterLevels[filt] {
  151. logto.Push(filt)
  152. }
  153. }
  154. // Only log if a filter requires it
  155. if len(logto) > 0 {
  156. // Determine caller func
  157. pc, _, lineno, ok := runtime.Caller(2)
  158. src := ""
  159. if ok {
  160. src = fmt.Sprintf("%s:%d", runtime.FuncForPC(pc).Name(), lineno)
  161. }
  162. // Make the log record
  163. rec := newLogRecord(level, src, fmt.Sprintf(format, args...))
  164. // Dispatch the logs
  165. for _,filt := range logto {
  166. log.filterLogWriters[filt].LogWrite(rec)
  167. }
  168. }
  169. }
  170. // Send a closure log message internally
  171. func (log *Logger) intLogc(level int, closure func()string) {
  172. // Create a vector long enough to not require resizing
  173. var logto vector.StringVector
  174. logto.Resize(0, len(log.filterLevels))
  175. // Determine if any logging will be done
  176. for filt := range log.filterLevels {
  177. if level >= log.filterLevels[filt] {
  178. logto.Push(filt)
  179. }
  180. }
  181. // Only log if a filter requires it
  182. if len(logto) > 0 {
  183. // Determine caller func
  184. pc, _, lineno, ok := runtime.Caller(2)
  185. src := ""
  186. if ok {
  187. src = fmt.Sprintf("%s:%d", runtime.FuncForPC(pc).Name(), lineno)
  188. }
  189. // Make the log record from the closure's return
  190. rec := newLogRecord(level, src, closure())
  191. // Dispatch the logs
  192. for _,filt := range logto {
  193. log.filterLogWriters[filt].LogWrite(rec)
  194. }
  195. }
  196. }
  197. // Send a log message manually
  198. func (log *Logger) Log(level int, source, message string) {
  199. // Create a vector long enough to not require resizing
  200. var logto vector.StringVector
  201. logto.Resize(0, len(log.filterLevels))
  202. // Determine if any logging will be done
  203. for filt := range log.filterLevels {
  204. if level >= log.filterLevels[filt] {
  205. logto.Push(filt)
  206. }
  207. }
  208. // Only log if a filter requires it
  209. if len(logto) > 0 {
  210. // Make the log record
  211. rec := newLogRecord(level, source, message)
  212. // Dispatch the logs
  213. for _,filt := range logto {
  214. lw := log.filterLogWriters[filt]
  215. if lw.Good() {
  216. lw.LogWrite(rec)
  217. }
  218. }
  219. }
  220. }
  221. // Send a formatted log message easily
  222. func (log *Logger) Logf(level int, format string, args ...interface{}) {
  223. log.intLogf(level, format, args...)
  224. }
  225. // Send a closure log message
  226. func (log *Logger) Logc(level int, closure func()string) {
  227. log.intLogc(level, closure)
  228. }
  229. // Utility for finest log messages (see Debug() for parameter explanation)
  230. func (log *Logger) Finest(arg0 interface{}, args ...interface{}) {
  231. const (
  232. lvl = FINEST
  233. )
  234. switch first := arg0.(type) {
  235. case string:
  236. // Use the string as a format string
  237. log.intLogf(lvl, first, args...)
  238. case func()string:
  239. // Log the closure (no other arguments used)
  240. log.intLogc(lvl, first)
  241. default:
  242. // Build a format string so that it will be similar to Sprint
  243. log.intLogf(lvl, fmt.Sprint(arg0) + strings.Repeat(" %v", len(args)), args...)
  244. }
  245. }
  246. // Utility for fine log messages (see Debug() for parameter explanation)
  247. func (log *Logger) Fine(arg0 interface{}, args ...interface{}) {
  248. const (
  249. lvl = FINE
  250. )
  251. switch first := arg0.(type) {
  252. case string:
  253. // Use the string as a format string
  254. log.intLogf(lvl, first, args...)
  255. case func()string:
  256. // Log the closure (no other arguments used)
  257. log.intLogc(lvl, first)
  258. default:
  259. // Build a format string so that it will be similar to Sprint
  260. log.intLogf(lvl, fmt.Sprint(arg0) + strings.Repeat(" %v", len(args)), args...)
  261. }
  262. }
  263. // Utility for debug log messages
  264. // When given a string as the first argument, this behaves like Logf but with the DEBUG log level (e.g. the first argument is interpreted as a format for the latter arguments)
  265. // When given a closure of type func()string, this logs the string returned by the closure iff it will be logged. The closure runs at most one time.
  266. // When given anything else, the log message will be each of the arguments formatted with %v and separated by spaces (ala Sprint).
  267. func (log *Logger) Debug(arg0 interface{}, args ...interface{}) {
  268. const (
  269. lvl = DEBUG
  270. )
  271. switch first := arg0.(type) {
  272. case string:
  273. // Use the string as a format string
  274. log.intLogf(lvl, first, args...)
  275. case func()string:
  276. // Log the closure (no other arguments used)
  277. log.intLogc(lvl, first)
  278. default:
  279. // Build a format string so that it will be similar to Sprint
  280. log.intLogf(lvl, fmt.Sprint(arg0) + strings.Repeat(" %v", len(args)), args...)
  281. }
  282. }
  283. // Utility for trace log messages (see Debug() for parameter explanation)
  284. func (log *Logger) Trace(arg0 interface{}, args ...interface{}) {
  285. const (
  286. lvl = TRACE
  287. )
  288. switch first := arg0.(type) {
  289. case string:
  290. // Use the string as a format string
  291. log.intLogf(lvl, first, args...)
  292. case func()string:
  293. // Log the closure (no other arguments used)
  294. log.intLogc(lvl, first)
  295. default:
  296. // Build a format string so that it will be similar to Sprint
  297. log.intLogf(lvl, fmt.Sprint(arg0) + strings.Repeat(" %v", len(args)), args...)
  298. }
  299. }
  300. // Utility for info log messages (see Debug() for parameter explanation)
  301. func (log *Logger) Info(arg0 interface{}, args ...interface{}) {
  302. const (
  303. lvl = INFO
  304. )
  305. switch first := arg0.(type) {
  306. case string:
  307. // Use the string as a format string
  308. log.intLogf(lvl, first, args...)
  309. case func()string:
  310. // Log the closure (no other arguments used)
  311. log.intLogc(lvl, first)
  312. default:
  313. // Build a format string so that it will be similar to Sprint
  314. log.intLogf(lvl, fmt.Sprint(arg0) + strings.Repeat(" %v", len(args)), args...)
  315. }
  316. }
  317. // Utility for warn log messages (returns an os.Error for easy function returns) (see Debug() for parameter explanation)
  318. // These functions will execute a closure exactly once, to build the error message for the return
  319. func (log *Logger) Warn(arg0 interface{}, args ...interface{}) os.Error {
  320. const (
  321. lvl = WARNING
  322. )
  323. switch first := arg0.(type) {
  324. case string:
  325. // Use the string as a format string
  326. log.intLogf(lvl, first, args...)
  327. return os.NewError(fmt.Sprintf(first, args...))
  328. case func()string:
  329. // Log the closure (no other arguments used)
  330. str := first()
  331. log.intLogf(lvl, "%s", str)
  332. return os.NewError(str)
  333. default:
  334. // Build a format string so that it will be similar to Sprint
  335. log.intLogf(lvl, fmt.Sprint(first) + strings.Repeat(" %v", len(args)), args...)
  336. return os.NewError(fmt.Sprint(first) + fmt.Sprintf(strings.Repeat(" %v", len(args)), args...))
  337. }
  338. return nil
  339. }
  340. // Utility for error log messages (returns an os.Error for easy function returns) (see Debug() for parameter explanation)
  341. // These functions will execute a closure exactly once, to build the error message for the return
  342. func (log *Logger) Error(arg0 interface{}, args ...interface{}) os.Error {
  343. const (
  344. lvl = ERROR
  345. )
  346. switch first := arg0.(type) {
  347. case string:
  348. // Use the string as a format string
  349. log.intLogf(lvl, first, args...)
  350. return os.NewError(fmt.Sprintf(first, args...))
  351. case func()string:
  352. // Log the closure (no other arguments used)
  353. str := first()
  354. log.intLogf(lvl, "%s", str)
  355. return os.NewError(str)
  356. default:
  357. // Build a format string so that it will be similar to Sprint
  358. log.intLogf(lvl, fmt.Sprint(first) + strings.Repeat(" %v", len(args)), args...)
  359. return os.NewError(fmt.Sprint(first) + fmt.Sprintf(strings.Repeat(" %v", len(args)), args...))
  360. }
  361. return nil
  362. }
  363. // Utility for critical log messages (returns an os.Error for easy function returns) (see Debug() for parameter explanation)
  364. // These functions will execute a closure exactly once, to build the error message for the return
  365. func (log *Logger) Critical(arg0 interface{}, args ...interface{}) os.Error {
  366. const (
  367. lvl = CRITICAL
  368. )
  369. switch first := arg0.(type) {
  370. case string:
  371. // Use the string as a format string
  372. log.intLogf(lvl, first, args...)
  373. return os.NewError(fmt.Sprintf(first, args...))
  374. case func()string:
  375. // Log the closure (no other arguments used)
  376. str := first()
  377. log.intLogf(lvl, "%s", str)
  378. return os.NewError(str)
  379. default:
  380. // Build a format string so that it will be similar to Sprint
  381. log.intLogf(lvl, fmt.Sprint(first) + strings.Repeat(" %v", len(args)), args...)
  382. return os.NewError(fmt.Sprint(first) + fmt.Sprintf(strings.Repeat(" %v", len(args)), args...))
  383. }
  384. return nil
  385. }