blob: 5732785b4ef74fb3c79711ed4acb71cd7f359c76 [file] [log] [blame]
// 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...)
}