buffer.go 5.0 KB

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