Skip to content

Commit 0644b52

Browse files
authored
feat(telemetry): implement secure telemetry logging with PII protection (#3948)
Signed-off-by: Kemal Akkoyun <[email protected]>
1 parent 5abe3af commit 0644b52

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+4910
-504
lines changed

Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ generate: tools-install ## Run code generation
3232
lint: tools-install ## Run linting checks
3333
$(BIN_PATH) ./scripts/lint.sh --all
3434

35+
.PHONY: lint/go
36+
lint/go: tools-install ## Run Go linting checks
37+
$(BIN_PATH) golangci-lint run ./...
38+
3539
.PHONY: lint-fix
3640
lint-fix: tools-install ## Fix linting issues automatically
3741
$(BIN_PATH) golangci-lint run --fix ./...

contrib/net/http/internal/wrap/roundtrip.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ func (t *httpTraceTimings) addTimingTags(span *tracer.Span) {
5252
t.addDurationTag(span, "http.tls.duration_ms", t.tlsStart, t.tlsEnd)
5353
t.addDurationTag(span, "http.get_conn.duration_ms", t.getConnStart, t.gotConn)
5454
t.addDurationTag(span, "http.first_byte.duration_ms", t.wroteHeaders, t.gotFirstByte)
55-
55+
5656
// Add error information if present
5757
if t.connectErr != nil {
5858
span.SetTag("http.connect.error", t.connectErr.Error())

ddtrace/opentelemetry/tracer.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,13 @@ import (
99
"context"
1010
"encoding/binary"
1111
"encoding/hex"
12+
"log/slog"
1213

1314
"github.com/DataDog/dd-trace-go/v2/ddtrace/baggage"
1415
"github.com/DataDog/dd-trace-go/v2/ddtrace/ext"
1516
"github.com/DataDog/dd-trace-go/v2/ddtrace/tracer"
1617
"github.com/DataDog/dd-trace-go/v2/internal/telemetry"
17-
"github.com/DataDog/dd-trace-go/v2/internal/telemetry/log"
18+
telemetrylog "github.com/DataDog/dd-trace-go/v2/internal/telemetry/log"
1819

1920
otelbaggage "go.opentelemetry.io/otel/baggage"
2021
oteltrace "go.opentelemetry.io/otel/trace"
@@ -128,7 +129,7 @@ func mergeBaggageFromContext(ctx context.Context) otelbaggage.Baggage {
128129
if err == nil {
129130
otelBag = b
130131
} else {
131-
log.Debug("Error adding baggage member with key %s; dropping", key)
132+
telemetrylog.Debug("Error adding baggage member; dropping", slog.String("key", key))
132133
}
133134
}
134135
}

ddtrace/tracer/log.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"bytes"
1010
"encoding/json"
1111
"fmt"
12+
"log/slog"
1213
"math"
1314
"net/http"
1415
"runtime"
@@ -173,5 +174,5 @@ func logStartup(t *tracer) {
173174
return
174175
}
175176
log.Info("DATADOG TRACER CONFIGURATION %s\n", string(bs))
176-
telemetrylog.Debug("DATADOG TRACER CONFIGURATION %s\n", string(bs))
177+
telemetrylog.Debug("DATADOG TRACER CONFIGURATION", slog.String("config", string(bs)))
177178
}

instrumentation/appsec/emitter/httpsec/http.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,8 @@ func RouteMatched(ctx context.Context, route string, routeParams map[string]stri
177177
op, ok := dyngo.FindOperation[HandlerOperation](ctx)
178178
if !ok {
179179
log.Debug("appsec: RouteMatched called without an active HandlerOperation in the context, ignoring")
180-
telemetrylog.Warn("appsec: RouteMatched called without an active HandlerOperation in the context, ignoring", telemetry.WithTags([]string{"product:appsec"}))
180+
telemetrylog.With(telemetry.WithTags([]string{"product:appsec"})).
181+
Warn("appsec: RouteMatched called without an active HandlerOperation in the context, ignoring")
181182
return nil
182183
}
183184

instrumentation/appsec/emitter/waf/actions/actions.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
package actions
77

88
import (
9+
"fmt"
10+
"log/slog"
11+
912
"github.com/DataDog/dd-trace-go/v2/instrumentation/appsec/dyngo"
1013
"github.com/DataDog/dd-trace-go/v2/internal/log"
1114
telemetrylog "github.com/DataDog/dd-trace-go/v2/internal/telemetry/log"
@@ -42,15 +45,15 @@ func SendActionEvents(op dyngo.Operation, actions map[string]any) bool {
4245
log.Debug("appsec: processing %q action with params %v", aType, params) //nolint:gocritic
4346
params, ok := params.(map[string]any)
4447
if !ok {
45-
telemetrylog.Error("appsec: could not cast action params to map[string]any from %T", params)
48+
telemetrylog.Error("appsec: could not cast action params to map[string]any", slog.String("actual_type", fmt.Sprintf("%T", params)))
4649
continue
4750
}
4851

4952
blocked = blocked || aType == "block_request"
5053

5154
actionHandler, ok := actionHandlers[aType]
5255
if !ok {
53-
telemetrylog.Error("appsec: unknown action type `%s`", aType)
56+
telemetrylog.Error("appsec: unknown action type", slog.String("action_type", aType))
5457
continue
5558
}
5659

instrumentation/instrumentation.go

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"github.com/DataDog/dd-trace-go/v2/internal/normalizer"
1818
"github.com/DataDog/dd-trace-go/v2/internal/stableconfig"
1919
"github.com/DataDog/dd-trace-go/v2/internal/telemetry"
20+
telemetrylog "github.com/DataDog/dd-trace-go/v2/internal/telemetry/log"
2021
"github.com/DataDog/dd-trace-go/v2/internal/version"
2122
)
2223

@@ -35,9 +36,11 @@ func Load(pkg Package) *Instrumentation {
3536
tracer.MarkIntegrationImported(info.TracedPackage)
3637

3738
return &Instrumentation{
38-
pkg: pkg,
39-
logger: newLogger(pkg),
40-
info: info,
39+
logger: newLogger(pkg),
40+
telemetrylog: telemetrylog.With(telemetry.WithTags([]string{"integration:" + string(pkg)})),
41+
42+
pkg: pkg,
43+
info: info,
4144
}
4245
}
4346

@@ -53,9 +56,11 @@ func Version() string {
5356

5457
// Instrumentation represents instrumentation for a package.
5558
type Instrumentation struct {
56-
pkg Package
57-
logger Logger
58-
info PackageInfo
59+
logger Logger
60+
telemetrylog *telemetrylog.Logger
61+
62+
pkg Package
63+
info PackageInfo
5964
}
6065

6166
// ServiceName returns the default service name to be set for the given instrumentation component.
@@ -93,6 +98,10 @@ func (i *Instrumentation) Logger() Logger {
9398
return i.logger
9499
}
95100

101+
func (i *Instrumentation) TelemetryLog() *telemetrylog.Logger {
102+
return i.telemetrylog
103+
}
104+
96105
func (i *Instrumentation) AnalyticsRate(defaultGlobal bool) float64 {
97106
if internal.BoolEnv("DD_TRACE_"+i.info.EnvVarPrefix+"_ANALYTICS_ENABLED", false) {
98107
return 1.0
@@ -145,9 +154,7 @@ type StatsdClient = internal.StatsdClient
145154
func (i *Instrumentation) StatsdClient(extraTags []string) (StatsdClient, error) {
146155
addr := globalconfig.DogstatsdAddr()
147156
tags := globalconfig.StatsTags()
148-
for _, tag := range extraTags {
149-
tags = append(tags, tag)
150-
}
157+
tags = append(tags, extraTags...)
151158
return internal.NewStatsdClient(addr, tags)
152159
}
153160

internal/appsec/appsec.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ package appsec
77

88
import (
99
"fmt"
10+
"log/slog"
1011
"sync"
1112

1213
"github.com/DataDog/go-libddwaf/v4"
@@ -74,7 +75,7 @@ func Start(opts ...config.StartOption) {
7475
} else {
7576
// DD_APPSEC_ENABLED is not set so we cannot know what the intent is here, we must log a
7677
// debug message instead to avoid showing an error to APM-tracing-only users.
77-
telemetrylog.Error("appsec: remote activation of threats detection cannot be enabled for the following reasons: %s", err.Error())
78+
telemetrylog.Error("appsec: remote activation of threats detection cannot be enabled", slog.Any("error", telemetrylog.NewSafeError(err)))
7879
}
7980
return
8081
}
@@ -90,7 +91,7 @@ func Start(opts ...config.StartOption) {
9091
// Start the remote configuration client
9192
log.Debug("appsec: starting the remote configuration client")
9293
if err := appsec.startRC(); err != nil {
93-
telemetrylog.Error("appsec: Remote config: disabled due to an instanciation error: %s", err.Error())
94+
telemetrylog.Error("appsec: Remote config: disabled due to an instantiation error", slog.Any("error", telemetrylog.NewSafeError(err)))
9495
}
9596

9697
if mode == config.RCStandby {
@@ -119,7 +120,8 @@ func Start(opts ...config.StartOption) {
119120
// Implement the AppSec log message C1
120121
func logUnexpectedStartError(err error) {
121122
log.Error("appsec: could not start because of an unexpected error: %s\nNo security activities will be collected. Please contact support at https://docs.datadoghq.com/help/ for help.", err.Error())
122-
telemetry.Log(telemetry.LogError, fmt.Sprintf("appsec: could not start because of an unexpected error: %s", err.Error()), telemetry.WithTags([]string{"product:appsec"}))
123+
124+
telemetrylog.Error("appsec: could not start because of an unexpected error", slog.Any("error", telemetrylog.NewSafeError(err)))
123125
telemetry.ProductStartError(telemetry.NamespaceAppSec, err)
124126
}
125127

internal/appsec/config/config.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,14 @@ package config
77

88
import (
99
"fmt"
10+
"log/slog"
1011
"time"
1112

1213
sharedinternal "github.com/DataDog/dd-trace-go/v2/internal"
1314
"github.com/DataDog/dd-trace-go/v2/internal/remoteconfig"
1415
"github.com/DataDog/dd-trace-go/v2/internal/stableconfig"
1516
"github.com/DataDog/dd-trace-go/v2/internal/telemetry"
16-
"github.com/DataDog/dd-trace-go/v2/internal/telemetry/log"
17+
telemetrylog "github.com/DataDog/dd-trace-go/v2/internal/telemetry/log"
1718
)
1819

1920
func init() {
@@ -26,7 +27,7 @@ func init() {
2627
func registerSCAAppConfigTelemetry() {
2728
_, _, err := stableconfig.Bool(EnvSCAEnabled, false)
2829
if err != nil {
29-
log.Error("appsec: %s", err.Error())
30+
telemetrylog.Error("appsec: failed to get SCA config", slog.Any("error", telemetrylog.NewSafeError(err)))
3031
return
3132
}
3233
}

internal/appsec/config/config_test.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@
66
package config
77

88
import (
9+
"strings"
910
"testing"
1011

1112
"github.com/DataDog/dd-trace-go/v2/internal/telemetry"
1213
"github.com/DataDog/dd-trace-go/v2/internal/telemetry/telemetrytest"
14+
"github.com/stretchr/testify/mock"
1315
)
1416

1517
func TestSCAEnabled(t *testing.T) {
@@ -42,7 +44,7 @@ func TestSCAEnabled(t *testing.T) {
4244
name: "parsing error",
4345
envVarVal: "not a boolean string representation [at {all!}]",
4446
telemetryExpected: false,
45-
telemetryLog: "appsec: non-boolean value for DD_APPSEC_SCA_ENABLED: 'not a boolean string representation [at {all!}]' in env_var configuration, dropping",
47+
telemetryLog: "appsec: failed to get SCA config",
4648
expectedValue: false,
4749
},
4850
} {
@@ -54,8 +56,13 @@ func TestSCAEnabled(t *testing.T) {
5456
telemetryClient := new(telemetrytest.MockClient)
5557
telemetryClient.On("RegisterAppConfigs", []telemetry.Configuration{{Name: EnvSCAEnabled, Value: tc.expectedValue, Origin: telemetry.OriginEnvVar}}).Return()
5658
telemetryClient.On("RegisterAppConfig", EnvSCAEnabled, tc.expectedValue, telemetry.OriginEnvVar).Return()
59+
60+
var logMatcher any
5761
if tc.telemetryLog != "" {
58-
telemetryClient.On("Log", telemetry.LogError, tc.telemetryLog, []telemetry.LogOption(nil)).Return()
62+
logMatcher = mock.MatchedBy(func(record telemetry.Record) bool {
63+
return strings.HasPrefix(record.Message, tc.telemetryLog)
64+
})
65+
telemetryClient.On("Log", logMatcher, []telemetry.LogOption(nil)).Return()
5966
}
6067
defer telemetry.MockClient(telemetryClient)()
6168

@@ -68,7 +75,7 @@ func TestSCAEnabled(t *testing.T) {
6875
telemetryClient.AssertNumberOfCalls(t, "RegisterAppConfigs", 0)
6976
}
7077
if tc.telemetryLog != "" {
71-
telemetryClient.AssertCalled(t, "Log", telemetry.LogError, tc.telemetryLog, []telemetry.LogOption(nil))
78+
telemetryClient.AssertCalled(t, "Log", logMatcher, []telemetry.LogOption(nil))
7279
}
7380
})
7481
}

0 commit comments

Comments
 (0)