@@ -50,7 +50,7 @@ func New(ctx context.Context, next http.Handler, cfg dynamic.Canary, name string
50
50
logger := log .FromContext (middlewares .GetLoggerCtx (ctx , name , typeName ))
51
51
52
52
if cfg .Product == "" {
53
- return nil , fmt .Errorf ("product name required for Canary middleware" )
53
+ return nil , fmt .Errorf ("product name required for canary middleware" )
54
54
}
55
55
56
56
expiration := time .Duration (cfg .CacheExpiration )
@@ -97,7 +97,13 @@ func (c *Canary) processRequestID(rw http.ResponseWriter, req *http.Request) {
97
97
requestID := req .Header .Get (headerXRequestID )
98
98
if c .addRequestID {
99
99
if requestID == "" {
100
- requestID = generatorUUID ()
100
+ // extract trace-id as x-request-id
101
+ // https://www.w3.org/TR/trace-context/#traceparent-header
102
+ if traceparent := req .Header .Get ("traceparent" ); len (traceparent ) >= 55 {
103
+ requestID = traceparent [3 :35 ]
104
+ } else {
105
+ requestID = generatorUUID ()
106
+ }
101
107
req .Header .Set (headerXRequestID , requestID )
102
108
}
103
109
rw .Header ().Set (headerXRequestID , requestID )
@@ -112,6 +118,9 @@ func (c *Canary) processRequestID(rw http.ResponseWriter, req *http.Request) {
112
118
logData .Core ["XRequestID" ] = requestID
113
119
logData .Core ["UserAgent" ] = req .Header .Get (headerUA )
114
120
logData .Core ["Referer" ] = req .Header .Get ("Referer" )
121
+ if traceparent := req .Header .Get ("traceparent" ); traceparent != "" {
122
+ logData .Core ["Traceparent" ] = traceparent
123
+ }
115
124
}
116
125
}
117
126
@@ -136,10 +145,10 @@ func (c *Canary) processCanary(rw http.ResponseWriter, req *http.Request) {
136
145
if info .label == "" && info .uid != "" {
137
146
labels := c .ls .MustLoadLabels (req .Context (), info .uid , req .Header .Get (headerXRequestID ))
138
147
for _ , l := range labels {
139
- if info . client != "" && ! l .MatchClient (info .client ) {
148
+ if ! l .MatchClient (info .client ) {
140
149
continue
141
150
}
142
- if info . channel != "" && ! l .MatchChannel (info .channel ) {
151
+ if ! l .MatchChannel (info .channel ) {
143
152
continue
144
153
}
145
154
info .label = l .Label
@@ -154,7 +163,7 @@ func (c *Canary) processCanary(rw http.ResponseWriter, req *http.Request) {
154
163
155
164
if logData := accesslog .GetLogData (req ); logData != nil {
156
165
logData .Core ["UID" ] = info .uid
157
- logData .Core ["XCanary" ] = req . Header . Values ( headerXCanary )
166
+ logData .Core ["XCanary" ] = info . String ( )
158
167
}
159
168
}
160
169
@@ -239,6 +248,12 @@ func extractUserIDFromBase64(s string) string {
239
248
return ""
240
249
}
241
250
251
+ // Canary Header specification, reference to https://www.w3.org/TR/trace-context/#tracestate-header
252
+ // X-Canary: label=beta,nofallback
253
+ // X-Canary: client=iOS,channel=stable,app=teambition,version=v10.0
254
+ // full example
255
+ // X-Canary: label=beta,product=urbs,uid=5c4057f0be825b390667abee,client=iOS,channel=stable,app=teambition,version=v10.0,nofallback,testing
256
+ // support fields: label, product, uid, client, channel, app, version, nofallback, testing
242
257
type canaryHeader struct {
243
258
label string
244
259
product string
@@ -252,8 +267,32 @@ type canaryHeader struct {
252
267
}
253
268
254
269
// uid and product will not be extracted
270
+ // standard
271
+ // X-Canary: label=beta,product=urbs,uid=5c4057f0be825b390667abee,nofallback ...
272
+ // and compatible with
273
+ // X-Canary: beta
274
+ // or
275
+ // X-Canary: label=beta; product=urbs; uid=5c4057f0be825b390667abee; nofallback ...
276
+ // or
277
+ // X-Canary: label=beta
278
+ // X-Canary: product=urbs
279
+ // X-Canary: uid=5c4057f0be825b390667abee
280
+ // X-Canary: nofallback
255
281
func (ch * canaryHeader ) fromHeader (header http.Header , trust bool ) {
256
- ch .feed (header .Values (headerXCanary ), trust )
282
+ vals := header .Values (headerXCanary )
283
+ if len (vals ) == 1 {
284
+ if strings .IndexByte (vals [0 ], ',' ) > 0 {
285
+ vals = strings .Split (vals [0 ], "," )
286
+ } else if strings .IndexByte (vals [0 ], ';' ) > 0 {
287
+ vals = strings .Split (vals [0 ], ";" )
288
+ }
289
+ }
290
+ ch .feed (vals , trust )
291
+ }
292
+
293
+ // label should not be empty
294
+ func (ch * canaryHeader ) intoHeader (header http.Header ) {
295
+ header .Set (headerXCanary , ch .String ())
257
296
}
258
297
259
298
func (ch * canaryHeader ) feed (vals []string , trust bool ) {
@@ -287,33 +326,35 @@ func (ch *canaryHeader) feed(vals []string, trust bool) {
287
326
}
288
327
289
328
// label should not be empty
290
- func (ch * canaryHeader ) intoHeader ( header http. Header ) {
329
+ func (ch * canaryHeader ) String () string {
291
330
if ch .label == "" {
292
- return
331
+ return ""
293
332
}
294
- header .Set (headerXCanary , fmt .Sprintf ("label=%s" , ch .label ))
333
+ vals := make ([]string , 0 , 4 )
334
+ vals = append (vals , fmt .Sprintf ("label=%s" , ch .label ))
295
335
if ch .product != "" {
296
- header . Add ( headerXCanary , fmt .Sprintf ("product=%s" , ch .product ))
336
+ vals = append ( vals , fmt .Sprintf ("product=%s" , ch .product ))
297
337
}
298
338
if ch .uid != "" {
299
- header . Add ( headerXCanary , fmt .Sprintf ("uid=%s" , ch .uid ))
339
+ vals = append ( vals , fmt .Sprintf ("uid=%s" , ch .uid ))
300
340
}
301
341
if ch .client != "" {
302
- header . Add ( headerXCanary , fmt .Sprintf ("client=%s" , ch .client ))
342
+ vals = append ( vals , fmt .Sprintf ("client=%s" , ch .client ))
303
343
}
304
344
if ch .channel != "" {
305
- header . Add ( headerXCanary , fmt .Sprintf ("channel=%s" , ch .channel ))
345
+ vals = append ( vals , fmt .Sprintf ("channel=%s" , ch .channel ))
306
346
}
307
347
if ch .app != "" {
308
- header . Add ( headerXCanary , fmt .Sprintf ("app=%s" , ch .app ))
348
+ vals = append ( vals , fmt .Sprintf ("app=%s" , ch .app ))
309
349
}
310
350
if ch .version != "" {
311
- header . Add ( headerXCanary , fmt .Sprintf ("version=%s" , ch .version ))
351
+ vals = append ( vals , fmt .Sprintf ("version=%s" , ch .version ))
312
352
}
313
353
if ch .nofallback {
314
- header . Add ( headerXCanary , "nofallback" )
354
+ vals = append ( vals , "nofallback" )
315
355
}
316
356
if ch .testing {
317
- header . Add ( headerXCanary , "testing" )
357
+ vals = append ( vals , "testing" )
318
358
}
359
+ return strings .Join (vals , "," )
319
360
}
0 commit comments