config.go 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. // Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>. All rights reserved.
  2. package log4go
  3. import (
  4. "encoding/xml"
  5. "fmt"
  6. "io/ioutil"
  7. "os"
  8. "strconv"
  9. "strings"
  10. )
  11. type xmlProperty struct {
  12. Name string `xml:"name,attr"`
  13. Value string `xml:",chardata"`
  14. }
  15. type xmlFilter struct {
  16. Enabled string `xml:"enabled,attr"`
  17. Tag string `xml:"tag"`
  18. Level string `xml:"level"`
  19. Type string `xml:"type"`
  20. Property []xmlProperty `xml:"property"`
  21. }
  22. type xmlLoggerConfig struct {
  23. Filter []xmlFilter `xml:"filter"`
  24. }
  25. // Load XML configuration; see examples/example.xml for documentation
  26. func (log Logger) LoadConfiguration(filename string) {
  27. log.Close()
  28. // Open the configuration file
  29. fd, err := os.Open(filename)
  30. if err != nil {
  31. fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Could not open %q for reading: %s\n", filename, err)
  32. os.Exit(1)
  33. }
  34. contents, err := ioutil.ReadAll(fd)
  35. if err != nil {
  36. fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Could not read %q: %s\n", filename, err)
  37. os.Exit(1)
  38. }
  39. xc := new(xmlLoggerConfig)
  40. if err := xml.Unmarshal(contents, xc); err != nil {
  41. fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Could not parse XML configuration in %q: %s\n", filename, err)
  42. os.Exit(1)
  43. }
  44. for _, xmlfilt := range xc.Filter {
  45. var filt LogWriter
  46. var lvl Level
  47. bad, good, enabled := false, true, false
  48. // Check required children
  49. if len(xmlfilt.Enabled) == 0 {
  50. fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required attribute %s for filter missing in %s\n", "enabled", filename)
  51. bad = true
  52. } else {
  53. enabled = xmlfilt.Enabled != "false"
  54. }
  55. if len(xmlfilt.Tag) == 0 {
  56. fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required child <%s> for filter missing in %s\n", "tag", filename)
  57. bad = true
  58. }
  59. if len(xmlfilt.Type) == 0 {
  60. fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required child <%s> for filter missing in %s\n", "type", filename)
  61. bad = true
  62. }
  63. if len(xmlfilt.Level) == 0 {
  64. fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required child <%s> for filter missing in %s\n", "level", filename)
  65. bad = true
  66. }
  67. switch xmlfilt.Level {
  68. case "FINEST":
  69. lvl = FINEST
  70. case "FINE":
  71. lvl = FINE
  72. case "DEBUG":
  73. lvl = DEBUG
  74. case "TRACE":
  75. lvl = TRACE
  76. case "INFO":
  77. lvl = INFO
  78. case "WARNING":
  79. lvl = WARNING
  80. case "ERROR":
  81. lvl = ERROR
  82. case "CRITICAL":
  83. lvl = CRITICAL
  84. default:
  85. fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required child <%s> for filter has unknown value in %s: %s\n", "level", filename, xmlfilt.Level)
  86. bad = true
  87. }
  88. // Just so all of the required attributes are errored at the same time if missing
  89. if bad {
  90. os.Exit(1)
  91. }
  92. switch xmlfilt.Type {
  93. case "console":
  94. filt, good = xmlToConsoleLogWriter(filename, xmlfilt.Property, enabled)
  95. case "file":
  96. filt, good = xmlToFileLogWriter(filename, xmlfilt.Property, enabled)
  97. case "xml":
  98. filt, good = xmlToXMLLogWriter(filename, xmlfilt.Property, enabled)
  99. case "socket":
  100. filt, good = xmlToSocketLogWriter(filename, xmlfilt.Property, enabled)
  101. default:
  102. fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Could not load XML configuration in %s: unknown filter type \"%s\"\n", filename, xmlfilt.Type)
  103. os.Exit(1)
  104. }
  105. // Just so all of the required params are errored at the same time if wrong
  106. if !good {
  107. os.Exit(1)
  108. }
  109. // If we're disabled (syntax and correctness checks only), don't add to logger
  110. if !enabled {
  111. continue
  112. }
  113. log[xmlfilt.Tag] = &Filter{lvl, filt}
  114. }
  115. }
  116. func xmlToConsoleLogWriter(filename string, props []xmlProperty, enabled bool) (*ConsoleLogWriter, bool) {
  117. format := "[%D %T] [%L] (%S) %M"
  118. // Parse properties
  119. for _, prop := range props {
  120. switch prop.Name {
  121. case "format":
  122. format = strings.Trim(prop.Value, " \r\n")
  123. default:
  124. fmt.Fprintf(os.Stderr, "LoadConfiguration: Warning: Unknown property \"%s\" for console filter in %s\n", prop.Name, filename)
  125. }
  126. }
  127. // If it's disabled, we're just checking syntax
  128. if !enabled {
  129. return nil, true
  130. }
  131. clw := NewConsoleLogWriter()
  132. clw.SetFormat(format)
  133. return clw, true
  134. }
  135. // Parse a number with K/M/G suffixes based on thousands (1000) or 2^10 (1024)
  136. func strToNumSuffix(str string, mult int) int {
  137. num := 1
  138. if len(str) > 1 {
  139. switch str[len(str)-1] {
  140. case 'G', 'g':
  141. num *= mult
  142. fallthrough
  143. case 'M', 'm':
  144. num *= mult
  145. fallthrough
  146. case 'K', 'k':
  147. num *= mult
  148. str = str[0 : len(str)-1]
  149. }
  150. }
  151. parsed, _ := strconv.Atoi(str)
  152. return parsed * num
  153. }
  154. func xmlToFileLogWriter(filename string, props []xmlProperty, enabled bool) (*FileLogWriter, bool) {
  155. file := ""
  156. format := "[%D %T] [%L] (%S) %M"
  157. maxlines := 0
  158. maxsize := 0
  159. daily := false
  160. rotate := false
  161. // Parse properties
  162. for _, prop := range props {
  163. switch prop.Name {
  164. case "filename":
  165. file = strings.Trim(prop.Value, " \r\n")
  166. case "format":
  167. format = strings.Trim(prop.Value, " \r\n")
  168. case "maxlines":
  169. maxlines = strToNumSuffix(strings.Trim(prop.Value, " \r\n"), 1000)
  170. case "maxsize":
  171. maxsize = strToNumSuffix(strings.Trim(prop.Value, " \r\n"), 1024)
  172. case "daily":
  173. daily = strings.Trim(prop.Value, " \r\n") != "false"
  174. case "rotate":
  175. rotate = strings.Trim(prop.Value, " \r\n") != "false"
  176. default:
  177. fmt.Fprintf(os.Stderr, "LoadConfiguration: Warning: Unknown property \"%s\" for file filter in %s\n", prop.Name, filename)
  178. }
  179. }
  180. // Check properties
  181. if len(file) == 0 {
  182. fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required property \"%s\" for file filter missing in %s\n", "filename", filename)
  183. return nil, false
  184. }
  185. // If it's disabled, we're just checking syntax
  186. if !enabled {
  187. return nil, true
  188. }
  189. flw := NewFileLogWriter(file, rotate)
  190. flw.SetFormat(format)
  191. flw.SetRotateLines(maxlines)
  192. flw.SetRotateSize(maxsize)
  193. flw.SetRotateDaily(daily)
  194. return flw, true
  195. }
  196. func xmlToXMLLogWriter(filename string, props []xmlProperty, enabled bool) (*FileLogWriter, bool) {
  197. file := ""
  198. maxrecords := 0
  199. maxsize := 0
  200. daily := false
  201. rotate := false
  202. // Parse properties
  203. for _, prop := range props {
  204. switch prop.Name {
  205. case "filename":
  206. file = strings.Trim(prop.Value, " \r\n")
  207. case "maxrecords":
  208. maxrecords = strToNumSuffix(strings.Trim(prop.Value, " \r\n"), 1000)
  209. case "maxsize":
  210. maxsize = strToNumSuffix(strings.Trim(prop.Value, " \r\n"), 1024)
  211. case "daily":
  212. daily = strings.Trim(prop.Value, " \r\n") != "false"
  213. case "rotate":
  214. rotate = strings.Trim(prop.Value, " \r\n") != "false"
  215. default:
  216. fmt.Fprintf(os.Stderr, "LoadConfiguration: Warning: Unknown property \"%s\" for xml filter in %s\n", prop.Name, filename)
  217. }
  218. }
  219. // Check properties
  220. if len(file) == 0 {
  221. fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required property \"%s\" for xml filter missing in %s\n", "filename", filename)
  222. return nil, false
  223. }
  224. // If it's disabled, we're just checking syntax
  225. if !enabled {
  226. return nil, true
  227. }
  228. xlw := NewXMLLogWriter(file, rotate)
  229. xlw.SetRotateLines(maxrecords)
  230. xlw.SetRotateSize(maxsize)
  231. xlw.SetRotateDaily(daily)
  232. return xlw, true
  233. }
  234. func xmlToSocketLogWriter(filename string, props []xmlProperty, enabled bool) (SocketLogWriter, bool) {
  235. endpoint := ""
  236. protocol := "udp"
  237. // Parse properties
  238. for _, prop := range props {
  239. switch prop.Name {
  240. case "endpoint":
  241. endpoint = strings.Trim(prop.Value, " \r\n")
  242. case "protocol":
  243. protocol = strings.Trim(prop.Value, " \r\n")
  244. default:
  245. fmt.Fprintf(os.Stderr, "LoadConfiguration: Warning: Unknown property \"%s\" for file filter in %s\n", prop.Name, filename)
  246. }
  247. }
  248. // Check properties
  249. if len(endpoint) == 0 {
  250. fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required property \"%s\" for file filter missing in %s\n", "endpoint", filename)
  251. return nil, false
  252. }
  253. // If it's disabled, we're just checking syntax
  254. if !enabled {
  255. return nil, true
  256. }
  257. return NewSocketLogWriter(protocol, endpoint), true
  258. }