glog_test.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  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. stdLog "log"
  21. "path/filepath"
  22. "runtime"
  23. "strconv"
  24. "strings"
  25. "testing"
  26. "time"
  27. )
  28. // Test that shortHostname works as advertised.
  29. func TestShortHostname(t *testing.T) {
  30. for hostname, expect := range map[string]string{
  31. "": "",
  32. "host": "host",
  33. "host.google.com": "host",
  34. } {
  35. if got := shortHostname(hostname); expect != got {
  36. t.Errorf("shortHostname(%q): expected %q, got %q", hostname, expect, got)
  37. }
  38. }
  39. }
  40. // flushBuffer wraps a bytes.Buffer to satisfy flushSyncWriter.
  41. type flushBuffer struct {
  42. bytes.Buffer
  43. }
  44. func (f *flushBuffer) Flush() error {
  45. return nil
  46. }
  47. func (f *flushBuffer) Sync() error {
  48. return nil
  49. }
  50. // swap sets the log writers and returns the old array.
  51. func (l *loggingT) swap(writers [numSeverity]flushSyncWriter) (old [numSeverity]flushSyncWriter) {
  52. l.mu.Lock()
  53. defer l.mu.Unlock()
  54. old = l.file
  55. for i, w := range writers {
  56. logging.file[i] = w
  57. }
  58. return
  59. }
  60. // newBuffers sets the log writers to all new byte buffers and returns the old array.
  61. func (l *loggingT) newBuffers() [numSeverity]flushSyncWriter {
  62. return l.swap([numSeverity]flushSyncWriter{new(flushBuffer), new(flushBuffer), new(flushBuffer), new(flushBuffer)})
  63. }
  64. // contents returns the specified log value as a string.
  65. func contents(s severity) string {
  66. return logging.file[s].(*flushBuffer).String()
  67. }
  68. // contains reports whether the string is contained in the log.
  69. func contains(s severity, str string, t *testing.T) bool {
  70. return strings.Contains(contents(s), str)
  71. }
  72. // setFlags configures the logging flags how the test expects them.
  73. func setFlags() {
  74. logging.toStderr = false
  75. }
  76. func TestOutputSeverity(t *testing.T) {
  77. setFlags()
  78. defer logging.swap(logging.newBuffers())
  79. Debug("test")
  80. if contains(debugLog, "test", t) {
  81. t.Error("OutputSeverity failed")
  82. }
  83. SetLevelString("DEBUG")
  84. Debug("test")
  85. if !contains(debugLog, "test", t) {
  86. t.Error("OutputSeverity failed")
  87. }
  88. }
  89. // Test that Info works as advertised.
  90. func TestInfo(t *testing.T) {
  91. setFlags()
  92. defer logging.swap(logging.newBuffers())
  93. Info("test")
  94. if !contains(infoLog, "I", t) {
  95. t.Errorf("Info has wrong character: %q", contents(infoLog))
  96. }
  97. if !contains(infoLog, "test", t) {
  98. t.Error("Info failed")
  99. }
  100. }
  101. func TestInfoDepth(t *testing.T) {
  102. setFlags()
  103. defer logging.swap(logging.newBuffers())
  104. f := func() { InfoDepth(1, "depth-test1") }
  105. // The next three lines must stay together
  106. _, _, wantLine, _ := runtime.Caller(0)
  107. InfoDepth(0, "depth-test0")
  108. f()
  109. msgs := strings.Split(strings.TrimSuffix(contents(infoLog), "\n"), "\n")
  110. if len(msgs) != 2 {
  111. t.Fatalf("Got %d lines, expected 2", len(msgs))
  112. }
  113. for i, m := range msgs {
  114. if !strings.HasPrefix(m, "I") {
  115. t.Errorf("InfoDepth[%d] has wrong character: %q", i, m)
  116. }
  117. w := fmt.Sprintf("depth-test%d", i)
  118. if !strings.Contains(m, w) {
  119. t.Errorf("InfoDepth[%d] missing %q: %q", i, w, m)
  120. }
  121. // pull out the line number (between : and ])
  122. msg := m[strings.LastIndex(m, ":")+1:]
  123. x := strings.Index(msg, "]")
  124. if x < 0 {
  125. t.Errorf("InfoDepth[%d]: missing ']': %q", i, m)
  126. continue
  127. }
  128. line, err := strconv.Atoi(msg[:x])
  129. if err != nil {
  130. t.Errorf("InfoDepth[%d]: bad line number: %q", i, m)
  131. continue
  132. }
  133. wantLine++
  134. if wantLine != line {
  135. t.Errorf("InfoDepth[%d]: got line %d, want %d", i, line, wantLine)
  136. }
  137. }
  138. }
  139. func init() {
  140. CopyStandardLogTo("INFO")
  141. }
  142. // Test that CopyStandardLogTo panics on bad input.
  143. func TestCopyStandardLogToPanic(t *testing.T) {
  144. defer func() {
  145. if s, ok := recover().(string); !ok || !strings.Contains(s, "LOG") {
  146. t.Errorf(`CopyStandardLogTo("LOG") should have panicked: %v`, s)
  147. }
  148. }()
  149. CopyStandardLogTo("LOG")
  150. }
  151. // Test that using the standard log package logs to INFO.
  152. func TestStandardLog(t *testing.T) {
  153. setFlags()
  154. defer logging.swap(logging.newBuffers())
  155. stdLog.Print("test")
  156. if !contains(infoLog, "I", t) {
  157. t.Errorf("Info has wrong character: %q", contents(infoLog))
  158. }
  159. if !contains(infoLog, "test", t) {
  160. t.Error("Info failed")
  161. }
  162. }
  163. // Test that the header has the correct format.
  164. func TestHeader(t *testing.T) {
  165. setFlags()
  166. defer logging.swap(logging.newBuffers())
  167. defer func(previous func() time.Time) { timeNow = previous }(timeNow)
  168. timeNow = func() time.Time {
  169. return time.Date(2006, 1, 2, 15, 4, 5, .067890e9, time.Local)
  170. }
  171. pid = 1234
  172. Info("test")
  173. var line int
  174. format := "I0102 15:04:05.067890 1234 glog_test.go:%d] test\n"
  175. n, err := fmt.Sscanf(contents(infoLog), format, &line)
  176. if n != 1 || err != nil {
  177. t.Errorf("log format error: %d elements, error %s:\n%s", n, err, contents(infoLog))
  178. }
  179. // Scanf treats multiple spaces as equivalent to a single space,
  180. // so check for correct space-padding also.
  181. want := fmt.Sprintf(format, line)
  182. if contents(infoLog) != want {
  183. t.Errorf("log format error: got:\n\t%q\nwant:\t%q", contents(infoLog), want)
  184. }
  185. }
  186. // Test that an Error log goes to Warning and Info.
  187. // Even in the Info log, the source character will be E, so the data should
  188. // all be identical.
  189. func TestError(t *testing.T) {
  190. setFlags()
  191. defer logging.swap(logging.newBuffers())
  192. Error("test")
  193. if !contains(errorLog, "E", t) {
  194. t.Errorf("Error has wrong character: %q", contents(errorLog))
  195. }
  196. if !contains(errorLog, "test", t) {
  197. t.Error("Error failed")
  198. }
  199. str := contents(errorLog)
  200. if !contains(warningLog, str, t) {
  201. t.Error("Warning failed")
  202. }
  203. if !contains(infoLog, str, t) {
  204. t.Error("Info failed")
  205. }
  206. if !contains(debugLog, str, t) {
  207. t.Error("Debug failed")
  208. }
  209. }
  210. // Test that a Warning log goes to Info.
  211. // Even in the Info log, the source character will be W, so the data should
  212. // all be identical.
  213. func TestWarning(t *testing.T) {
  214. setFlags()
  215. defer logging.swap(logging.newBuffers())
  216. Warning("test")
  217. if !contains(warningLog, "W", t) {
  218. t.Errorf("Warning has wrong character: %q", contents(warningLog))
  219. }
  220. if !contains(warningLog, "test", t) {
  221. t.Error("Warning failed")
  222. }
  223. str := contents(warningLog)
  224. if !contains(infoLog, str, t) {
  225. t.Error("Info failed")
  226. }
  227. }
  228. // Test that a V log goes to Info.
  229. func TestV(t *testing.T) {
  230. setFlags()
  231. defer logging.swap(logging.newBuffers())
  232. logging.verbosity.Set("2")
  233. defer logging.verbosity.Set("0")
  234. V(2).Info("test")
  235. if !contains(infoLog, "I", t) {
  236. t.Errorf("Info has wrong character: %q", contents(infoLog))
  237. }
  238. if !contains(infoLog, "test", t) {
  239. t.Error("Info failed")
  240. }
  241. }
  242. // Test that a vmodule enables a log in this file.
  243. func TestVmoduleOn(t *testing.T) {
  244. setFlags()
  245. defer logging.swap(logging.newBuffers())
  246. logging.vmodule.Set("glog_test=2")
  247. defer logging.vmodule.Set("")
  248. if !V(1) {
  249. t.Error("V not enabled for 1")
  250. }
  251. if !V(2) {
  252. t.Error("V not enabled for 2")
  253. }
  254. if V(3) {
  255. t.Error("V enabled for 3")
  256. }
  257. V(2).Info("test")
  258. if !contains(infoLog, "I", t) {
  259. t.Errorf("Info has wrong character: %q", contents(infoLog))
  260. }
  261. if !contains(infoLog, "test", t) {
  262. t.Error("Info failed")
  263. }
  264. }
  265. // Test that a vmodule of another file does not enable a log in this file.
  266. func TestVmoduleOff(t *testing.T) {
  267. setFlags()
  268. defer logging.swap(logging.newBuffers())
  269. logging.vmodule.Set("notthisfile=2")
  270. defer logging.vmodule.Set("")
  271. for i := 1; i <= 3; i++ {
  272. if V(Level(i)) {
  273. t.Errorf("V enabled for %d", i)
  274. }
  275. }
  276. V(2).Info("test")
  277. if contents(infoLog) != "" {
  278. t.Error("V logged incorrectly")
  279. }
  280. }
  281. // vGlobs are patterns that match/don't match this file at V=2.
  282. var vGlobs = map[string]bool{
  283. // Easy to test the numeric match here.
  284. "glog_test=1": false, // If -vmodule sets V to 1, V(2) will fail.
  285. "glog_test=2": true,
  286. "glog_test=3": true, // If -vmodule sets V to 1, V(3) will succeed.
  287. // These all use 2 and check the patterns. All are true.
  288. "*=2": true,
  289. "?l*=2": true,
  290. "????_*=2": true,
  291. "??[mno]?_*t=2": true,
  292. // These all use 2 and check the patterns. All are false.
  293. "*x=2": false,
  294. "m*=2": false,
  295. "??_*=2": false,
  296. "?[abc]?_*t=2": false,
  297. }
  298. // Test that vmodule globbing works as advertised.
  299. func testVmoduleGlob(pat string, match bool, t *testing.T) {
  300. setFlags()
  301. defer logging.swap(logging.newBuffers())
  302. defer logging.vmodule.Set("")
  303. logging.vmodule.Set(pat)
  304. if V(2) != Verbose(match) {
  305. t.Errorf("incorrect match for %q: got %t expected %t", pat, V(2), match)
  306. }
  307. }
  308. // Test that a vmodule globbing works as advertised.
  309. func TestVmoduleGlob(t *testing.T) {
  310. for glob, match := range vGlobs {
  311. testVmoduleGlob(glob, match, t)
  312. }
  313. }
  314. func TestRollover(t *testing.T) {
  315. setFlags()
  316. var err error
  317. defer func(previous func(error)) { logExitFunc = previous }(logExitFunc)
  318. logExitFunc = func(e error) {
  319. err = e
  320. }
  321. defer func(previous uint64) { MaxSize = previous }(MaxSize)
  322. MaxSize = 512
  323. Info("x") // Be sure we have a file.
  324. info, ok := logging.file[infoLog].(*syncBuffer)
  325. if !ok {
  326. t.Fatal("info wasn't created")
  327. }
  328. if err != nil {
  329. t.Fatalf("info has initial error: %v", err)
  330. }
  331. fname0 := info.file.Name()
  332. Info(strings.Repeat("x", int(MaxSize))) // force a rollover
  333. if err != nil {
  334. t.Fatalf("info has error after big write: %v", err)
  335. }
  336. // Make sure the next log file gets a file name with a different
  337. // time stamp.
  338. //
  339. // TODO: determine whether we need to support subsecond log
  340. // rotation. C++ does not appear to handle this case (nor does it
  341. // handle Daylight Savings Time properly).
  342. time.Sleep(1 * time.Second)
  343. Info("x") // create a new file
  344. if err != nil {
  345. t.Fatalf("error after rotation: %v", err)
  346. }
  347. fname1 := info.file.Name()
  348. if fname0 == fname1 {
  349. t.Errorf("info.f.Name did not change: %v", fname0)
  350. }
  351. if info.nbytes >= MaxSize {
  352. t.Errorf("file size was not reset: %d", info.nbytes)
  353. }
  354. }
  355. func TestLogBacktraceAt(t *testing.T) {
  356. setFlags()
  357. defer logging.swap(logging.newBuffers())
  358. // The peculiar style of this code simplifies line counting and maintenance of the
  359. // tracing block below.
  360. var infoLine string
  361. setTraceLocation := func(file string, line int, ok bool, delta int) {
  362. if !ok {
  363. t.Fatal("could not get file:line")
  364. }
  365. _, file = filepath.Split(file)
  366. infoLine = fmt.Sprintf("%s:%d", file, line+delta)
  367. err := logging.traceLocation.Set(infoLine)
  368. if err != nil {
  369. t.Fatal("error setting log_backtrace_at: ", err)
  370. }
  371. }
  372. {
  373. // Start of tracing block. These lines know about each other's relative position.
  374. _, file, line, ok := runtime.Caller(0)
  375. setTraceLocation(file, line, ok, +2) // Two lines between Caller and Info calls.
  376. Info("we want a stack trace here")
  377. }
  378. numAppearances := strings.Count(contents(infoLog), infoLine)
  379. if numAppearances < 2 {
  380. // Need 2 appearances, one in the log header and one in the trace:
  381. // log_test.go:281: I0511 16:36:06.952398 02238 log_test.go:280] we want a stack trace here
  382. // ...
  383. // github.com/glog/glog_test.go:280 (0x41ba91)
  384. // ...
  385. // We could be more precise but that would require knowing the details
  386. // of the traceback format, which may not be dependable.
  387. t.Fatal("got no trace back; log is ", contents(infoLog))
  388. }
  389. }
  390. func BenchmarkHeader(b *testing.B) {
  391. for i := 0; i < b.N; i++ {
  392. buf, _, _ := logging.header(infoLog, 0)
  393. logging.putBuffer(buf)
  394. }
  395. }