164 lines
4.0 KiB
Go
164 lines
4.0 KiB
Go
// 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...)
|
|
}
|