Skip to content

Commit 49850ea

Browse files
committed
Redirect messages written to the standard "log" package to the INFO log,
in particular to capture messages from the "net/http" package. This change only affects programs that import "github.com/golang/glog". Messages written to the standard "log" package will always appear on standard error, as they do now, though they will be formatted like INFO log messages. These messages will also appear in the INFO log file unless --logtostderr is set.
1 parent d1c4472 commit 49850ea

File tree

2 files changed

+113
-16
lines changed

2 files changed

+113
-16
lines changed

glog.go

Lines changed: 83 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ import (
7777
"flag"
7878
"fmt"
7979
"io"
80+
stdLog "log"
8081
"os"
8182
"path/filepath"
8283
"runtime"
@@ -93,6 +94,9 @@ import (
9394
// the corresponding constants in C++.
9495
type severity int32 // sync/atomic int32
9596

97+
// These constants identify the log levels in order of increasing severity.
98+
// A message written to a high-severity log file is also written to each
99+
// lower-severity log file.
96100
const (
97101
infoLog severity = iota
98102
warningLog
@@ -311,7 +315,7 @@ func (m *moduleSpec) Set(value string) error {
311315
// isLiteral reports whether the pattern is a literal string, that is, has no metacharacters
312316
// that require filepath.Match to be called to match the pattern.
313317
func isLiteral(pattern string) bool {
314-
return !strings.ContainsAny(pattern, `*?[]\`)
318+
return !strings.ContainsAny(pattern, `\*?[]`)
315319
}
316320

317321
// traceLocation represents the setting of the -log_backtrace_at flag.
@@ -513,7 +517,7 @@ var timeNow = time.Now // Stubbed out for testing.
513517

514518
/*
515519
header formats a log header as defined by the C++ implementation.
516-
It returns a buffer containing the formatted header.
520+
It returns a buffer containing the formatted header and the user's file and line number.
517521
518522
Log lines have this form:
519523
Lmmdd hh:mm:ss.uuuuuu threadid file:line] msg...
@@ -527,9 +531,7 @@ where the fields are defined as follows:
527531
line The line number
528532
msg The user-supplied message
529533
*/
530-
func (l *loggingT) header(s severity) *buffer {
531-
// Lmmdd hh:mm:ss.uuuuuu threadid file:line]
532-
now := timeNow()
534+
func (l *loggingT) header(s severity) (*buffer, string, int) {
533535
_, file, line, ok := runtime.Caller(3) // It's always the same number of frames to the user's call.
534536
if !ok {
535537
file = "???"
@@ -540,6 +542,12 @@ func (l *loggingT) header(s severity) *buffer {
540542
file = file[slash+1:]
541543
}
542544
}
545+
return l.formatHeader(s, file, line), file, line
546+
}
547+
548+
// formatHeader formats a log header using the provided file name and line number.
549+
func (l *loggingT) formatHeader(s severity, file string, line int) *buffer {
550+
now := timeNow()
543551
if line < 0 {
544552
line = 0 // not a real line number, but acceptable to someDigits
545553
}
@@ -552,6 +560,7 @@ func (l *loggingT) header(s severity) *buffer {
552560
// It's worth about 3X. Fprintf is hard.
553561
_, month, day := now.Date()
554562
hour, minute, second := now.Clock()
563+
// Lmmdd hh:mm:ss.uuuuuu threadid file:line]
555564
buf.tmp[0] = severityChar[s]
556565
buf.twoDigits(1, int(month))
557566
buf.twoDigits(3, day)
@@ -612,43 +621,54 @@ func (buf *buffer) someDigits(i, d int) int {
612621
}
613622

614623
func (l *loggingT) println(s severity, args ...interface{}) {
615-
buf := l.header(s)
624+
buf, file, line := l.header(s)
616625
fmt.Fprintln(buf, args...)
617-
l.output(s, buf)
626+
l.output(s, buf, file, line, false)
618627
}
619628

620629
func (l *loggingT) print(s severity, args ...interface{}) {
621-
buf := l.header(s)
630+
buf, file, line := l.header(s)
622631
fmt.Fprint(buf, args...)
623632
if buf.Bytes()[buf.Len()-1] != '\n' {
624633
buf.WriteByte('\n')
625634
}
626-
l.output(s, buf)
635+
l.output(s, buf, file, line, false)
627636
}
628637

629638
func (l *loggingT) printf(s severity, format string, args ...interface{}) {
630-
buf := l.header(s)
639+
buf, file, line := l.header(s)
631640
fmt.Fprintf(buf, format, args...)
632641
if buf.Bytes()[buf.Len()-1] != '\n' {
633642
buf.WriteByte('\n')
634643
}
635-
l.output(s, buf)
644+
l.output(s, buf, file, line, false)
645+
}
646+
647+
// printWithFileLine behaves like print but uses the provided file and line number. If
648+
// alsoLogToStderr is true, the log message always appears on standard error; it
649+
// will also appear in the log file unless --logtostderr is set.
650+
func (l *loggingT) printWithFileLine(s severity, file string, line int, alsoToStderr bool, args ...interface{}) {
651+
buf := l.formatHeader(s, file, line)
652+
fmt.Fprint(buf, args...)
653+
if buf.Bytes()[buf.Len()-1] != '\n' {
654+
buf.WriteByte('\n')
655+
}
656+
l.output(s, buf, file, line, alsoToStderr)
636657
}
637658

638659
// output writes the data to the log files and releases the buffer.
639-
func (l *loggingT) output(s severity, buf *buffer) {
660+
func (l *loggingT) output(s severity, buf *buffer, file string, line int, alsoToStderr bool) {
640661
l.mu.Lock()
641662
if l.traceLocation.isSet() {
642-
_, file, line, ok := runtime.Caller(3) // It's always the same number of frames to the user's call (same as header).
643-
if ok && l.traceLocation.match(file, line) {
663+
if l.traceLocation.match(file, line) {
644664
buf.Write(stacks(false))
645665
}
646666
}
647667
data := buf.Bytes()
648668
if l.toStderr {
649669
os.Stderr.Write(data)
650670
} else {
651-
if l.alsoToStderr || s >= l.stderrThreshold.get() {
671+
if alsoToStderr || l.alsoToStderr || s >= l.stderrThreshold.get() {
652672
os.Stderr.Write(data)
653673
}
654674
if l.file[s] == nil {
@@ -861,6 +881,54 @@ func (l *loggingT) flushAll() {
861881
}
862882
}
863883

884+
// CopyStandardLogTo arranges for messages written to the Go "log" package's
885+
// default logs to also appear in the Google logs for the named and lower
886+
// severities. Subsequent changes to the standard log's default output location
887+
// or format may break this behavior.
888+
//
889+
// Valid names are "INFO", "WARNING", "ERROR", and "FATAL". If the name is not
890+
// recognized, CopyStandardLogTo panics.
891+
func CopyStandardLogTo(name string) {
892+
sev, ok := severityByName(name)
893+
if !ok {
894+
panic(fmt.Sprintf("log.CopyStandardLogTo(%q): unrecognized severity name", name))
895+
}
896+
// Set a log format that captures the user's file and line:
897+
// d.go:23: message
898+
stdLog.SetFlags(stdLog.Lshortfile)
899+
stdLog.SetOutput(logBridge(sev))
900+
}
901+
902+
// logBridge provides the Write method that enables CopyStandardLogTo to connect
903+
// Go's standard logs to the logs provided by this package.
904+
type logBridge severity
905+
906+
// Write parses the standard logging line and passes its components to the
907+
// logger for severity(lb).
908+
func (lb logBridge) Write(b []byte) (n int, err error) {
909+
var (
910+
file = "???"
911+
line = 1
912+
text string
913+
)
914+
// Split "d.go:23: message" into "d.go", "23", and "message".
915+
if parts := bytes.SplitN(b, []byte{':'}, 3); len(parts) != 3 || len(parts[0]) < 1 || len(parts[2]) < 1 {
916+
text = fmt.Sprintf("bad log format: %s", b)
917+
} else {
918+
file = string(parts[0])
919+
text = string(parts[2][1:]) // skip leading space
920+
line, err = strconv.Atoi(string(parts[1]))
921+
if err != nil {
922+
text = fmt.Sprintf("bad line number: %s", b)
923+
line = 1
924+
}
925+
}
926+
// printWithFileLine with alsoToStderr=true, so standard log messages
927+
// always appear on standard error.
928+
logging.printWithFileLine(severity(lb), file, line, true, text)
929+
return len(b), nil
930+
}
931+
864932
// setV computes and remembers the V level for a given PC
865933
// when vmodule is enabled.
866934
// File pattern matching takes the basename of the file, stripped

glog_test.go

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package glog
1919
import (
2020
"bytes"
2121
"fmt"
22+
stdLog "log"
2223
"path/filepath"
2324
"runtime"
2425
"strings"
@@ -96,6 +97,33 @@ func TestInfo(t *testing.T) {
9697
}
9798
}
9899

100+
func init() {
101+
CopyStandardLogTo("INFO")
102+
}
103+
104+
// Test that CopyStandardLogTo panics on bad input.
105+
func TestCopyStandardLogToPanic(t *testing.T) {
106+
defer func() {
107+
if s, ok := recover().(string); !ok || !strings.Contains(s, "LOG") {
108+
t.Errorf(`CopyStandardLogTo("LOG") should have panicked: %v`, s)
109+
}
110+
}()
111+
CopyStandardLogTo("LOG")
112+
}
113+
114+
// Test that using the standard log package logs to INFO.
115+
func TestStandardLog(t *testing.T) {
116+
setFlags()
117+
defer logging.swap(logging.newBuffers())
118+
stdLog.Print("test")
119+
if !contains(infoLog, "I", t) {
120+
t.Errorf("Info has wrong character: %q", contents(infoLog))
121+
}
122+
if !contains(infoLog, "test", t) {
123+
t.Error("Info failed")
124+
}
125+
}
126+
99127
// Test that the header has the correct format.
100128
func TestHeader(t *testing.T) {
101129
setFlags()
@@ -328,6 +356,7 @@ func TestLogBacktraceAt(t *testing.T) {
328356

329357
func BenchmarkHeader(b *testing.B) {
330358
for i := 0; i < b.N; i++ {
331-
logging.putBuffer(logging.header(infoLog))
359+
buf, _, _ := logging.header(infoLog)
360+
logging.putBuffer(buf)
332361
}
333362
}

0 commit comments

Comments
 (0)