Skip to content

Commit

Permalink
Merge pull request #2939 from redpanda-data/telemetry
Browse files Browse the repository at this point in the history
Add telemetry to our builds
  • Loading branch information
Jeffail authored Oct 16, 2024
2 parents 5dc5c0e + 6283e43 commit d7128c4
Show file tree
Hide file tree
Showing 15 changed files with 392 additions and 18 deletions.
14 changes: 14 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,13 @@ jobs:
- name: Release Notes
run: ./resources/scripts/release_notes.sh > ./release_notes.md

- name: Write telemetry private key
env:
CONNECT_TELEMETRY_PRIV_KEY: ${{ secrets.TELEMETRY_PRIVATE_KEY }}
run: |
git update-index --skip-worktree ./internal/telemetry/key.pem
echo "$CONNECT_TELEMETRY_PRIV_KEY" > ./internal/telemetry/key.pem
- uses: actions/setup-python@v5
with:
python-version: '3.12'
Expand Down Expand Up @@ -131,6 +138,13 @@ jobs:
id: buildx
uses: docker/setup-buildx-action@v3

- name: Write telemetry private key
env:
CONNECT_TELEMETRY_PRIV_KEY: ${{ secrets.TELEMETRY_PRIVATE_KEY }}
run: |
git update-index --skip-worktree ./internal/telemetry/key.pem
echo "$CONNECT_TELEMETRY_PRIV_KEY" > ./internal/telemetry/key.pem
- name: Docker meta
id: docker_meta
uses: docker/metadata-action@v5
Expand Down
6 changes: 6 additions & 0 deletions .github/workflows/upload_plugin.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ jobs:
with:
go-version: 1.22.x
check-latest: true
- name: Write telemetry private key
env:
CONNECT_TELEMETRY_PRIV_KEY: ${{ secrets.TELEMETRY_PRIVATE_KEY }}
run: |
git update-index --skip-worktree ./internal/telemetry/key.pem
echo "$CONNECT_TELEMETRY_PRIV_KEY" > ./internal/telemetry/key.pem
- name: Build binaries (dry run / snapshot mode)
if: ${{ env.DRY_RUN != 'false' }}
uses: goreleaser/goreleaser-action@v6
Expand Down
5 changes: 4 additions & 1 deletion .goreleaser.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ builds:
-X main.Version={{.Version}}
-X main.DateBuilt={{.Date}}
-X main.BinaryName=redpanda-connect
-X github.com/redpanda-data/connect/v4/internal/telemetry.ExportHost={{ if index .Env "CONNECT_TELEMETRY_HOST" }}{{ .Env.CONNECT_TELEMETRY_HOST }}{{ else }}{{ end }}
-X github.com/redpanda-data/connect/v4/internal/telemetry.ExportDelay={{ if index .Env "CONNECT_TELEMETRY_DELAY" }}{{ .Env.CONNECT_TELEMETRY_DELAY }}{{ else }}{{ end }}
-X github.com/redpanda-data/connect/v4/internal/telemetry.ExportPeriod={{ if index .Env "CONNECT_TELEMETRY_PERIOD" }}{{ .Env.CONNECT_TELEMETRY_PERIOD }}{{ else }}{{ end }}
- id: connect-cloud
main: cmd/redpanda-connect-cloud/main.go
Expand Down Expand Up @@ -122,4 +125,4 @@ publishers:
- connect-linux-pkgs
cmd: ./resources/scripts/push_pkg_to_cloudsmith.sh {{ .ArtifactPath }}
env:
- CLOUDSMITH_API_KEY={{ .Env.CLOUDSMITH_API_KEY }}
- CLOUDSMITH_API_KEY={{ .Env.CLOUDSMITH_API_KEY }}
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ All notable changes to this project will be documented in this file.

### Added

