You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

211 line
6.6 KiB

  1. /*
  2. *
  3. * Copyright 2018 gRPC authors.
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. *
  17. */
  18. package binarylog
  19. import (
  20. "errors"
  21. "fmt"
  22. "regexp"
  23. "strconv"
  24. "strings"
  25. "google.golang.org/grpc/grpclog"
  26. )
  27. // NewLoggerFromConfigString reads the string and build a logger. It can be used
  28. // to build a new logger and assign it to binarylog.Logger.
  29. //
  30. // Example filter config strings:
  31. // - "" Nothing will be logged
  32. // - "*" All headers and messages will be fully logged.
  33. // - "*{h}" Only headers will be logged.
  34. // - "*{m:256}" Only the first 256 bytes of each message will be logged.
  35. // - "Foo/*" Logs every method in service Foo
  36. // - "Foo/*,-Foo/Bar" Logs every method in service Foo except method /Foo/Bar
  37. // - "Foo/*,Foo/Bar{m:256}" Logs the first 256 bytes of each message in method
  38. // /Foo/Bar, logs all headers and messages in every other method in service
  39. // Foo.
  40. //
  41. // If two configs exist for one certain method or service, the one specified
  42. // later overrides the privous config.
  43. func NewLoggerFromConfigString(s string) Logger {
  44. if s == "" {
  45. return nil
  46. }
  47. l := newEmptyLogger()
  48. methods := strings.Split(s, ",")
  49. for _, method := range methods {
  50. if err := l.fillMethodLoggerWithConfigString(method); err != nil {
  51. grpclog.Warningf("failed to parse binary log config: %v", err)
  52. return nil
  53. }
  54. }
  55. return l
  56. }
  57. // fillMethodLoggerWithConfigString parses config, creates methodLogger and adds
  58. // it to the right map in the logger.
  59. func (l *logger) fillMethodLoggerWithConfigString(config string) error {
  60. // "" is invalid.
  61. if config == "" {
  62. return errors.New("empty string is not a valid method binary logging config")
  63. }
  64. // "-service/method", blacklist, no * or {} allowed.
  65. if config[0] == '-' {
  66. s, m, suffix, err := parseMethodConfigAndSuffix(config[1:])
  67. if err != nil {
  68. return fmt.Errorf("invalid config: %q, %v", config, err)
  69. }
  70. if m == "*" {
  71. return fmt.Errorf("invalid config: %q, %v", config, "* not allowd in blacklist config")
  72. }
  73. if suffix != "" {
  74. return fmt.Errorf("invalid config: %q, %v", config, "header/message limit not allowed in blacklist config")
  75. }
  76. if err := l.setBlacklist(s + "/" + m); err != nil {
  77. return fmt.Errorf("invalid config: %v", err)
  78. }
  79. return nil
  80. }
  81. // "*{h:256;m:256}"
  82. if config[0] == '*' {
  83. hdr, msg, err := parseHeaderMessageLengthConfig(config[1:])
  84. if err != nil {
  85. return fmt.Errorf("invalid config: %q, %v", config, err)
  86. }
  87. if err := l.setDefaultMethodLogger(&methodLoggerConfig{hdr: hdr, msg: msg}); err != nil {
  88. return fmt.Errorf("invalid config: %v", err)
  89. }
  90. return nil
  91. }
  92. s, m, suffix, err := parseMethodConfigAndSuffix(config)
  93. if err != nil {
  94. return fmt.Errorf("invalid config: %q, %v", config, err)
  95. }
  96. hdr, msg, err := parseHeaderMessageLengthConfig(suffix)
  97. if err != nil {
  98. return fmt.Errorf("invalid header/message length config: %q, %v", suffix, err)
  99. }
  100. if m == "*" {
  101. if err := l.setServiceMethodLogger(s, &methodLoggerConfig{hdr: hdr, msg: msg}); err != nil {
  102. return fmt.Errorf("invalid config: %v", err)
  103. }
  104. } else {
  105. if err := l.setMethodMethodLogger(s+"/"+m, &methodLoggerConfig{hdr: hdr, msg: msg}); err != nil {
  106. return fmt.Errorf("invalid config: %v", err)
  107. }
  108. }
  109. return nil
  110. }
  111. const (
  112. // TODO: this const is only used by env_config now. But could be useful for
  113. // other config. Move to binarylog.go if necessary.
  114. maxUInt = ^uint64(0)
  115. // For "p.s/m" plus any suffix. Suffix will be parsed again. See test for
  116. // expected output.
  117. longMethodConfigRegexpStr = `^([\w./]+)/((?:\w+)|[*])(.+)?$`
  118. // For suffix from above, "{h:123,m:123}". See test for expected output.
  119. optionalLengthRegexpStr = `(?::(\d+))?` // Optional ":123".
  120. headerConfigRegexpStr = `^{h` + optionalLengthRegexpStr + `}$`
  121. messageConfigRegexpStr = `^{m` + optionalLengthRegexpStr + `}$`
  122. headerMessageConfigRegexpStr = `^{h` + optionalLengthRegexpStr + `;m` + optionalLengthRegexpStr + `}$`
  123. )
  124. var (
  125. longMethodConfigRegexp = regexp.MustCompile(longMethodConfigRegexpStr)
  126. headerConfigRegexp = regexp.MustCompile(headerConfigRegexpStr)
  127. messageConfigRegexp = regexp.MustCompile(messageConfigRegexpStr)
  128. headerMessageConfigRegexp = regexp.MustCompile(headerMessageConfigRegexpStr)
  129. )
  130. // Turn "service/method{h;m}" into "service", "method", "{h;m}".
  131. func parseMethodConfigAndSuffix(c string) (service, method, suffix string, _ error) {
  132. // Regexp result:
  133. //
  134. // in: "p.s/m{h:123,m:123}",
  135. // out: []string{"p.s/m{h:123,m:123}", "p.s", "m", "{h:123,m:123}"},
  136. match := longMethodConfigRegexp.FindStringSubmatch(c)
  137. if match == nil {
  138. return "", "", "", fmt.Errorf("%q contains invalid substring", c)
  139. }
  140. service = match[1]
  141. method = match[2]
  142. suffix = match[3]
  143. return
  144. }
  145. // Turn "{h:123;m:345}" into 123, 345.
  146. //
  147. // Return maxUInt if length is unspecified.
  148. func parseHeaderMessageLengthConfig(c string) (hdrLenStr, msgLenStr uint64, err error) {
  149. if c == "" {
  150. return maxUInt, maxUInt, nil
  151. }
  152. // Header config only.
  153. if match := headerConfigRegexp.FindStringSubmatch(c); match != nil {
  154. if s := match[1]; s != "" {
  155. hdrLenStr, err = strconv.ParseUint(s, 10, 64)
  156. if err != nil {
  157. return 0, 0, fmt.Errorf("failed to convert %q to uint", s)
  158. }
  159. return hdrLenStr, 0, nil
  160. }
  161. return maxUInt, 0, nil
  162. }
  163. // Message config only.
  164. if match := messageConfigRegexp.FindStringSubmatch(c); match != nil {
  165. if s := match[1]; s != "" {
  166. msgLenStr, err = strconv.ParseUint(s, 10, 64)
  167. if err != nil {
  168. return 0, 0, fmt.Errorf("failed to convert %q to uint", s)
  169. }
  170. return 0, msgLenStr, nil
  171. }
  172. return 0, maxUInt, nil
  173. }
  174. // Header and message config both.
  175. if match := headerMessageConfigRegexp.FindStringSubmatch(c); match != nil {
  176. // Both hdr and msg are specified, but one or two of them might be empty.
  177. hdrLenStr = maxUInt
  178. msgLenStr = maxUInt
  179. if s := match[1]; s != "" {
  180. hdrLenStr, err = strconv.ParseUint(s, 10, 64)
  181. if err != nil {
  182. return 0, 0, fmt.Errorf("failed to convert %q to uint", s)
  183. }
  184. }
  185. if s := match[2]; s != "" {
  186. msgLenStr, err = strconv.ParseUint(s, 10, 64)
  187. if err != nil {
  188. return 0, 0, fmt.Errorf("failed to convert %q to uint", s)
  189. }
  190. }
  191. return hdrLenStr, msgLenStr, nil
  192. }
  193. return 0, 0, fmt.Errorf("%q contains invalid substring", c)
  194. }