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