- Anonymous telemetry data is now sent by Connect instances after running for >5 mins. Details about which data is sent, when it is sent, and how to disable it can be found in the [telemetry README](./internal/telemetry/README.md). (@Jeffail)
- Field `checksum_algorithm` added to the `aws_s3` output. (@dom-lee-naimuri)
- Field `nkey` added to `nats`, `nats_jetstream`, `nats_kv` and `nats_stream` components. (@ye11ow)
- Field `private_key` added to the `snowflake_put` output. (@mihaitodor)
Expand Down
6 changes: 4 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ require (
github.com/generikvault/gvalstrings v0.0.0-20180926130504-471f38f0112a
github.com/getsentry/sentry-go v0.28.1
github.com/go-faker/faker/v4 v4.4.2
github.com/go-jose/go-jose/v3 v3.0.3
github.com/go-resty/resty/v2 v2.15.3
github.com/go-sql-driver/mysql v1.8.1
github.com/gocql/gocql v1.6.0
github.com/gofrs/uuid v4.4.0+incompatible
Expand Down Expand Up @@ -99,7 +101,7 @@ require (
github.com/rabbitmq/amqp091-go v1.10.0
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475
github.com/redis/go-redis/v9 v9.6.1
github.com/redpanda-data/benthos/v4 v4.37.0
github.com/redpanda-data/benthos/v4 v4.39.0
github.com/redpanda-data/connect/public/bundle/free/v4 v4.31.0
github.com/rs/xid v1.5.0
github.com/sashabaranov/go-openai v1.28.3
Expand Down Expand Up @@ -376,7 +378,7 @@ require (
golang.org/x/oauth2 v0.22.0 // indirect
golang.org/x/sys v0.24.0 // indirect
golang.org/x/term v0.23.0 // indirect
golang.org/x/time v0.5.0 // indirect
golang.org/x/time v0.6.0 // indirect
golang.org/x/tools v0.24.0 // indirect
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
google.golang.org/genproto v0.0.0-20240708141625-4ad9e859172b // indirect
Expand Down
12 changes: 8 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,8 @@ github.com/go-fonts/stix v0.1.0/go.mod h1:w/c1f0ldAUlJmLBvlbkvVXLAD+tAMqobIIQpmn
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-jose/go-jose/v3 v3.0.3 h1:fFKWeig/irsp7XD2zBxvnmA/XaRWp5V3CBsZXJF7G7k=
github.com/go-jose/go-jose/v3 v3.0.3/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
Expand All @@ -486,6 +488,8 @@ github.com/go-pg/zerochecker v0.2.0 h1:pp7f72c3DobMWOb2ErtZsnrPaSvHd2W4o9//8HtF4
github.com/go-pg/zerochecker v0.2.0/go.mod h1:NJZ4wKL0NmTtz0GKCoJ8kym6Xn/EQzXRl2OnAe7MmDo=
github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI=
github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow=
github.com/go-resty/resty/v2 v2.15.3 h1:bqff+hcqAflpiF591hhJzNdkRsFhlB96CYfBwSFvql8=
github.com/go-resty/resty/v2 v2.15.3/go.mod h1:0fHAoK7JoBy/Ch36N8VFeMsK7xQOHhvWaC3iOktwmIU=
github.com/go-sourcemap/sourcemap v2.1.4+incompatible h1:a+iTbH5auLKxaNwQFg0B+TCYl6lbukKPc7b5x0n1s6Q=
github.com/go-sourcemap/sourcemap v2.1.4+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
Expand Down Expand Up @@ -1018,8 +1022,8 @@ github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5X
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/redis/go-redis/v9 v9.6.1 h1:HHDteefn6ZkTtY5fGUE8tj8uy85AHk6zP7CpzIAM0y4=
github.com/redis/go-redis/v9 v9.6.1/go.mod h1:0C0c6ycQsdpVNQpxb1njEQIqkx5UcsM8FJCQLgE9+RA=
github.com/redpanda-data/benthos/v4 v4.37.0 h1:/tRHEkpguH4qjbAqbOuVJS75b9IJ7fYeayCw3iUb3LA=
github.com/redpanda-data/benthos/v4 v4.37.0/go.mod h1:A5izknIGyzs16rCU0qliFVgdCLn2yyvLM4Hltx+s+TI=
github.com/redpanda-data/benthos/v4 v4.39.0 h1:vQUTgI7xNibq11sc9X92RRjP9+VoFmVSTSau37BT8EU=
github.com/redpanda-data/benthos/v4 v4.39.0/go.mod h1:A5izknIGyzs16rCU0qliFVgdCLn2yyvLM4Hltx+s+TI=
github.com/redpanda-data/connect/public/bundle/free/v4 v4.31.0 h1:Qiz4Q8ZO17n8797hgDdJ2f1XN7wh6J2hIRgeeSw4F24=
github.com/redpanda-data/connect/public/bundle/free/v4 v4.31.0/go.mod h1:ISgO+/kuuSW0Z7sJo1rWe/rYKIv1rDPHTQ/bSLQEog0=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
Expand Down Expand Up @@ -1487,8 +1491,8 @@ golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxb
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
Expand Down
12 changes: 9 additions & 3 deletions internal/cli/enterprise.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,17 @@ import (
"github.com/rs/xid"

"github.com/redpanda-data/connect/v4/internal/impl/kafka/enterprise"
"github.com/redpanda-data/connect/v4/internal/telemetry"
)

// InitEnterpriseCLI kicks off the benthos cli with a suite of options that adds
// all of the enterprise functionality of Redpanda Connect. This has been
// abstracted into a separate package so that multiple distributions (classic
// versus cloud) can reference the same code.
func InitEnterpriseCLI(binaryName, version, dateBuilt string, schema *service.ConfigSchema, opts ...service.CLIOptFunc) {
rpLogger := enterprise.NewTopicLogger(xid.New().String())
instanceID := xid.New().String()

rpLogger := enterprise.NewTopicLogger(instanceID)
var fbLogger *service.Logger

opts = append(opts,
Expand Down Expand Up @@ -57,8 +60,11 @@ func InitEnterpriseCLI(binaryName, version, dateBuilt string, schema *service.Co
rpLogger.SetFallbackLogger(l)
}),
service.CLIOptAddTeeLogger(slog.New(rpLogger)),
service.CLIOptOnConfigParse(func(fn *service.ParsedConfig) error {
return rpLogger.InitOutputFromParsed(fn.Namespace("redpanda"))
service.CLIOptOnConfigParse(func(pConf *service.ParsedConfig) error {
// Kick off telemetry exporter.
telemetry.ActivateExporter(instanceID, version, fbLogger, schema, pConf)

return rpLogger.InitOutputFromParsed(pConf.Namespace("redpanda"))
}),
service.CLIOptOnStreamStart(func(s *service.RunningStreamSummary) error {
rpLogger.SetStreamSummary(s)
Expand Down
1 change: 1 addition & 0 deletions internal/impl/kafka/enterprise/schema_registry_input.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"sync"

"github.com/redpanda-data/benthos/v4/public/service"

"github.com/redpanda-data/connect/v4/internal/impl/confluent/sr"
)

Expand Down
1 change: 1 addition & 0 deletions internal/impl/kafka/enterprise/schema_registry_output.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"sync/atomic"

"github.com/redpanda-data/benthos/v4/public/service"

"github.com/redpanda-data/connect/v4/internal/impl/confluent/sr"
)

Expand Down
16 changes: 8 additions & 8 deletions internal/impl/prometheus/metrics_prometheus.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,17 +80,17 @@ If the Push Gateway requires HTTP Basic Authentication it can be configured with
Default(0.0),
).
Description("A list of timing metrics summary buckets (as quantiles). Applicable when `use_histogram_timing` is set to `false`.").
Example([]map[string]float64{
{"quantile": 0.5, "error": 0.05},
{"quantile": 0.9, "error": 0.01},
{"quantile": 0.99, "error": 0.001},
Example([]any{
map[string]any{"quantile": 0.5, "error": 0.05},
map[string]any{"quantile": 0.9, "error": 0.01},
map[string]any{"quantile": 0.99, "error": 0.001},
}).
Advanced().
Version("4.23.0").
Default([]map[string]float64{
{"quantile": 0.5, "error": 0.05},
{"quantile": 0.9, "error": 0.01},
{"quantile": 0.99, "error": 0.001},
Default([]any{
map[string]any{"quantile": 0.5, "error": 0.05},
map[string]any{"quantile": 0.9, "error": 0.01},
map[string]any{"quantile": 0.99, "error": 0.001},
}),
service.NewBoolField(pmFieldAddProcessMetrics).
Description("Whether to export process metrics such as CPU and memory usage in addition to Redpanda Connect metrics.").
Expand Down
44 changes: 44 additions & 0 deletions internal/telemetry/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
Telemetry
=========

## What is this for?

Our main goal is to find out the frequency with which each plugin is used in production environments, as this helps us prioritise enhancements and bug fixes for various plugin families on our roadmap.

Ideally, we'd also like to identify common patterns in plugin usage that may help us plan new work or identify gaps in our functionality. For example, if we were to see that almost all `aws_s3` outputs were paired with a `mutation` processor then we might conclude that embedding a mutation field into the plugin itself could be a useful feature.

## What is being sent?

When a Redpanda Connect instance exports telemetry data to our collection server it sends a JSON payload that contains a high-level and anonymous summary of the contents of the config file being executed. Specific field values are never transmitted, nor are decorations of the config such as label names. For example, with an instance running the following config:

```yaml
input:
label: fooer
generate:
interval: 1s
mapping: 'root.foo = "bar"'

output:
label: bazer
aws_s3:
bucket: baz
path: meow.txt
```
We would extract the following information:
- A unique identifier for the Redpanda Connect instance.
- The duration for which the config has been running thus far.
- That the config contains a `generate` input and an `aws_s3` output.
- The IP address of the running Redpanda Connect instance (as a byproduct of the data delivery mechanism).

The code responsible for extracting this data is simple enough to dig into, and we encourage curious users to do so. A good place to start is the data format, which can be found at [`./payload.go`](./payload.go).

## When is it sent?

Telemetry data is sent from an instance of Redpanda Connect that has been running for at least 5 minutes, this is in order to avoid sending data from instances used for testing or experimentation. Once telemetry data starts being emitted it is sent once every 24 hours.

## How do I avoid it?

Any custom build of Redpanda Connect will not send this data, as it is only included in the build artifacts published by us either through Github releases or our official Docker images. You can also prevent telemetry by blocking the internet traffic, Redpanda Connect will continue operating as normal if it is unable to deliver telemetry data.

Empty file added internal/telemetry/key.pem
Empty file.
33 changes: 33 additions & 0 deletions internal/telemetry/logger.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright 2024 Redpanda Data, Inc.
//
// Licensed 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 telemetry

import "github.com/redpanda-data/benthos/v4/public/service"

type logWrapper struct {
l *service.Logger
}

func (l *logWrapper) Errorf(format string, v ...interface{}) {
l.l.With("component", "resty").Debugf(format, v...)
}

func (l *logWrapper) Warnf(format string, v ...interface{}) {
// Ignore
}

func (l *logWrapper) Debugf(format string, v ...interface{}) {
// Ignore
}
84 changes: 84 additions & 0 deletions internal/telemetry/payload.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Copyright 2024 Redpanda Data, Inc.
//
// Licensed 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 telemetry

import (
"fmt"
"time"

"github.com/redpanda-data/benthos/v4/public/service"
)

// Information gathered from each component present in the running config.
type componentInfo struct {
// The type (input, output, etc) of the plugin.
Type string `json:"type"`

// The name (aws_s3, generate, etc) of the plugin.
Name string `json:"name"`
}

// Contains all of the information which is delivered during a telemetry
// export, serialisable in JSON format.
type payload struct {
// A unique identifier for the Redpanda Connect instance.
ID string `json:"id"`

// Uptime of the Redpanda Connect instance.
Uptime int64 `json:"uptime"`

// A slice representing each component within a config.
Components []componentInfo `json:"components"`
}

// All information sent during a telemetry export is extracted within this
// function and stored within the payload.
func extractPayload(identifier string, logger *service.Logger, schema *service.ConfigSchema, conf *service.ParsedConfig) (*payload, error) {
p := payload{ID: identifier, Uptime: 0}

rootValue, err := conf.FieldAny()
if err != nil {
return nil, fmt.Errorf("failed to obtain root of config: %w", err)
}

if err := schema.NewStreamConfigWalker().WalkComponentsAny(rootValue, func(w *service.WalkedComponent) error {
p.Components = append(p.Components, componentInfo{
Type: w.ComponentType,
Name: w.Name,
})
return nil
}); err != nil {
logger.With("error", err).Debug("Failed to walk config")
}

return &p, nil
}

// This function runs asynchronously and is solely where telemetry data is
// exported.
func exporterLoop(p *payload, exportDelay, exportPeriod time.Duration, exporter *telemetryExporter) {
started := time.Now()

// First, wait until after the export delay has passed.
time.Sleep(exportDelay)

for {
p.Uptime = int64(time.Since(started) / time.Second)
exporter.export(p)

// Now wait for the next export.
time.Sleep(exportPeriod)
}
}
Loading

0 comments on commit d7128c4

Please sign in to comment.