diff options
Diffstat (limited to 'pkg/log/glog.go')
-rw-r--r-- | pkg/log/glog.go | 163 |
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..5732785b4 --- /dev/null +++ b/pkg/log/glog.go @@ -0,0 +1,163 @@ +// Copyright 2018 The gVisor Authors. +// +// 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(b/73383460): 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...) +} |