...

Source file src/k8s.io/klog/v2/internal/buffer/buffer.go

Documentation: k8s.io/klog/v2/internal/buffer

     1  // Copyright 2013 Google Inc. All Rights Reserved.
     2  // Copyright 2022 The Kubernetes Authors.
     3  //
     4  // Licensed under the Apache License, Version 2.0 (the "License");
     5  // you may not use this file except in compliance with the License.
     6  // You may obtain a copy of the License at
     7  //
     8  //     http://www.apache.org/licenses/LICENSE-2.0
     9  //
    10  // Unless required by applicable law or agreed to in writing, software
    11  // distributed under the License is distributed on an "AS IS" BASIS,
    12  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  // See the License for the specific language governing permissions and
    14  // limitations under the License.
    15  
    16  // Package buffer provides a cache for byte.Buffer instances that can be reused
    17  // to avoid frequent allocation and deallocation. It also has utility code
    18  // for log header formatting that use these buffers.
    19  package buffer
    20  
    21  import (
    22  	"bytes"
    23  	"os"
    24  	"sync"
    25  	"time"
    26  
    27  	"k8s.io/klog/v2/internal/severity"
    28  )
    29  
    30  var (
    31  	// Pid is inserted into log headers. Can be overridden for tests.
    32  	Pid = os.Getpid()
    33  
    34  	// Time, if set, will be used instead of the actual current time.
    35  	Time *time.Time
    36  )
    37  
    38  // Buffer holds a single byte.Buffer for reuse. The zero value is ready for
    39  // use. It also provides some helper methods for output formatting.
    40  type Buffer struct {
    41  	bytes.Buffer
    42  	Tmp [64]byte // temporary byte array for creating headers.
    43  }
    44  
    45  var buffers = sync.Pool{
    46  	New: func() interface{} {
    47  		return new(Buffer)
    48  	},
    49  }
    50  
    51  // GetBuffer returns a new, ready-to-use buffer.
    52  func GetBuffer() *Buffer {
    53  	b := buffers.Get().(*Buffer)
    54  	b.Reset()
    55  	return b
    56  }
    57  
    58  // PutBuffer returns a buffer to the free list.
    59  func PutBuffer(b *Buffer) {
    60  	if b.Len() >= 256 {
    61  		// Let big buffers die a natural death, without relying on
    62  		// sync.Pool behavior. The documentation implies that items may
    63  		// get deallocated while stored there ("If the Pool holds the
    64  		// only reference when this [= be removed automatically]
    65  		// happens, the item might be deallocated."), but
    66  		// https://github.com/golang/go/issues/23199 leans more towards
    67  		// having such a size limit.
    68  		return
    69  	}
    70  
    71  	buffers.Put(b)
    72  }
    73  
    74  // Some custom tiny helper functions to print the log header efficiently.
    75  
    76  const digits = "0123456789"
    77  
    78  // twoDigits formats a zero-prefixed two-digit integer at buf.Tmp[i].
    79  func (buf *Buffer) twoDigits(i, d int) {
    80  	buf.Tmp[i+1] = digits[d%10]
    81  	d /= 10
    82  	buf.Tmp[i] = digits[d%10]
    83  }
    84  
    85  // nDigits formats an n-digit integer at buf.Tmp[i],
    86  // padding with pad on the left.
    87  // It assumes d >= 0.
    88  func (buf *Buffer) nDigits(n, i, d int, pad byte) {
    89  	j := n - 1
    90  	for ; j >= 0 && d > 0; j-- {
    91  		buf.Tmp[i+j] = digits[d%10]
    92  		d /= 10
    93  	}
    94  	for ; j >= 0; j-- {
    95  		buf.Tmp[i+j] = pad
    96  	}
    97  }
    98  
    99  // someDigits formats a zero-prefixed variable-width integer at buf.Tmp[i].
   100  func (buf *Buffer) someDigits(i, d int) int {
   101  	// Print into the top, then copy down. We know there's space for at least
   102  	// a 10-digit number.
   103  	j := len(buf.Tmp)
   104  	for {
   105  		j--
   106  		buf.Tmp[j] = digits[d%10]
   107  		d /= 10
   108  		if d == 0 {
   109  			break
   110  		}
   111  	}
   112  	return copy(buf.Tmp[i:], buf.Tmp[j:])
   113  }
   114  
   115  // FormatHeader formats a log header using the provided file name and line number
   116  // and writes it into the buffer.
   117  func (buf *Buffer) FormatHeader(s severity.Severity, file string, line int, now time.Time) {
   118  	if line < 0 {
   119  		line = 0 // not a real line number, but acceptable to someDigits
   120  	}
   121  	if s > severity.FatalLog {
   122  		s = severity.InfoLog // for safety.
   123  	}
   124  
   125  	// Avoid Fprintf, for speed. The format is so simple that we can do it quickly by hand.
   126  	// It's worth about 3X. Fprintf is hard.
   127  	if Time != nil {
   128  		now = *Time
   129  	}
   130  	_, month, day := now.Date()
   131  	hour, minute, second := now.Clock()
   132  	// Lmmdd hh:mm:ss.uuuuuu threadid file:line]
   133  	buf.Tmp[0] = severity.Char[s]
   134  	buf.twoDigits(1, int(month))
   135  	buf.twoDigits(3, day)
   136  	buf.Tmp[5] = ' '
   137  	buf.twoDigits(6, hour)
   138  	buf.Tmp[8] = ':'
   139  	buf.twoDigits(9, minute)
   140  	buf.Tmp[11] = ':'
   141  	buf.twoDigits(12, second)
   142  	buf.Tmp[14] = '.'
   143  	buf.nDigits(6, 15, now.Nanosecond()/1000, '0')
   144  	buf.Tmp[21] = ' '
   145  	buf.nDigits(7, 22, Pid, ' ') // TODO: should be TID
   146  	buf.Tmp[29] = ' '
   147  	buf.Write(buf.Tmp[:30])
   148  	buf.WriteString(file)
   149  	buf.Tmp[0] = ':'
   150  	n := buf.someDigits(1, line)
   151  	buf.Tmp[n+1] = ']'
   152  	buf.Tmp[n+2] = ' '
   153  	buf.Write(buf.Tmp[:n+3])
   154  }
   155  
   156  // SprintHeader formats a log header and returns a string. This is a simpler
   157  // version of FormatHeader for use in ktesting.
   158  func (buf *Buffer) SprintHeader(s severity.Severity, now time.Time) string {
   159  	if s > severity.FatalLog {
   160  		s = severity.InfoLog // for safety.
   161  	}
   162  
   163  	// Avoid Fprintf, for speed. The format is so simple that we can do it quickly by hand.
   164  	// It's worth about 3X. Fprintf is hard.
   165  	if Time != nil {
   166  		now = *Time
   167  	}
   168  	_, month, day := now.Date()
   169  	hour, minute, second := now.Clock()
   170  	// Lmmdd hh:mm:ss.uuuuuu threadid file:line]
   171  	buf.Tmp[0] = severity.Char[s]
   172  	buf.twoDigits(1, int(month))
   173  	buf.twoDigits(3, day)
   174  	buf.Tmp[5] = ' '
   175  	buf.twoDigits(6, hour)
   176  	buf.Tmp[8] = ':'
   177  	buf.twoDigits(9, minute)
   178  	buf.Tmp[11] = ':'
   179  	buf.twoDigits(12, second)
   180  	buf.Tmp[14] = '.'
   181  	buf.nDigits(6, 15, now.Nanosecond()/1000, '0')
   182  	buf.Tmp[21] = ']'
   183  	return string(buf.Tmp[:22])
   184  }
   185  

View as plain text