buffer.go 4.9 KB

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