You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

333 lines
9.1 KiB

  1. // Go support for leveled logs, analogous to https://code.google.com/p/google-glog/
  2. //
  3. // Copyright 2013 Google Inc. All Rights Reserved.
  4. //
  5. // Licensed under the Apache License, Version 2.0 (the "License");
  6. // you may not use this file except in compliance with the License.
  7. // You may obtain a copy of the License at
  8. //
  9. // http://www.apache.org/licenses/LICENSE-2.0
  10. //
  11. // Unless required by applicable law or agreed to in writing, software
  12. // distributed under the License is distributed on an "AS IS" BASIS,
  13. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. // See the License for the specific language governing permissions and
  15. // limitations under the License.
  16. package glog
  17. import (
  18. "bytes"
  19. "fmt"
  20. "path/filepath"
  21. "runtime"
  22. "strings"
  23. "testing"
  24. "time"
  25. )
  26. // Test that shortHostname works as advertised.
  27. func TestShortHostname(t *testing.T) {
  28. for hostname, expect := range map[string]string{
  29. "": "",
  30. "host": "host",
  31. "host.google.com": "host",
  32. } {
  33. if got := shortHostname(hostname); expect != got {
  34. t.Errorf("shortHostname(%q): expected %q, got %q", hostname, expect, got)
  35. }
  36. }
  37. }
  38. // flushBuffer wraps a bytes.Buffer to satisfy flushSyncWriter.
  39. type flushBuffer struct {
  40. bytes.Buffer
  41. }
  42. func (f *flushBuffer) Flush() error {
  43. return nil
  44. }
  45. func (f *flushBuffer) Sync() error {
  46. return nil
  47. }
  48. // swap sets the log writers and returns the old array.
  49. func (l *loggingT) swap(writers [numSeverity]flushSyncWriter) (old [numSeverity]flushSyncWriter) {
  50. l.mu.Lock()
  51. defer l.mu.Unlock()
  52. old = l.file
  53. for i, w := range writers {
  54. logging.file[i] = w
  55. }
  56. return
  57. }
  58. // newBuffers sets the log writers to all new byte buffers and returns the old array.
  59. func (l *loggingT) newBuffers() [numSeverity]flushSyncWriter {
  60. return l.swap([numSeverity]flushSyncWriter{new(flushBuffer), new(flushBuffer), new(flushBuffer), new(flushBuffer)})
  61. }
  62. // contents returns the specified log value as a string.
  63. func contents(s severity) string {
  64. return logging.file[s].(*flushBuffer).String()
  65. }
  66. // contains reports whether the string is contained in the log.
  67. func contains(s severity, str string, t *testing.T) bool {
  68. return strings.Contains(contents(s), str)
  69. }
  70. // setFlags configures the logging flags how the test expects them.
  71. func setFlags() {
  72. logging.toStderr = false
  73. }
  74. // Test that Info works as advertised.
  75. func TestInfo(t *testing.T) {
  76. setFlags()
  77. defer logging.swap(logging.newBuffers())
  78. Info("test")
  79. if !contains(infoLog, "I", t) {
  80. t.Errorf("Info has wrong character: %q", contents(infoLog))
  81. }
  82. if !contains(infoLog, "test", t) {
  83. t.Error("Info failed")
  84. }
  85. }
  86. // Test that the header has the correct format.
  87. func TestHeader(t *testing.T) {
  88. setFlags()
  89. defer logging.swap(logging.newBuffers())
  90. defer func(previous func() time.Time) { timeNow = previous }(timeNow)
  91. timeNow = func() time.Time {
  92. return time.Date(2006, 1, 2, 15, 4, 5, .678901e9, time.Local)
  93. }
  94. Info("test")
  95. var line, pid int
  96. n, err := fmt.Sscanf(contents(infoLog), "I0102 15:04:05 %d glog_test.go:%d] test\n", &pid, &line)
  97. if n != 2 || err != nil {
  98. t.Errorf("log format error: %d elements, error %s:\n%s", n, err, contents(infoLog))
  99. }
  100. }
  101. // Test that an Error log goes to Warning and Info.
  102. // Even in the Info log, the source character will be E, so the data should
  103. // all be identical.
  104. func TestError(t *testing.T) {
  105. setFlags()
  106. defer logging.swap(logging.newBuffers())
  107. Error("test")
  108. if !contains(errorLog, "E", t) {
  109. t.Errorf("Error has wrong character: %q", contents(errorLog))
  110. }
  111. if !contains(errorLog, "test", t) {
  112. t.Error("Error failed")
  113. }
  114. str := contents(errorLog)
  115. if !contains(warningLog, str, t) {
  116. t.Error("Warning failed")
  117. }
  118. if !contains(infoLog, str, t) {
  119. t.Error("Info failed")
  120. }
  121. }
  122. // Test that a Warning log goes to Info.
  123. // Even in the Info log, the source character will be W, so the data should
  124. // all be identical.
  125. func TestWarning(t *testing.T) {
  126. setFlags()
  127. defer logging.swap(logging.newBuffers())
  128. Warning("test")
  129. if !contains(warningLog, "W", t) {
  130. t.Errorf("Warning has wrong character: %q", contents(warningLog))
  131. }
  132. if !contains(warningLog, "test", t) {
  133. t.Error("Warning failed")
  134. }
  135. str := contents(warningLog)
  136. if !contains(infoLog, str, t) {
  137. t.Error("Info failed")
  138. }
  139. }
  140. // Test that a V log goes to Info.
  141. func TestV(t *testing.T) {
  142. setFlags()
  143. defer logging.swap(logging.newBuffers())
  144. logging.verbosity.Set("2")
  145. defer logging.verbosity.Set("0")
  146. V(2).Info("test")
  147. if !contains(infoLog, "I", t) {
  148. t.Errorf("Info has wrong character: %q", contents(infoLog))
  149. }
  150. if !contains(infoLog, "test", t) {
  151. t.Error("Info failed")
  152. }
  153. }
  154. // Test that a vmodule enables a log in this file.
  155. func TestVmoduleOn(t *testing.T) {
  156. setFlags()
  157. defer logging.swap(logging.newBuffers())
  158. logging.vmodule.Set("glog_test=2")
  159. defer logging.vmodule.Set("")
  160. if !V(1) {
  161. t.Error("V not enabled for 1")
  162. }
  163. if !V(2) {
  164. t.Error("V not enabled for 2")
  165. }
  166. if V(3) {
  167. t.Error("V enabled for 3")
  168. }
  169. V(2).Info("test")
  170. if !contains(infoLog, "I", t) {
  171. t.Errorf("Info has wrong character: %q", contents(infoLog))
  172. }
  173. if !contains(infoLog, "test", t) {
  174. t.Error("Info failed")
  175. }
  176. }
  177. // Test that a vmodule of another file does not enable a log in this file.
  178. func TestVmoduleOff(t *testing.T) {
  179. setFlags()
  180. defer logging.swap(logging.newBuffers())
  181. logging.vmodule.Set("notthisfile=2")
  182. defer logging.vmodule.Set("")
  183. for i := 1; i <= 3; i++ {
  184. if V(Level(i)) {
  185. t.Errorf("V enabled for %d", i)
  186. }
  187. }
  188. V(2).Info("test")
  189. if contents(infoLog) != "" {
  190. t.Error("V logged incorrectly")
  191. }
  192. }
  193. // vGlobs are patterns that match/don't match this file at V=2.
  194. var vGlobs = map[string]bool{
  195. // Easy to test the numeric match here.
  196. "glog_test=1": false, // If -vmodule sets V to 1, V(2) will fail.
  197. "glog_test=2": true,
  198. "glog_test=3": true, // If -vmodule sets V to 1, V(3) will succeed.
  199. // These all use 2 and check the patterns. All are true.
  200. "*=2": true,
  201. "?l*=2": true,
  202. "????_*=2": true,
  203. "??[mno]?_*t=2": true,
  204. // These all use 2 and check the patterns. All are false.
  205. "*x=2": false,
  206. "m*=2": false,
  207. "??_*=2": false,
  208. "?[abc]?_*t=2": false,
  209. }
  210. // Test that vmodule globbing works as advertised.
  211. func testVmoduleGlob(pat string, match bool, t *testing.T) {
  212. setFlags()
  213. defer logging.swap(logging.newBuffers())
  214. defer logging.vmodule.Set("")
  215. logging.vmodule.Set(pat)
  216. if V(2) != Verbose(match) {
  217. t.Errorf("incorrect match for %q: got %t expected %t", pat, V(2), match)
  218. }
  219. }
  220. // Test that a vmodule globbing works as advertised.
  221. func TestVmoduleGlob(t *testing.T) {
  222. for glob, match := range vGlobs {
  223. testVmoduleGlob(glob, match, t)
  224. }
  225. }
  226. func TestRollover(t *testing.T) {
  227. setFlags()
  228. var err error
  229. defer func(previous func(error)) { logExitFunc = previous }(logExitFunc)
  230. logExitFunc = func(e error) {
  231. err = e
  232. }
  233. defer func(previous uint64) { MaxSize = previous }(MaxSize)
  234. MaxSize = 512
  235. Info("x") // Be sure we have a file.
  236. info, ok := logging.file[infoLog].(*syncBuffer)
  237. if !ok {
  238. t.Fatal("info wasn't created")
  239. }
  240. if err != nil {
  241. t.Fatalf("info has initial error: %v", err)
  242. }
  243. fname0 := info.file.Name()
  244. Info(strings.Repeat("x", int(MaxSize))) // force a rollover
  245. if err != nil {
  246. t.Fatalf("info has error after big write: %v", err)
  247. }
  248. // Make sure the next log file gets a file name with a different
  249. // time stamp.
  250. //
  251. // TODO: determine whether we need to support subsecond log
  252. // rotation. C++ does not appear to handle this case (nor does it
  253. // handle Daylight Savings Time properly).
  254. time.Sleep(1 * time.Second)
  255. Info("x") // create a new file
  256. if err != nil {
  257. t.Fatalf("error after rotation: %v", err)
  258. }
  259. fname1 := info.file.Name()
  260. if fname0 == fname1 {
  261. t.Errorf("info.f.Name did not change: %v", fname0)
  262. }
  263. if info.nbytes >= MaxSize {
  264. t.Errorf("file size was not reset: %d", info.nbytes)
  265. }
  266. }
  267. func TestLogBacktraceAt(t *testing.T) {
  268. setFlags()
  269. defer logging.swap(logging.newBuffers())
  270. // The peculiar style of this code simplifies line counting and maintenance of the
  271. // tracing block below.
  272. var infoLine string
  273. setTraceLocation := func(file string, line int, ok bool, delta int) {
  274. if !ok {
  275. t.Fatal("could not get file:line")
  276. }
  277. _, file = filepath.Split(file)
  278. infoLine = fmt.Sprintf("%s:%d", file, line+delta)
  279. err := logging.traceLocation.Set(infoLine)
  280. if err != nil {
  281. t.Fatal("error setting log_backtrace_at: ", err)
  282. }
  283. }
  284. {
  285. // Start of tracing block. These lines know about each other's relative position.
  286. _, file, line, ok := runtime.Caller(0)
  287. setTraceLocation(file, line, ok, +2) // Two lines between Caller and Info calls.
  288. Info("we want a stack trace here")
  289. }
  290. numAppearances := strings.Count(contents(infoLog), infoLine)
  291. if numAppearances < 2 {
  292. // Need 2 appearances, one in the log header and one in the trace:
  293. // log_test.go:281: I0511 16:36:06.952398 02238 log_test.go:280] we want a stack trace here
  294. // ...
  295. // github.com/glog/glog_test.go:280 (0x41ba91)
  296. // ...
  297. // We could be more precise but that would require knowing the details
  298. // of the traceback format, which may not be dependable.
  299. t.Fatal("got no trace back; log is ", contents(infoLog))
  300. }
  301. }
  302. func BenchmarkHeader(b *testing.B) {
  303. for i := 0; i < b.N; i++ {
  304. logging.putBuffer(logging.header(infoLog))
  305. }
  306. }