139 lines
4.1 KiB
Go
139 lines
4.1 KiB
Go
|
// Licensed to SkyAPM org under one or more contributor
|
||
|
// license agreements. See the NOTICE file distributed with
|
||
|
// this work for additional information regarding copyright
|
||
|
// ownership. SkyAPM org licenses this file to you under
|
||
|
// the Apache License, Version 2.0 (the "License"); you may
|
||
|
// not use this file except in compliance with the License.
|
||
|
// You may obtain a copy of the License at
|
||
|
//
|
||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||
|
//
|
||
|
// Unless required by applicable law or agreed to in writing,
|
||
|
// software distributed under the License is distributed on an
|
||
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||
|
// KIND, either express or implied. See the License for the
|
||
|
// specific language governing permissions and limitations
|
||
|
// under the License.
|
||
|
|
||
|
/*
|
||
|
Package propagation holds the required function signatures for Injection and
|
||
|
Extraction. It also contains decoder and encoder of SkyWalking propagation protocol.
|
||
|
*/
|
||
|
package propagation
|
||
|
|
||
|
import (
|
||
|
"encoding/base64"
|
||
|
"fmt"
|
||
|
"strconv"
|
||
|
"strings"
|
||
|
|
||
|
"github.com/pkg/errors"
|
||
|
)
|
||
|
|
||
|
const (
|
||
|
Header string = "sw8"
|
||
|
headerLen int = 8
|
||
|
splitToken string = "-"
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
errEmptyHeader = errors.New("empty header")
|
||
|
errInsufficientHeaderEntities = errors.New("insufficient header entities")
|
||
|
)
|
||
|
|
||
|
// Extractor is a tool specification which define how to
|
||
|
// extract trace parent context from propagation context
|
||
|
type Extractor func() (string, error)
|
||
|
|
||
|
// Injector is a tool specification which define how to
|
||
|
// inject trace context into propagation context
|
||
|
type Injector func(header string) error
|
||
|
|
||
|
// SpanContext defines propagation specification of SkyWalking
|
||
|
type SpanContext struct {
|
||
|
TraceID string `json:"trace_id"`
|
||
|
ParentSegmentID string `json:"parent_segment_id"`
|
||
|
ParentService string `json:"parent_service"`
|
||
|
ParentServiceInstance string `json:"parent_service_instance"`
|
||
|
ParentEndpoint string `json:"parent_endpoint"`
|
||
|
AddressUsedAtClient string `json:"address_used_at_client"`
|
||
|
ParentSpanID int32 `json:"parent_span_id"`
|
||
|
Sample int8 `json:"sample"`
|
||
|
}
|
||
|
|
||
|
// DecodeSW6 converts string header to SpanContext
|
||
|
func (tc *SpanContext) DecodeSW8(header string) error {
|
||
|
if header == "" {
|
||
|
return errEmptyHeader
|
||
|
}
|
||
|
hh := strings.Split(header, splitToken)
|
||
|
if len(hh) < headerLen {
|
||
|
return errors.WithMessagef(errInsufficientHeaderEntities, "header string: %s", header)
|
||
|
}
|
||
|
sample, err := strconv.ParseInt(hh[0], 10, 8)
|
||
|
if err != nil {
|
||
|
return errors.Errorf("str to int8 error %s", hh[0])
|
||
|
}
|
||
|
tc.Sample = int8(sample)
|
||
|
tc.TraceID, err = decodeBase64(hh[1])
|
||
|
if err != nil {
|
||
|
return errors.Wrap(err, "trace id parse error")
|
||
|
}
|
||
|
tc.ParentSegmentID, err = decodeBase64(hh[2])
|
||
|
if err != nil {
|
||
|
return errors.Wrap(err, "parent segment id parse error")
|
||
|
}
|
||
|
tc.ParentSpanID, err = stringConvertInt32(hh[3])
|
||
|
if err != nil {
|
||
|
return errors.Wrap(err, "parent span id parse error")
|
||
|
}
|
||
|
tc.ParentService, err = decodeBase64(hh[4])
|
||
|
if err != nil {
|
||
|
return errors.Wrap(err, "parent service parse error")
|
||
|
}
|
||
|
tc.ParentServiceInstance, err = decodeBase64(hh[5])
|
||
|
if err != nil {
|
||
|
return errors.Wrap(err, "parent service instance parse error")
|
||
|
}
|
||
|
tc.ParentEndpoint, err = decodeBase64(hh[6])
|
||
|
if err != nil {
|
||
|
return errors.Wrap(err, "parent endpoint parse error")
|
||
|
}
|
||
|
tc.AddressUsedAtClient, err = decodeBase64(hh[7])
|
||
|
if err != nil {
|
||
|
return errors.Wrap(err, "network address parse error")
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// EncodeSW6 converts SpanContext to string header
|
||
|
func (tc *SpanContext) EncodeSW8() string {
|
||
|
return strings.Join([]string{
|
||
|
fmt.Sprint(tc.Sample),
|
||
|
encodeBase64(tc.TraceID),
|
||
|
encodeBase64(tc.ParentSegmentID),
|
||
|
fmt.Sprint(tc.ParentSpanID),
|
||
|
encodeBase64(tc.ParentService),
|
||
|
encodeBase64(tc.ParentServiceInstance),
|
||
|
encodeBase64(tc.ParentEndpoint),
|
||
|
encodeBase64(tc.AddressUsedAtClient),
|
||
|
}, "-")
|
||
|
}
|
||
|
|
||
|
func stringConvertInt32(str string) (int32, error) {
|
||
|
i, err := strconv.ParseInt(str, 0, 32)
|
||
|
return int32(i), err
|
||
|
}
|
||
|
|
||
|
func decodeBase64(str string) (string, error) {
|
||
|
ret, err := base64.StdEncoding.DecodeString(str)
|
||
|
if err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
return string(ret), nil
|
||
|
}
|
||
|
|
||
|
func encodeBase64(str string) string {
|
||
|
return base64.StdEncoding.EncodeToString([]byte(str))
|
||
|
}
|