| 
									
										
										
										
											2015-04-03 17:09:01 +02:00
										 |  |  | // Go support for leveled logs, analogous to https://code.google.com/p/google-glog/ | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // Copyright 2013 Google Inc. All Rights Reserved. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // Licensed under the Apache License, Version 2.0 (the "License"); | 
					
						
							|  |  |  | // you may not use this file except in compliance with the License. | 
					
						
							|  |  |  | // You may obtain a copy of the License at | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | //     http://www.apache.org/licenses/LICENSE-2.0 | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // Unless required by applicable law or agreed to in writing, software | 
					
						
							|  |  |  | // distributed under the License is distributed on an "AS IS" BASIS, | 
					
						
							|  |  |  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
					
						
							|  |  |  | // See the License for the specific language governing permissions and | 
					
						
							|  |  |  | // limitations under the License. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | package glog | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"bytes" | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							|  |  |  | 	stdLog "log" | 
					
						
							|  |  |  | 	"path/filepath" | 
					
						
							|  |  |  | 	"runtime" | 
					
						
							|  |  |  | 	"strconv" | 
					
						
							|  |  |  | 	"strings" | 
					
						
							|  |  |  | 	"testing" | 
					
						
							|  |  |  | 	"time" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Test that shortHostname works as advertised. | 
					
						
							|  |  |  | func TestShortHostname(t *testing.T) { | 
					
						
							|  |  |  | 	for hostname, expect := range map[string]string{ | 
					
						
							|  |  |  | 		"":                "", | 
					
						
							|  |  |  | 		"host":            "host", | 
					
						
							|  |  |  | 		"host.google.com": "host", | 
					
						
							|  |  |  | 	} { | 
					
						
							|  |  |  | 		if got := shortHostname(hostname); expect != got { | 
					
						
							|  |  |  | 			t.Errorf("shortHostname(%q): expected %q, got %q", hostname, expect, got) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // flushBuffer wraps a bytes.Buffer to satisfy flushSyncWriter. | 
					
						
							|  |  |  | type flushBuffer struct { | 
					
						
							|  |  |  | 	bytes.Buffer | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (f *flushBuffer) Flush() error { | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (f *flushBuffer) Sync() error { | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // swap sets the log writers and returns the old array. | 
					
						
							|  |  |  | func (l *loggingT) swap(writers [numSeverity]flushSyncWriter) (old [numSeverity]flushSyncWriter) { | 
					
						
							|  |  |  | 	l.mu.Lock() | 
					
						
							|  |  |  | 	defer l.mu.Unlock() | 
					
						
							|  |  |  | 	old = l.file | 
					
						
							|  |  |  | 	for i, w := range writers { | 
					
						
							|  |  |  | 		logging.file[i] = w | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // newBuffers sets the log writers to all new byte buffers and returns the old array. | 
					
						
							|  |  |  | func (l *loggingT) newBuffers() [numSeverity]flushSyncWriter { | 
					
						
							|  |  |  | 	return l.swap([numSeverity]flushSyncWriter{new(flushBuffer), new(flushBuffer), new(flushBuffer), new(flushBuffer)}) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // contents returns the specified log value as a string. | 
					
						
							|  |  |  | func contents(s severity) string { | 
					
						
							|  |  |  | 	return logging.file[s].(*flushBuffer).String() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // contains reports whether the string is contained in the log. | 
					
						
							|  |  |  | func contains(s severity, str string, t *testing.T) bool { | 
					
						
							|  |  |  | 	return strings.Contains(contents(s), str) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // setFlags configures the logging flags how the test expects them. | 
					
						
							|  |  |  | func setFlags() { | 
					
						
							|  |  |  | 	logging.toStderr = false | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Test that Info works as advertised. | 
					
						
							|  |  |  | func TestInfo(t *testing.T) { | 
					
						
							|  |  |  | 	setFlags() | 
					
						
							|  |  |  | 	defer logging.swap(logging.newBuffers()) | 
					
						
							|  |  |  | 	Info("test") | 
					
						
							|  |  |  | 	if !contains(infoLog, "I", t) { | 
					
						
							|  |  |  | 		t.Errorf("Info has wrong character: %q", contents(infoLog)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if !contains(infoLog, "test", t) { | 
					
						
							|  |  |  | 		t.Error("Info failed") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func TestInfoDepth(t *testing.T) { | 
					
						
							|  |  |  | 	setFlags() | 
					
						
							|  |  |  | 	defer logging.swap(logging.newBuffers()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	f := func() { InfoDepth(1, "depth-test1") } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// The next three lines must stay together | 
					
						
							|  |  |  | 	_, _, wantLine, _ := runtime.Caller(0) | 
					
						
							|  |  |  | 	InfoDepth(0, "depth-test0") | 
					
						
							|  |  |  | 	f() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	msgs := strings.Split(strings.TrimSuffix(contents(infoLog), "\n"), "\n") | 
					
						
							|  |  |  | 	if len(msgs) != 2 { | 
					
						
							|  |  |  | 		t.Fatalf("Got %d lines, expected 2", len(msgs)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for i, m := range msgs { | 
					
						
							|  |  |  | 		if !strings.HasPrefix(m, "I") { | 
					
						
							|  |  |  | 			t.Errorf("InfoDepth[%d] has wrong character: %q", i, m) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		w := fmt.Sprintf("depth-test%d", i) | 
					
						
							|  |  |  | 		if !strings.Contains(m, w) { | 
					
						
							|  |  |  | 			t.Errorf("InfoDepth[%d] missing %q: %q", i, w, m) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// pull out the line number (between : and ]) | 
					
						
							|  |  |  | 		msg := m[strings.LastIndex(m, ":")+1:] | 
					
						
							|  |  |  | 		x := strings.Index(msg, "]") | 
					
						
							|  |  |  | 		if x < 0 { | 
					
						
							|  |  |  | 			t.Errorf("InfoDepth[%d]: missing ']': %q", i, m) | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		line, err := strconv.Atoi(msg[:x]) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			t.Errorf("InfoDepth[%d]: bad line number: %q", i, m) | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		wantLine++ | 
					
						
							|  |  |  | 		if wantLine != line { | 
					
						
							|  |  |  | 			t.Errorf("InfoDepth[%d]: got line %d, want %d", i, line, wantLine) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func init() { | 
					
						
							|  |  |  | 	CopyStandardLogTo("INFO") | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Test that CopyStandardLogTo panics on bad input. | 
					
						
							|  |  |  | func TestCopyStandardLogToPanic(t *testing.T) { | 
					
						
							|  |  |  | 	defer func() { | 
					
						
							|  |  |  | 		if s, ok := recover().(string); !ok || !strings.Contains(s, "LOG") { | 
					
						
							|  |  |  | 			t.Errorf(`CopyStandardLogTo("LOG") should have panicked: %v`, s) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | 	CopyStandardLogTo("LOG") | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Test that using the standard log package logs to INFO. | 
					
						
							|  |  |  | func TestStandardLog(t *testing.T) { | 
					
						
							|  |  |  | 	setFlags() | 
					
						
							|  |  |  | 	defer logging.swap(logging.newBuffers()) | 
					
						
							|  |  |  | 	stdLog.Print("test") | 
					
						
							|  |  |  | 	if !contains(infoLog, "I", t) { | 
					
						
							|  |  |  | 		t.Errorf("Info has wrong character: %q", contents(infoLog)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if !contains(infoLog, "test", t) { | 
					
						
							|  |  |  | 		t.Error("Info failed") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Test that the header has the correct format. | 
					
						
							|  |  |  | func TestHeader(t *testing.T) { | 
					
						
							|  |  |  | 	setFlags() | 
					
						
							|  |  |  | 	defer logging.swap(logging.newBuffers()) | 
					
						
							|  |  |  | 	defer func(previous func() time.Time) { timeNow = previous }(timeNow) | 
					
						
							|  |  |  | 	timeNow = func() time.Time { | 
					
						
							|  |  |  | 		return time.Date(2006, 1, 2, 15, 4, 5, .067890e9, time.Local) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	pid = 1234 | 
					
						
							|  |  |  | 	Info("test") | 
					
						
							|  |  |  | 	var line int | 
					
						
							| 
									
										
										
										
											2016-01-20 14:13:10 +01:00
										 |  |  | 	format := "I0102 15:04:05.067890 logger/glog/glog_test.go:%d] test\n" | 
					
						
							| 
									
										
										
										
											2015-04-03 17:09:01 +02:00
										 |  |  | 	n, err := fmt.Sscanf(contents(infoLog), format, &line) | 
					
						
							|  |  |  | 	if n != 1 || err != nil { | 
					
						
							|  |  |  | 		t.Errorf("log format error: %d elements, error %s:\n%s", n, err, contents(infoLog)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Scanf treats multiple spaces as equivalent to a single space, | 
					
						
							|  |  |  | 	// so check for correct space-padding also. | 
					
						
							|  |  |  | 	want := fmt.Sprintf(format, line) | 
					
						
							|  |  |  | 	if contents(infoLog) != want { | 
					
						
							|  |  |  | 		t.Errorf("log format error: got:\n\t%q\nwant:\t%q", contents(infoLog), want) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Test that an Error log goes to Warning and Info. | 
					
						
							|  |  |  | // Even in the Info log, the source character will be E, so the data should | 
					
						
							|  |  |  | // all be identical. | 
					
						
							|  |  |  | func TestError(t *testing.T) { | 
					
						
							|  |  |  | 	setFlags() | 
					
						
							|  |  |  | 	defer logging.swap(logging.newBuffers()) | 
					
						
							|  |  |  | 	Error("test") | 
					
						
							|  |  |  | 	if !contains(errorLog, "E", t) { | 
					
						
							|  |  |  | 		t.Errorf("Error has wrong character: %q", contents(errorLog)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if !contains(errorLog, "test", t) { | 
					
						
							|  |  |  | 		t.Error("Error failed") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	str := contents(errorLog) | 
					
						
							|  |  |  | 	if !contains(warningLog, str, t) { | 
					
						
							|  |  |  | 		t.Error("Warning failed") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if !contains(infoLog, str, t) { | 
					
						
							|  |  |  | 		t.Error("Info failed") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Test that a Warning log goes to Info. | 
					
						
							|  |  |  | // Even in the Info log, the source character will be W, so the data should | 
					
						
							|  |  |  | // all be identical. | 
					
						
							|  |  |  | func TestWarning(t *testing.T) { | 
					
						
							|  |  |  | 	setFlags() | 
					
						
							|  |  |  | 	defer logging.swap(logging.newBuffers()) | 
					
						
							|  |  |  | 	Warning("test") | 
					
						
							|  |  |  | 	if !contains(warningLog, "W", t) { | 
					
						
							|  |  |  | 		t.Errorf("Warning has wrong character: %q", contents(warningLog)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if !contains(warningLog, "test", t) { | 
					
						
							|  |  |  | 		t.Error("Warning failed") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	str := contents(warningLog) | 
					
						
							|  |  |  | 	if !contains(infoLog, str, t) { | 
					
						
							|  |  |  | 		t.Error("Info failed") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Test that a V log goes to Info. | 
					
						
							|  |  |  | func TestV(t *testing.T) { | 
					
						
							|  |  |  | 	setFlags() | 
					
						
							|  |  |  | 	defer logging.swap(logging.newBuffers()) | 
					
						
							|  |  |  | 	logging.verbosity.Set("2") | 
					
						
							|  |  |  | 	defer logging.verbosity.Set("0") | 
					
						
							|  |  |  | 	V(2).Info("test") | 
					
						
							|  |  |  | 	if !contains(infoLog, "I", t) { | 
					
						
							|  |  |  | 		t.Errorf("Info has wrong character: %q", contents(infoLog)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if !contains(infoLog, "test", t) { | 
					
						
							|  |  |  | 		t.Error("Info failed") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Test that a vmodule enables a log in this file. | 
					
						
							|  |  |  | func TestVmoduleOn(t *testing.T) { | 
					
						
							|  |  |  | 	setFlags() | 
					
						
							|  |  |  | 	defer logging.swap(logging.newBuffers()) | 
					
						
							| 
									
										
										
										
											2016-01-20 14:13:10 +01:00
										 |  |  | 	logging.vmodule.Set("glog_test.go=2") | 
					
						
							| 
									
										
										
										
											2015-04-03 17:09:01 +02:00
										 |  |  | 	defer logging.vmodule.Set("") | 
					
						
							|  |  |  | 	if !V(1) { | 
					
						
							|  |  |  | 		t.Error("V not enabled for 1") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if !V(2) { | 
					
						
							|  |  |  | 		t.Error("V not enabled for 2") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if V(3) { | 
					
						
							|  |  |  | 		t.Error("V enabled for 3") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	V(2).Info("test") | 
					
						
							|  |  |  | 	if !contains(infoLog, "I", t) { | 
					
						
							|  |  |  | 		t.Errorf("Info has wrong character: %q", contents(infoLog)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if !contains(infoLog, "test", t) { | 
					
						
							|  |  |  | 		t.Error("Info failed") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Test that a vmodule of another file does not enable a log in this file. | 
					
						
							|  |  |  | func TestVmoduleOff(t *testing.T) { | 
					
						
							|  |  |  | 	setFlags() | 
					
						
							|  |  |  | 	defer logging.swap(logging.newBuffers()) | 
					
						
							|  |  |  | 	logging.vmodule.Set("notthisfile=2") | 
					
						
							|  |  |  | 	defer logging.vmodule.Set("") | 
					
						
							|  |  |  | 	for i := 1; i <= 3; i++ { | 
					
						
							|  |  |  | 		if V(Level(i)) { | 
					
						
							|  |  |  | 			t.Errorf("V enabled for %d", i) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	V(2).Info("test") | 
					
						
							|  |  |  | 	if contents(infoLog) != "" { | 
					
						
							|  |  |  | 		t.Error("V logged incorrectly") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-20 14:13:10 +01:00
										 |  |  | var patternTests = []struct{ input, want string }{ | 
					
						
							|  |  |  | 	{"foo/bar/x.go", ".*/foo/bar/x\\.go$"}, | 
					
						
							|  |  |  | 	{"foo/*/x.go", ".*/foo(/.*)?/x\\.go$"}, | 
					
						
							|  |  |  | 	{"foo/*", ".*/foo(/.*)?/[^/]+\\.go$"}, | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func TestCompileModulePattern(t *testing.T) { | 
					
						
							|  |  |  | 	for _, test := range patternTests { | 
					
						
							|  |  |  | 		re, err := compileModulePattern(test.input) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2016-04-15 10:58:41 +02:00
										 |  |  | 			t.Fatalf("%s: %v", test.input, err) | 
					
						
							| 
									
										
										
										
											2016-01-20 14:13:10 +01:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		if re.String() != test.want { | 
					
						
							|  |  |  | 			t.Errorf("mismatch for %q: got %q, want %q", test.input, re.String(), test.want) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-03 17:09:01 +02:00
										 |  |  | // vGlobs are patterns that match/don't match this file at V=2. | 
					
						
							|  |  |  | var vGlobs = map[string]bool{ | 
					
						
							|  |  |  | 	// Easy to test the numeric match here. | 
					
						
							| 
									
										
										
										
											2016-01-20 14:13:10 +01:00
										 |  |  | 	"glog_test.go=1": false, // If -vmodule sets V to 1, V(2) will fail. | 
					
						
							|  |  |  | 	"glog_test.go=2": true, | 
					
						
							|  |  |  | 	"glog_test.go=3": true, // If -vmodule sets V to 1, V(3) will succeed. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Import path prefix matching | 
					
						
							|  |  |  | 	"logger/glog=1": false, | 
					
						
							|  |  |  | 	"logger/glog=2": true, | 
					
						
							|  |  |  | 	"logger/glog=3": true, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Import path glob matching | 
					
						
							|  |  |  | 	"logger/*=1": false, | 
					
						
							|  |  |  | 	"logger/*=2": true, | 
					
						
							|  |  |  | 	"logger/*=3": true, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// These all use 2 and check the patterns. | 
					
						
							|  |  |  | 	"*=2": true, | 
					
						
							| 
									
										
										
										
											2015-04-03 17:09:01 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Test that vmodule globbing works as advertised. | 
					
						
							|  |  |  | func testVmoduleGlob(pat string, match bool, t *testing.T) { | 
					
						
							|  |  |  | 	setFlags() | 
					
						
							|  |  |  | 	defer logging.swap(logging.newBuffers()) | 
					
						
							|  |  |  | 	defer logging.vmodule.Set("") | 
					
						
							|  |  |  | 	logging.vmodule.Set(pat) | 
					
						
							|  |  |  | 	if V(2) != Verbose(match) { | 
					
						
							|  |  |  | 		t.Errorf("incorrect match for %q: got %t expected %t", pat, V(2), match) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Test that a vmodule globbing works as advertised. | 
					
						
							|  |  |  | func TestVmoduleGlob(t *testing.T) { | 
					
						
							|  |  |  | 	for glob, match := range vGlobs { | 
					
						
							|  |  |  | 		testVmoduleGlob(glob, match, t) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func TestRollover(t *testing.T) { | 
					
						
							|  |  |  | 	setFlags() | 
					
						
							|  |  |  | 	var err error | 
					
						
							|  |  |  | 	defer func(previous func(error)) { logExitFunc = previous }(logExitFunc) | 
					
						
							|  |  |  | 	logExitFunc = func(e error) { | 
					
						
							|  |  |  | 		err = e | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	defer func(previous uint64) { MaxSize = previous }(MaxSize) | 
					
						
							|  |  |  | 	MaxSize = 512 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	Info("x") // Be sure we have a file. | 
					
						
							|  |  |  | 	info, ok := logging.file[infoLog].(*syncBuffer) | 
					
						
							|  |  |  | 	if !ok { | 
					
						
							|  |  |  | 		t.Fatal("info wasn't created") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Fatalf("info has initial error: %v", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	fname0 := info.file.Name() | 
					
						
							|  |  |  | 	Info(strings.Repeat("x", int(MaxSize))) // force a rollover | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Fatalf("info has error after big write: %v", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Make sure the next log file gets a file name with a different | 
					
						
							|  |  |  | 	// time stamp. | 
					
						
							|  |  |  | 	// | 
					
						
							|  |  |  | 	// TODO: determine whether we need to support subsecond log | 
					
						
							|  |  |  | 	// rotation.  C++ does not appear to handle this case (nor does it | 
					
						
							|  |  |  | 	// handle Daylight Savings Time properly). | 
					
						
							|  |  |  | 	time.Sleep(1 * time.Second) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	Info("x") // create a new file | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Fatalf("error after rotation: %v", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	fname1 := info.file.Name() | 
					
						
							|  |  |  | 	if fname0 == fname1 { | 
					
						
							|  |  |  | 		t.Errorf("info.f.Name did not change: %v", fname0) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if info.nbytes >= MaxSize { | 
					
						
							|  |  |  | 		t.Errorf("file size was not reset: %d", info.nbytes) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func TestLogBacktraceAt(t *testing.T) { | 
					
						
							|  |  |  | 	setFlags() | 
					
						
							|  |  |  | 	defer logging.swap(logging.newBuffers()) | 
					
						
							|  |  |  | 	// The peculiar style of this code simplifies line counting and maintenance of the | 
					
						
							|  |  |  | 	// tracing block below. | 
					
						
							|  |  |  | 	var infoLine string | 
					
						
							|  |  |  | 	setTraceLocation := func(file string, line int, ok bool, delta int) { | 
					
						
							|  |  |  | 		if !ok { | 
					
						
							|  |  |  | 			t.Fatal("could not get file:line") | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		_, file = filepath.Split(file) | 
					
						
							|  |  |  | 		infoLine = fmt.Sprintf("%s:%d", file, line+delta) | 
					
						
							|  |  |  | 		err := logging.traceLocation.Set(infoLine) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			t.Fatal("error setting log_backtrace_at: ", err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		// Start of tracing block. These lines know about each other's relative position. | 
					
						
							|  |  |  | 		_, file, line, ok := runtime.Caller(0) | 
					
						
							|  |  |  | 		setTraceLocation(file, line, ok, +2) // Two lines between Caller and Info calls. | 
					
						
							|  |  |  | 		Info("we want a stack trace here") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	numAppearances := strings.Count(contents(infoLog), infoLine) | 
					
						
							|  |  |  | 	if numAppearances < 2 { | 
					
						
							|  |  |  | 		// Need 2 appearances, one in the log header and one in the trace: | 
					
						
							|  |  |  | 		//   log_test.go:281: I0511 16:36:06.952398 02238 log_test.go:280] we want a stack trace here | 
					
						
							|  |  |  | 		//   ... | 
					
						
							|  |  |  | 		//   github.com/glog/glog_test.go:280 (0x41ba91) | 
					
						
							|  |  |  | 		//   ... | 
					
						
							|  |  |  | 		// We could be more precise but that would require knowing the details | 
					
						
							|  |  |  | 		// of the traceback format, which may not be dependable. | 
					
						
							|  |  |  | 		t.Fatal("got no trace back; log is ", contents(infoLog)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func BenchmarkHeader(b *testing.B) { | 
					
						
							|  |  |  | 	for i := 0; i < b.N; i++ { | 
					
						
							|  |  |  | 		buf, _, _ := logging.header(infoLog, 0) | 
					
						
							|  |  |  | 		logging.putBuffer(buf) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } |