glog.go raw

   1  // Copyright 2018 The gVisor Authors.
   2  //
   3  // Licensed under the Apache License, Version 2.0 (the "License");
   4  // you may not use this file except in compliance with the License.
   5  // You may obtain a copy of the License at
   6  //
   7  //     http://www.apache.org/licenses/LICENSE-2.0
   8  //
   9  // Unless required by applicable law or agreed to in writing, software
  10  // distributed under the License is distributed on an "AS IS" BASIS,
  11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12  // See the License for the specific language governing permissions and
  13  // limitations under the License.
  14  
  15  package log
  16  
  17  import (
  18  	"fmt"
  19  	"os"
  20  	"runtime"
  21  	"strings"
  22  	"time"
  23  )
  24  
  25  // GoogleEmitter is a wrapper that emits logs in a format compatible with
  26  // package github.com/golang/glog.
  27  type GoogleEmitter struct {
  28  	*Writer
  29  }
  30  
  31  // pid is used for the threadid component of the header.
  32  var pid = os.Getpid()
  33  
  34  // Emit emits the message, google-style.
  35  //
  36  // Log lines have this form:
  37  //
  38  //	Lmmdd hh:mm:ss.uuuuuu threadid file:line] msg...
  39  //
  40  // where the fields are defined as follows:
  41  //
  42  //	L                A single character, representing the log level (eg 'I' for INFO)
  43  //	mm               The month (zero padded; ie May is '05')
  44  //	dd               The day (zero padded)
  45  //	hh:mm:ss.uuuuuu  Time in hours, minutes and fractional seconds
  46  //	threadid         The space-padded thread ID as returned by GetTID()
  47  //	file             The file name
  48  //	line             The line number
  49  //	msg              The user-supplied message
  50  func (g GoogleEmitter) Emit(depth int, level Level, timestamp time.Time, format string, args ...any) {
  51  	// Log level.
  52  	prefix := byte('?')
  53  	switch level {
  54  	case Debug:
  55  		prefix = byte('D')
  56  	case Info:
  57  		prefix = byte('I')
  58  	case Warning:
  59  		prefix = byte('W')
  60  	}
  61  
  62  	// Timestamp.
  63  	_, month, day := timestamp.Date()
  64  	hour, minute, second := timestamp.Clock()
  65  	microsecond := int(timestamp.Nanosecond() / 1000)
  66  
  67  	// 0 = this frame.
  68  	_, file, line, ok := runtime.Caller(depth + 1)
  69  	if ok {
  70  		// Trim any directory path from the file.
  71  		slash := strings.LastIndexByte(file, byte('/'))
  72  		if slash >= 0 {
  73  			file = file[slash+1:]
  74  		}
  75  	} else {
  76  		// We don't have a filename.
  77  		file = "???"
  78  		line = 0
  79  	}
  80  
  81  	// Generate the message.
  82  	message := fmt.Sprintf(format, args...)
  83  
  84  	// Emit the formatted result.
  85  	fmt.Fprintf(g.Writer, "%c%02d%02d %02d:%02d:%02d.%06d % 7d %s:%d] %s\n", prefix, int(month), day, hour, minute, second, microsecond, pid, file, line, message)
  86  }
  87