123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176 |
- // Copyright 2013 Google Inc. All Rights Reserved.
- // Copyright 2022 The Kubernetes 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 buffer provides a cache for byte.Buffer instances that can be reused
- // to avoid frequent allocation and deallocation. It also has utility code
- // for log header formatting that use these buffers.
- package buffer
- import (
- "bytes"
- "os"
- "sync"
- "time"
- "k8s.io/klog/v2/internal/severity"
- )
- var (
- // Pid is inserted into log headers. Can be overridden for tests.
- Pid = os.Getpid()
- )
- // Buffer holds a single byte.Buffer for reuse. The zero value is ready for
- // use. It also provides some helper methods for output formatting.
- type Buffer struct {
- bytes.Buffer
- Tmp [64]byte // temporary byte array for creating headers.
- next *Buffer
- }
- var buffers = sync.Pool{
- New: func() interface{} {
- return new(Buffer)
- },
- }
- // GetBuffer returns a new, ready-to-use buffer.
- func GetBuffer() *Buffer {
- b := buffers.Get().(*Buffer)
- b.Reset()
- return b
- }
- // PutBuffer returns a buffer to the free list.
- func PutBuffer(b *Buffer) {
- if b.Len() >= 256 {
- // Let big buffers die a natural death, without relying on
- // sync.Pool behavior. The documentation implies that items may
- // get deallocated while stored there ("If the Pool holds the
- // only reference when this [= be removed automatically]
- // happens, the item might be deallocated."), but
- // https://github.com/golang/go/issues/23199 leans more towards
- // having such a size limit.
- return
- }
- buffers.Put(b)
- }
- // Some custom tiny helper functions to print the log header efficiently.
- const digits = "0123456789"
- // twoDigits formats a zero-prefixed two-digit integer at buf.Tmp[i].
- func (buf *Buffer) twoDigits(i, d int) {
- buf.Tmp[i+1] = digits[d%10]
- d /= 10
- buf.Tmp[i] = digits[d%10]
- }
- // nDigits formats an n-digit integer at buf.Tmp[i],
- // padding with pad on the left.
- // It assumes d >= 0.
- func (buf *Buffer) nDigits(n, i, d int, pad byte) {
- j := n - 1
- for ; j >= 0 && d > 0; j-- {
- buf.Tmp[i+j] = digits[d%10]
- d /= 10
- }
- for ; j >= 0; j-- {
- buf.Tmp[i+j] = pad
- }
- }
- // someDigits formats a zero-prefixed variable-width integer at buf.Tmp[i].
- func (buf *Buffer) someDigits(i, d int) int {
- // Print into the top, then copy down. We know there's space for at least
- // a 10-digit number.
- j := len(buf.Tmp)
- for {
- j--
- buf.Tmp[j] = digits[d%10]
- d /= 10
- if d == 0 {
- break
- }
- }
- return copy(buf.Tmp[i:], buf.Tmp[j:])
- }
- // FormatHeader formats a log header using the provided file name and line number
- // and writes it into the buffer.
- func (buf *Buffer) FormatHeader(s severity.Severity, file string, line int, now time.Time) {
- if line < 0 {
- line = 0 // not a real line number, but acceptable to someDigits
- }
- if s > severity.FatalLog {
- s = severity.InfoLog // for safety.
- }
- // Avoid Fprintf, for speed. The format is so simple that we can do it quickly by hand.
- // It's worth about 3X. Fprintf is hard.
- _, month, day := now.Date()
- hour, minute, second := now.Clock()
- // Lmmdd hh:mm:ss.uuuuuu threadid file:line]
- buf.Tmp[0] = severity.Char[s]
- buf.twoDigits(1, int(month))
- buf.twoDigits(3, day)
- buf.Tmp[5] = ' '
- buf.twoDigits(6, hour)
- buf.Tmp[8] = ':'
- buf.twoDigits(9, minute)
- buf.Tmp[11] = ':'
- buf.twoDigits(12, second)
- buf.Tmp[14] = '.'
- buf.nDigits(6, 15, now.Nanosecond()/1000, '0')
- buf.Tmp[21] = ' '
- buf.nDigits(7, 22, Pid, ' ') // TODO: should be TID
- buf.Tmp[29] = ' '
- buf.Write(buf.Tmp[:30])
- buf.WriteString(file)
- buf.Tmp[0] = ':'
- n := buf.someDigits(1, line)
- buf.Tmp[n+1] = ']'
- buf.Tmp[n+2] = ' '
- buf.Write(buf.Tmp[:n+3])
- }
- // SprintHeader formats a log header and returns a string. This is a simpler
- // version of FormatHeader for use in ktesting.
- func (buf *Buffer) SprintHeader(s severity.Severity, now time.Time) string {
- if s > severity.FatalLog {
- s = severity.InfoLog // for safety.
- }
- // Avoid Fprintf, for speed. The format is so simple that we can do it quickly by hand.
- // It's worth about 3X. Fprintf is hard.
- _, month, day := now.Date()
- hour, minute, second := now.Clock()
- // Lmmdd hh:mm:ss.uuuuuu threadid file:line]
- buf.Tmp[0] = severity.Char[s]
- buf.twoDigits(1, int(month))
- buf.twoDigits(3, day)
- buf.Tmp[5] = ' '
- buf.twoDigits(6, hour)
- buf.Tmp[8] = ':'
- buf.twoDigits(9, minute)
- buf.Tmp[11] = ':'
- buf.twoDigits(12, second)
- buf.Tmp[14] = '.'
- buf.nDigits(6, 15, now.Nanosecond()/1000, '0')
- buf.Tmp[21] = ']'
- return string(buf.Tmp[:22])
- }
|