summaryrefslogtreecommitdiffhomepage
path: root/pkg/log/glog.go
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/log/glog.go')
-rw-r--r--pkg/log/glog.go163
1 files changed, 163 insertions, 0 deletions
diff --git a/pkg/log/glog.go b/pkg/log/glog.go
new file mode 100644
index 000000000..58b4052e6
--- /dev/null
+++ b/pkg/log/glog.go
@@ -0,0 +1,163 @@
+// Copyright 2018 Google Inc.
+//
+// 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 log
+
+import (
+ "os"
+ "time"
+)
+
+// GoogleEmitter is a wrapper that emits logs in a format compatible with
+// package github.com/golang/glog.
+type GoogleEmitter struct {
+ // Emitter is the underlying emitter.
+ Emitter
+}
+
+// buffer is a simple inline buffer to avoid churn. The data slice is generally
+// kept to the local byte array, and we avoid having to allocate it on the heap.
+type buffer struct {
+ local [256]byte
+ data []byte
+}
+
+func (b *buffer) start() {
+ b.data = b.local[:0]
+}
+
+func (b *buffer) String() string {
+ return unsafeString(b.data)
+}
+
+func (b *buffer) write(c byte) {
+ b.data = append(b.data, c)
+}
+
+func (b *buffer) writeAll(d []byte) {
+ b.data = append(b.data, d...)
+}
+
+func (b *buffer) writeOneDigit(d byte) {
+ b.write('0' + d)
+}
+
+func (b *buffer) writeTwoDigits(v int) {
+ v = v % 100
+ b.writeOneDigit(byte(v / 10))
+ b.writeOneDigit(byte(v % 10))
+}
+
+func (b *buffer) writeSixDigits(v int) {
+ v = v % 1000000
+ b.writeOneDigit(byte(v / 100000))
+ b.writeOneDigit(byte((v % 100000) / 10000))
+ b.writeOneDigit(byte((v % 10000) / 1000))
+ b.writeOneDigit(byte((v % 1000) / 100))
+ b.writeOneDigit(byte((v % 100) / 10))
+ b.writeOneDigit(byte(v % 10))
+}
+
+func calculateBytes(v int, pad int) []byte {
+ var d []byte
+ r := 1
+
+ for n := 10; v >= r; n = n * 10 {
+ d = append(d, '0'+byte((v%n)/r))
+ r = n
+ }
+
+ for i := len(d); i < pad; i++ {
+ d = append(d, ' ')
+ }
+
+ for i := 0; i < len(d)/2; i++ {
+ d[i], d[len(d)-(i+1)] = d[len(d)-(i+1)], d[i]
+ }
+ return d
+}
+
+// pid is used for the threadid component of the header.
+//
+// The glog package logger uses 7 spaces of padding. See
+// glob.loggingT.formatHeader.
+var pid = calculateBytes(os.Getpid(), 7)
+
+// caller is faked out as the caller. See FIXME below.
+var caller = []byte("x:0")
+
+// Emit emits the message, google-style.
+func (g GoogleEmitter) Emit(level Level, timestamp time.Time, format string, args ...interface{}) {
+ var b buffer
+ b.start()
+
+ // Log lines have this form:
+ // Lmmdd hh:mm:ss.uuuuuu threadid file:line] msg...
+ //
+ // where the fields are defined as follows:
+ // L A single character, representing the log level (eg 'I' for INFO)
+ // mm The month (zero padded; ie May is '05')
+ // dd The day (zero padded)
+ // hh:mm:ss.uuuuuu Time in hours, minutes and fractional seconds
+ // threadid The space-padded thread ID as returned by GetTID()
+ // file The file name
+ // line The line number
+ // msg The user-supplied message
+
+ // Log level.
+ switch level {
+ case Debug:
+ b.write('D')
+ case Info:
+ b.write('I')
+ case Warning:
+ b.write('W')
+ }
+
+ // Timestamp.
+ _, month, day := timestamp.Date()
+ hour, minute, second := timestamp.Clock()
+ b.writeTwoDigits(int(month))
+ b.writeTwoDigits(int(day))
+ b.write(' ')
+ b.writeTwoDigits(int(hour))
+ b.write(':')
+ b.writeTwoDigits(int(minute))
+ b.write(':')
+ b.writeTwoDigits(int(second))
+ b.write('.')
+ b.writeSixDigits(int(timestamp.Nanosecond() / 1000))
+ b.write(' ')
+
+ // The pid.
+ b.writeAll(pid)
+ b.write(' ')
+
+ // FIXME: The caller, fabricated. This really sucks, but it
+ // is unacceptable to put runtime.Callers() in the hot path.
+ b.writeAll(caller)
+ b.write(']')
+ b.write(' ')
+
+ // User-provided format string, copied.
+ for i := 0; i < len(format); i++ {
+ b.write(format[i])
+ }
+
+ // End with a newline.
+ b.write('\n')
+
+ // Pass to the underlying routine.
+ g.Emitter.Emit(level, timestamp, b.String(), args...)
+}