| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205 |
- // Unless explicitly stated otherwise all files in this repository are licensed
- // under the Apache License Version 2.0.
- // This product includes software developed at Datadog (https://www.datadoghq.com/).
- // Copyright 2016 Datadog, Inc.
- package tracer
- import (
- "strconv"
- "strings"
- "gopkg.in/DataDog/dd-trace-go.v1/ddtrace"
- "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext"
- "gopkg.in/DataDog/dd-trace-go.v1/internal/globalconfig"
- "gopkg.in/DataDog/dd-trace-go.v1/internal/log"
- )
- // SQLCommentInjectionMode represents the mode of SQL comment injection.
- //
- // Deprecated: Use DBMPropagationMode instead.
- type SQLCommentInjectionMode DBMPropagationMode
- const (
- // SQLInjectionUndefined represents the comment injection mode is not set. This is the same as SQLInjectionDisabled.
- SQLInjectionUndefined SQLCommentInjectionMode = SQLCommentInjectionMode(DBMPropagationModeUndefined)
- // SQLInjectionDisabled represents the comment injection mode where all injection is disabled.
- SQLInjectionDisabled SQLCommentInjectionMode = SQLCommentInjectionMode(DBMPropagationModeDisabled)
- // SQLInjectionModeService represents the comment injection mode where only service tags (name, env, version) are injected.
- SQLInjectionModeService SQLCommentInjectionMode = SQLCommentInjectionMode(DBMPropagationModeService)
- // SQLInjectionModeFull represents the comment injection mode where both service tags and tracing tags. Tracing tags include span id, trace id and sampling priority.
- SQLInjectionModeFull SQLCommentInjectionMode = SQLCommentInjectionMode(DBMPropagationModeFull)
- )
- // DBMPropagationMode represents the mode of dbm propagation.
- //
- // Note that enabling sql comment propagation results in potentially confidential data (service names)
- // being stored in the databases which can then be accessed by other 3rd parties that have been granted
- // access to the database.
- type DBMPropagationMode string
- const (
- // DBMPropagationModeUndefined represents the dbm propagation mode not being set. This is the same as DBMPropagationModeDisabled.
- DBMPropagationModeUndefined DBMPropagationMode = ""
- // DBMPropagationModeDisabled represents the dbm propagation mode where all propagation is disabled.
- DBMPropagationModeDisabled DBMPropagationMode = "disabled"
- // DBMPropagationModeService represents the dbm propagation mode where only service tags (name, env, version) are propagated to dbm.
- DBMPropagationModeService DBMPropagationMode = "service"
- // DBMPropagationModeFull represents the dbm propagation mode where both service tags and tracing tags are propagated. Tracing tags include span id, trace id and the sampled flag.
- DBMPropagationModeFull DBMPropagationMode = "full"
- )
- // Key names for SQL comment tags.
- const (
- sqlCommentTraceParent = "traceparent"
- sqlCommentParentService = "ddps"
- sqlCommentDBService = "dddbs"
- sqlCommentParentVersion = "ddpv"
- sqlCommentEnv = "dde"
- )
- // Current trace context version (see https://www.w3.org/TR/trace-context/#version)
- const w3cContextVersion = "00"
- // SQLCommentCarrier is a carrier implementation that injects a span context in a SQL query in the form
- // of a sqlcommenter formatted comment prepended to the original query text.
- // See https://google.github.io/sqlcommenter/spec/ for more details.
- type SQLCommentCarrier struct {
- Query string
- Mode DBMPropagationMode
- DBServiceName string
- SpanID uint64
- }
- // Inject injects a span context in the carrier's Query field as a comment.
- func (c *SQLCommentCarrier) Inject(spanCtx ddtrace.SpanContext) error {
- c.SpanID = generateSpanID(now())
- tags := make(map[string]string)
- switch c.Mode {
- case DBMPropagationModeUndefined:
- fallthrough
- case DBMPropagationModeDisabled:
- return nil
- case DBMPropagationModeFull:
- var (
- samplingPriority int
- traceID uint64
- )
- if ctx, ok := spanCtx.(*spanContext); ok {
- if sp, ok := ctx.samplingPriority(); ok {
- samplingPriority = sp
- }
- traceID = ctx.TraceID()
- }
- if traceID == 0 {
- traceID = c.SpanID
- }
- sampled := int64(0)
- if samplingPriority > 0 {
- sampled = 1
- }
- tags[sqlCommentTraceParent] = encodeTraceParent(traceID, c.SpanID, sampled)
- fallthrough
- case DBMPropagationModeService:
- var env, version string
- if ctx, ok := spanCtx.(*spanContext); ok {
- if e, ok := ctx.meta(ext.Environment); ok {
- env = e
- }
- if v, ok := ctx.meta(ext.Version); ok {
- version = v
- }
- }
- if globalconfig.ServiceName() != "" {
- tags[sqlCommentParentService] = globalconfig.ServiceName()
- }
- if env != "" {
- tags[sqlCommentEnv] = env
- }
- if version != "" {
- tags[sqlCommentParentVersion] = version
- }
- tags[sqlCommentDBService] = c.DBServiceName
- }
- c.Query = commentQuery(c.Query, tags)
- return nil
- }
- // encodeTraceParent encodes trace parent as per the w3c trace context spec (https://www.w3.org/TR/trace-context/#version).
- func encodeTraceParent(traceID uint64, spanID uint64, sampled int64) string {
- var b strings.Builder
- // traceparent has a fixed length of 55:
- // 2 bytes for the version, 32 for the trace id, 16 for the span id, 2 for the sampled flag and 3 for separators
- b.Grow(55)
- b.WriteString(w3cContextVersion)
- b.WriteRune('-')
- tid := strconv.FormatUint(traceID, 16)
- for i := 0; i < 32-len(tid); i++ {
- b.WriteRune('0')
- }
- b.WriteString(tid)
- b.WriteRune('-')
- sid := strconv.FormatUint(spanID, 16)
- for i := 0; i < 16-len(sid); i++ {
- b.WriteRune('0')
- }
- b.WriteString(sid)
- b.WriteRune('-')
- b.WriteRune('0')
- b.WriteString(strconv.FormatInt(sampled, 16))
- return b.String()
- }
- var (
- keyReplacer = strings.NewReplacer(" ", "%20", "!", "%21", "#", "%23", "$", "%24", "%", "%25", "&", "%26", "'", "%27", "(", "%28", ")", "%29", "*", "%2A", "+", "%2B", ",", "%2C", "/", "%2F", ":", "%3A", ";", "%3B", "=", "%3D", "?", "%3F", "@", "%40", "[", "%5B", "]", "%5D")
- valueReplacer = strings.NewReplacer(" ", "%20", "!", "%21", "#", "%23", "$", "%24", "%", "%25", "&", "%26", "'", "%27", "(", "%28", ")", "%29", "*", "%2A", "+", "%2B", ",", "%2C", "/", "%2F", ":", "%3A", ";", "%3B", "=", "%3D", "?", "%3F", "@", "%40", "[", "%5B", "]", "%5D", "'", "\\'")
- )
- // commentQuery returns the given query with the tags from the SQLCommentCarrier applied to it as a
- // prepended SQL comment. The format of the comment follows the sqlcommenter spec.
- // See https://google.github.io/sqlcommenter/spec/ for more details.
- func commentQuery(query string, tags map[string]string) string {
- if len(tags) == 0 {
- return ""
- }
- var b strings.Builder
- // the sqlcommenter specification dictates that tags should be sorted. Since we know all injected keys,
- // we skip a sorting operation by specifying the order of keys statically
- orderedKeys := []string{sqlCommentDBService, sqlCommentEnv, sqlCommentParentService, sqlCommentParentVersion, sqlCommentTraceParent}
- first := true
- for _, k := range orderedKeys {
- if v, ok := tags[k]; ok {
- // we need to URL-encode both keys and values and escape single quotes in values
- // https://google.github.io/sqlcommenter/spec/
- key := keyReplacer.Replace(k)
- val := valueReplacer.Replace(v)
- if first {
- b.WriteString("/*")
- } else {
- b.WriteRune(',')
- }
- b.WriteString(key)
- b.WriteRune('=')
- b.WriteRune('\'')
- b.WriteString(val)
- b.WriteRune('\'')
- first = false
- }
- }
- if b.Len() == 0 {
- return query
- }
- b.WriteString("*/")
- if query == "" {
- return b.String()
- }
- log.Debug("Injected sql comment: %s", b.String())
- b.WriteRune(' ')
- b.WriteString(query)
- return b.String()
- }
- // Extract is not implemented on SQLCommentCarrier
- func (c *SQLCommentCarrier) Extract() (ddtrace.SpanContext, error) {
- return nil, nil
- }
|