lib/telemetrygateway: publish all non-Sourcegraph-specific Telemetry Gateway bindings (#62061)

Migrates the Telemetry Gateway:

1. Service specification
2. Generated Go bindings
3. UUID constructor

into an exported `lib/telemetrygateway` package for internal and external consumption. See https://github.com/sourcegraph/sourcegraph/issues/61489 for use cases. This allows MSP services to more easily start publishing events for to Telemetry Gateway, and adds no new dependencies to `lib`.

Splits Sourcegraph-specific functionality that used to live in the `telemetrygateway/v1` package to:

1. `internal/telemetrygateway`: backcompat testing
2. `internal/telmeetrygateway/event`: event constructors (collapsing into parent caused import cycle)

I've left README + a stub service spec in the old package to redirect visitors from outdated links.

Closes https://github.com/sourcegraph/sourcegraph/issues/61489

## Test plan

```
sg start
```

watch for successful export logs from `telemetrygatewayexporter`
This commit is contained in:
Robert Lin 2024-04-22 14:36:46 -07:00 committed by GitHub
parent 8becda4424
commit fce0faf66f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
69 changed files with 465 additions and 426 deletions

View File

@ -17,10 +17,11 @@ go_library(
"//internal/database",
"//internal/gqlutil",
"//internal/telemetry/teestore",
"//internal/telemetrygateway/v1:telemetrygateway",
"//internal/telemetrygateway/event",
"//internal/version",
"//lib/errors",
"//lib/pointers",
"//lib/telemetrygateway/v1:telemetrygateway",
"@com_github_graph_gophers_graphql_go//:graphql-go",
"@com_github_graph_gophers_graphql_go//relay",
"@com_github_sourcegraph_log//:log",
@ -49,8 +50,8 @@ go_test(
"//internal/database/dbtest",
"//internal/database/fakedb",
"//internal/gqlutil",
"//internal/telemetrygateway/v1:telemetrygateway",
"//internal/types",
"//lib/telemetrygateway/v1:telemetrygateway",
"@com_github_hexops_autogold_v2//:autogold",
"@com_github_sourcegraph_log//logtest",
"@com_github_stretchr_testify//assert",

View File

@ -11,8 +11,8 @@ import (
"github.com/sourcegraph/sourcegraph/internal/auth"
"github.com/sourcegraph/sourcegraph/internal/database"
"github.com/sourcegraph/sourcegraph/internal/telemetry/teestore"
telemetrygatewayv1 "github.com/sourcegraph/sourcegraph/internal/telemetrygateway/v1"
"github.com/sourcegraph/sourcegraph/lib/errors"
telemetrygatewayv1 "github.com/sourcegraph/sourcegraph/lib/telemetrygateway/v1"
)
// Resolver is the GraphQL resolver of all things related to telemetry V2.

View File

@ -17,8 +17,8 @@ import (
"github.com/sourcegraph/sourcegraph/internal/database/dbmocks"
"github.com/sourcegraph/sourcegraph/internal/database/dbtest"
"github.com/sourcegraph/sourcegraph/internal/database/fakedb"
telemetrygatewayv1 "github.com/sourcegraph/sourcegraph/internal/telemetrygateway/v1"
"github.com/sourcegraph/sourcegraph/internal/types"
telemetrygatewayv1 "github.com/sourcegraph/sourcegraph/lib/telemetrygateway/v1"
)
func TestExportedEvents(t *testing.T) {

View File

@ -8,9 +8,11 @@ import (
"google.golang.org/protobuf/types/known/timestamppb"
"github.com/sourcegraph/sourcegraph/cmd/frontend/graphqlbackend"
telemetrygatewayv1 "github.com/sourcegraph/sourcegraph/internal/telemetrygateway/v1"
"github.com/sourcegraph/sourcegraph/internal/version"
"github.com/sourcegraph/sourcegraph/lib/errors"
telemetrygatewayevent "github.com/sourcegraph/sourcegraph/internal/telemetrygateway/event"
telemetrygatewayv1 "github.com/sourcegraph/sourcegraph/lib/telemetrygateway/v1"
)
// newTelemetryGatewayEvents converts GraphQL telemetry input to the Telemetry
@ -30,7 +32,7 @@ func newTelemetryGatewayEvents(
return nil, errors.Newf("action is required for event %d", i)
}
event := telemetrygatewayv1.NewEventWithDefaults(ctx, now, newUUID)
event := telemetrygatewayevent.New(ctx, now, newUUID)
if gqlEvent.Timestamp != nil {
event.Timestamp = timestamppb.New(gqlEvent.Timestamp.Time)

View File

@ -11,9 +11,9 @@ go_library(
visibility = ["//cmd/telemetry-gateway:__subpackages__"],
deps = [
"//internal/pubsub",
"//internal/telemetrygateway/v1:telemetrygateway",
"//internal/trace",
"//lib/errors",
"//lib/telemetrygateway/v1:telemetrygateway",
"@com_github_cockroachdb_redact//:redact",
"@com_github_sourcegraph_conc//pool",
"@com_github_sourcegraph_log//:log",
@ -32,7 +32,7 @@ go_test(
embed = [":events"],
deps = [
"//internal/pubsub/pubsubtest",
"//internal/telemetrygateway/v1:telemetrygateway",
"//lib/telemetrygateway/v1:telemetrygateway",
"@com_github_hexops_autogold_v2//:autogold",
"@com_github_sourcegraph_log//logtest",
"@com_github_stretchr_testify//assert",

View File

@ -4,7 +4,7 @@ import (
"strconv"
"strings"
telemetrygatewayv1 "github.com/sourcegraph/sourcegraph/internal/telemetrygateway/v1"
telemetrygatewayv1 "github.com/sourcegraph/sourcegraph/lib/telemetrygateway/v1"
)
// extractPubSubAttributes extracts attributes from the event for use in the

View File

@ -6,7 +6,7 @@ import (
"github.com/hexops/autogold/v2"
"google.golang.org/protobuf/types/known/structpb"
telemetrygatewayv1 "github.com/sourcegraph/sourcegraph/internal/telemetrygateway/v1"
telemetrygatewayv1 "github.com/sourcegraph/sourcegraph/lib/telemetrygateway/v1"
)
func TestExtractPubSubAttributes(t *testing.T) {

View File

@ -14,9 +14,9 @@ import (
"github.com/sourcegraph/log"
"github.com/sourcegraph/sourcegraph/internal/pubsub"
telemetrygatewayv1 "github.com/sourcegraph/sourcegraph/internal/telemetrygateway/v1"
"github.com/sourcegraph/sourcegraph/internal/trace"
"github.com/sourcegraph/sourcegraph/lib/errors"
telemetrygatewayv1 "github.com/sourcegraph/sourcegraph/lib/telemetrygateway/v1"
)
type Publisher struct {

View File

@ -16,7 +16,7 @@ import (
"github.com/sourcegraph/sourcegraph/cmd/telemetry-gateway/internal/events"
"github.com/sourcegraph/sourcegraph/internal/pubsub/pubsubtest"
telemetrygatewayv1 "github.com/sourcegraph/sourcegraph/internal/telemetrygateway/v1"
telemetrygatewayv1 "github.com/sourcegraph/sourcegraph/lib/telemetrygateway/v1"
)
func TestPublish(t *testing.T) {

View File

@ -17,9 +17,9 @@ go_library(
"//internal/licensing",
"//internal/pubsub",
"//internal/sams",
"//internal/telemetrygateway/v1:telemetrygateway",
"//internal/trace",
"//lib/errors",
"//lib/telemetrygateway/v1:telemetrygateway",
"@com_github_cockroachdb_redact//:redact",
"@com_github_sourcegraph_log//:log",
"@io_opentelemetry_go_otel//:otel",
@ -39,8 +39,8 @@ go_test(
embed = [":server"],
deps = [
"//cmd/telemetry-gateway/internal/events",
"//internal/telemetrygateway/v1:telemetrygateway",
"//lib/errors",
"//lib/telemetrygateway/v1:telemetrygateway",
"@com_github_hexops_autogold_v2//:autogold",
"@com_github_stretchr_testify//assert",
"@com_github_stretchr_testify//require",

View File

@ -1,6 +1,6 @@
package server
import telemetrygatewayv1 "github.com/sourcegraph/sourcegraph/internal/telemetrygateway/v1"
import telemetrygatewayv1 "github.com/sourcegraph/sourcegraph/lib/telemetrygateway/v1"
// migrateEvents does an in-place migration of any legacy field usages to support
// older exporters.

View File

@ -5,7 +5,7 @@ import (
"github.com/stretchr/testify/assert"
telemetrygatewayv1 "github.com/sourcegraph/sourcegraph/internal/telemetrygateway/v1"
telemetrygatewayv1 "github.com/sourcegraph/sourcegraph/lib/telemetrygateway/v1"
)
func TestMigrateEvents(t *testing.T) {

View File

@ -10,9 +10,9 @@ import (
"go.opentelemetry.io/otel/metric"
"github.com/sourcegraph/sourcegraph/cmd/telemetry-gateway/internal/events"
telemetrygatewayv1 "github.com/sourcegraph/sourcegraph/internal/telemetrygateway/v1"
sgtrace "github.com/sourcegraph/sourcegraph/internal/trace"
"github.com/sourcegraph/sourcegraph/lib/errors"
telemetrygatewayv1 "github.com/sourcegraph/sourcegraph/lib/telemetrygateway/v1"
)
func handlePublishEvents(

View File

@ -10,8 +10,8 @@ import (
"github.com/stretchr/testify/require"
"github.com/sourcegraph/sourcegraph/cmd/telemetry-gateway/internal/events"
telemetrygatewayv1 "github.com/sourcegraph/sourcegraph/internal/telemetrygateway/v1"
"github.com/sourcegraph/sourcegraph/lib/errors"
telemetrygatewayv1 "github.com/sourcegraph/sourcegraph/lib/telemetrygateway/v1"
)
func TestSummarizeFailedEvents(t *testing.T) {

View File

@ -20,7 +20,7 @@ import (
"github.com/sourcegraph/sourcegraph/cmd/telemetry-gateway/internal/events"
"github.com/sourcegraph/sourcegraph/cmd/telemetry-gateway/internal/server/samsm2m"
telemetrygatewayv1 "github.com/sourcegraph/sourcegraph/internal/telemetrygateway/v1"
telemetrygatewayv1 "github.com/sourcegraph/sourcegraph/lib/telemetrygateway/v1"
)
type Server struct {

View File

@ -17,13 +17,13 @@ go_library(
"//internal/httpserver",
"//internal/pubsub",
"//internal/sams",
"//internal/telemetrygateway/v1:telemetrygateway",
"//internal/trace/policy",
"//internal/version",
"//lib/background",
"//lib/errors",
"//lib/managedservicesplatform/runtime",
"//lib/managedservicesplatform/runtime/contract",
"//lib/telemetrygateway/v1:telemetrygateway",
"@com_github_sourcegraph_log//:log",
"@io_opentelemetry_go_otel//:otel",
"@io_opentelemetry_go_otel_metric//:metric",

View File

@ -28,7 +28,7 @@ import (
"github.com/sourcegraph/sourcegraph/cmd/telemetry-gateway/internal/events"
"github.com/sourcegraph/sourcegraph/cmd/telemetry-gateway/internal/server"
telemetrygatewayv1 "github.com/sourcegraph/sourcegraph/internal/telemetrygateway/v1"
telemetrygatewayv1 "github.com/sourcegraph/sourcegraph/lib/telemetrygateway/v1"
)
var meter = otel.GetMeterProvider().Meter("cmd/telemetry-gateway/service")

View File

@ -2595,8 +2595,8 @@ def go_dependencies():
name = "com_github_google_uuid",
build_file_proto_mode = "disable_global",
importpath = "github.com/google/uuid",
sum = "h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=",
version = "v1.5.0",
sum = "h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=",
version = "v1.6.0",
)
go_repository(
name = "com_github_googleapis_enterprise_certificate_proxy",

View File

@ -13,7 +13,7 @@ filegroup(
write_generated_to_source_files(
name = "write_telemetrygateway_doc",
output_files = {"protocol.md": "internal/telemetrygateway/v1/protocol.md/protocol.md"},
output_files = {"protocol.md": "lib/telemetrygateway/v1/protocol.md/protocol.md"},
tags = ["go_generate"],
target = "//internal/telemetrygateway/v1:protocol.md",
target = "//lib/telemetrygateway/v1:protocol.md",
)

2
go.mod
View File

@ -116,7 +116,7 @@ require (
github.com/gomodule/redigo v2.0.0+incompatible
github.com/google/go-cmp v0.6.0
github.com/google/go-querystring v1.1.0
github.com/google/uuid v1.5.0
github.com/google/uuid v1.6.0
github.com/gorilla/context v1.1.1
github.com/gorilla/mux v1.8.0
github.com/gorilla/schema v1.2.0

4
go.sum
View File

@ -941,8 +941,8 @@ github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs=
github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0=
github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=

View File

@ -138,7 +138,6 @@ go_library(
"//internal/search/result",
"//internal/security",
"//internal/telemetry/sensitivemetadataallowlist",
"//internal/telemetrygateway/v1:telemetrygateway",
"//internal/temporarysettings",
"//internal/timeutil",
"//internal/trace",
@ -146,6 +145,7 @@ go_library(
"//internal/version",
"//lib/errors",
"//lib/pointers",
"//lib/telemetrygateway/v1:telemetrygateway",
"//schema",
"@com_github_gofrs_uuid//:uuid",
"@com_github_google_uuid//:uuid",
@ -292,7 +292,6 @@ go_test(
"//internal/perforce",
"//internal/rbac/types",
"//internal/search/result",
"//internal/telemetrygateway/v1:telemetrygateway",
"//internal/temporarysettings",
"//internal/timeutil",
"//internal/trace",
@ -302,6 +301,7 @@ go_test(
"//internal/version",
"//lib/errors",
"//lib/pointers",
"//lib/telemetrygateway/v1:telemetrygateway",
"//schema",
"@com_github_cockroachdb_errors//errbase",
"@com_github_gitchander_permutation//:permutation",

View File

@ -19,9 +19,9 @@ go_library(
"//internal/github_apps/store",
"//internal/own/types",
"//internal/search/result",
"//internal/telemetrygateway/v1:telemetrygateway",
"//internal/temporarysettings",
"//internal/types",
"//lib/telemetrygateway/v1:telemetrygateway",
"//schema",
"@com_github_google_uuid//:uuid",
"@com_github_keegancsmith_sqlf//:sqlf",

View File

@ -27,9 +27,9 @@ import (
store "github.com/sourcegraph/sourcegraph/internal/github_apps/store"
types1 "github.com/sourcegraph/sourcegraph/internal/own/types"
result "github.com/sourcegraph/sourcegraph/internal/search/result"
v1 "github.com/sourcegraph/sourcegraph/internal/telemetrygateway/v1"
temporarysettings "github.com/sourcegraph/sourcegraph/internal/temporarysettings"
types "github.com/sourcegraph/sourcegraph/internal/types"
v1 "github.com/sourcegraph/sourcegraph/lib/telemetrygateway/v1"
schema "github.com/sourcegraph/sourcegraph/schema"
zoekt "github.com/sourcegraph/zoekt"
)

View File

@ -21,9 +21,9 @@ import (
"github.com/sourcegraph/sourcegraph/internal/database/batch"
"github.com/sourcegraph/sourcegraph/internal/licensing"
"github.com/sourcegraph/sourcegraph/internal/telemetry/sensitivemetadataallowlist"
telemetrygatewayv1 "github.com/sourcegraph/sourcegraph/internal/telemetrygateway/v1"
"github.com/sourcegraph/sourcegraph/internal/trace"
"github.com/sourcegraph/sourcegraph/lib/errors"
telemetrygatewayv1 "github.com/sourcegraph/sourcegraph/lib/telemetrygateway/v1"
)
var counterQueuedEvents = promauto.NewCounterVec(prometheus.CounterOpts{

View File

@ -15,7 +15,7 @@ import (
"github.com/sourcegraph/sourcegraph/internal/database/dbtest"
"github.com/sourcegraph/sourcegraph/internal/licensing"
telemetrygatewayv1 "github.com/sourcegraph/sourcegraph/internal/telemetrygateway/v1"
telemetrygatewayv1 "github.com/sourcegraph/sourcegraph/lib/telemetrygateway/v1"
)
func TestTelemetryEventsExportQueue_QueueForExport(t *testing.T) {

View File

@ -14,10 +14,11 @@ go_library(
importpath = "github.com/sourcegraph/sourcegraph/internal/telemetry",
visibility = ["//:__subpackages__"],
deps = [
"//internal/telemetrygateway/v1:telemetrygateway",
"//internal/telemetrygateway/event",
"//internal/trace",
"//internal/version",
"//lib/errors",
"//lib/telemetrygateway/v1:telemetrygateway",
"@com_github_sourcegraph_log//:log",
"@org_golang_google_protobuf//types/known/structpb",
],

View File

@ -13,8 +13,8 @@ go_library(
"//internal/dotcom",
"//internal/env",
"//internal/telemetry",
"//internal/telemetrygateway/v1:telemetrygateway",
"//lib/errors",
"//lib/telemetrygateway/v1:telemetrygateway",
],
)
@ -27,8 +27,8 @@ go_test(
embed = [":sensitivemetadataallowlist"],
deps = [
"//internal/telemetry",
"//internal/telemetrygateway/v1:telemetrygateway",
"//lib/pointers",
"//lib/telemetrygateway/v1:telemetrygateway",
"@com_github_hexops_autogold_v2//:autogold",
"@com_github_stretchr_testify//assert",
"@com_github_stretchr_testify//require",

View File

@ -1,7 +1,7 @@
package sensitivemetadataallowlist
import (
telemetrygatewayv1 "github.com/sourcegraph/sourcegraph/internal/telemetrygateway/v1"
telemetrygatewayv1 "github.com/sourcegraph/sourcegraph/lib/telemetrygateway/v1"
)
// redactMode dictates how much to redact. The lowest value indicates our

View File

@ -6,8 +6,8 @@ import (
"github.com/stretchr/testify/assert"
"google.golang.org/protobuf/types/known/structpb"
telemetrygatewayv1 "github.com/sourcegraph/sourcegraph/internal/telemetrygateway/v1"
"github.com/sourcegraph/sourcegraph/lib/pointers"
telemetrygatewayv1 "github.com/sourcegraph/sourcegraph/lib/telemetrygateway/v1"
)
func TestRedactEvent(t *testing.T) {

View File

@ -9,7 +9,7 @@ import (
"github.com/sourcegraph/sourcegraph/internal/dotcom"
"github.com/sourcegraph/sourcegraph/internal/env"
"github.com/sourcegraph/sourcegraph/internal/telemetry"
telemetrygatewayv1 "github.com/sourcegraph/sourcegraph/internal/telemetrygateway/v1"
telemetrygatewayv1 "github.com/sourcegraph/sourcegraph/lib/telemetrygateway/v1"
)
var rawAdditionalAllowedEventTypes = env.Get("SRC_TELEMETRY_SENSITIVEMETADATA_ADDITIONAL_ALLOWED_EVENT_TYPES", "",

View File

@ -8,7 +8,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/sourcegraph/sourcegraph/internal/telemetry"
v1 "github.com/sourcegraph/sourcegraph/internal/telemetrygateway/v1"
v1 "github.com/sourcegraph/sourcegraph/lib/telemetrygateway/v1"
)
func TestIsAllowed(t *testing.T) {

View File

@ -13,9 +13,9 @@ go_library(
"//internal/database",
"//internal/featureflag",
"//internal/telemetry/sensitivemetadataallowlist",
"//internal/telemetrygateway/v1:telemetrygateway",
"//lib/errors",
"//lib/pointers",
"//lib/telemetrygateway/v1:telemetrygateway",
"@com_github_prometheus_client_golang//prometheus",
"@com_github_prometheus_client_golang//prometheus/promauto",
"@com_github_sourcegraph_conc//pool",
@ -33,8 +33,8 @@ go_test(
deps = [
"//internal/database",
"//internal/database/dbtest",
"//internal/telemetrygateway/v1:telemetrygateway",
"//lib/pointers",
"//lib/telemetrygateway/v1:telemetrygateway",
"@com_github_hexops_autogold_v2//:autogold",
"@com_github_sourcegraph_log//logtest",
"@com_github_stretchr_testify//assert",

View File

@ -14,9 +14,9 @@ import (
"github.com/sourcegraph/sourcegraph/internal/database"
"github.com/sourcegraph/sourcegraph/internal/featureflag"
"github.com/sourcegraph/sourcegraph/internal/telemetry/sensitivemetadataallowlist"
telemetrygatewayv1 "github.com/sourcegraph/sourcegraph/internal/telemetrygateway/v1"
"github.com/sourcegraph/sourcegraph/lib/errors"
"github.com/sourcegraph/sourcegraph/lib/pointers"
telemetrygatewayv1 "github.com/sourcegraph/sourcegraph/lib/telemetrygateway/v1"
)
var counterV1Events = promauto.NewCounterVec(prometheus.CounterOpts{

View File

@ -15,8 +15,8 @@ import (
"github.com/sourcegraph/sourcegraph/internal/database"
"github.com/sourcegraph/sourcegraph/internal/database/dbtest"
telemetrygatewayv1 "github.com/sourcegraph/sourcegraph/internal/telemetrygateway/v1"
"github.com/sourcegraph/sourcegraph/lib/pointers"
telemetrygatewayv1 "github.com/sourcegraph/sourcegraph/lib/telemetrygateway/v1"
)
// see TestRecorderEndToEnd for tests that include teestore.Store and the database

View File

@ -7,8 +7,8 @@ import (
"context"
"time"
telemetrygatewayv1 "github.com/sourcegraph/sourcegraph/internal/telemetrygateway/v1"
"github.com/sourcegraph/sourcegraph/lib/errors"
telemetrygatewayv1 "github.com/sourcegraph/sourcegraph/lib/telemetrygateway/v1"
)
// constString effectively requires strings to be statically defined constants.

View File

@ -6,8 +6,10 @@ import (
"google.golang.org/protobuf/types/known/structpb"
telemetrygatewayv1 "github.com/sourcegraph/sourcegraph/internal/telemetrygateway/v1"
"github.com/sourcegraph/sourcegraph/internal/version"
telemetrygatewayevent "github.com/sourcegraph/sourcegraph/internal/telemetrygateway/event"
telemetrygatewayv1 "github.com/sourcegraph/sourcegraph/lib/telemetrygateway/v1"
)
// newTelemetryGatewayEvent translates recording to raw events for storage and
@ -26,7 +28,7 @@ func newTelemetryGatewayEvent(
parameters = &EventParameters{}
}
event := telemetrygatewayv1.NewEventWithDefaults(ctx, now, newUUID)
event := telemetrygatewayevent.New(ctx, now, newUUID)
event.Feature = string(feature)
event.Action = string(action)
event.Source = &telemetrygatewayv1.EventSource{

View File

@ -14,7 +14,7 @@ go_library(
"//internal/database",
"//internal/database/dbmocks",
"//internal/telemetry",
"//internal/telemetrygateway/v1:telemetrygateway",
"//lib/telemetrygateway/v1:telemetrygateway",
"@com_github_sourcegraph_log//logtest",
],
)
@ -25,7 +25,7 @@ go_test(
embed = [":telemetrytest"],
deps = [
"//internal/telemetry",
"//internal/telemetrygateway/v1:telemetrygateway",
"//lib/telemetrygateway/v1:telemetrygateway",
"@com_github_hexops_autogold_v2//:autogold",
"@com_github_sourcegraph_log//logtest",
"@com_github_stretchr_testify//require",

View File

@ -11,7 +11,7 @@ import (
"sync"
telemetry "github.com/sourcegraph/sourcegraph/internal/telemetry"
v1 "github.com/sourcegraph/sourcegraph/internal/telemetrygateway/v1"
v1 "github.com/sourcegraph/sourcegraph/lib/telemetrygateway/v1"
)
// MockEventsStore is a mock implementation of the EventsStore interface

View File

@ -11,8 +11,8 @@ import (
"github.com/sourcegraph/sourcegraph/internal/database/dbmocks"
"github.com/sourcegraph/sourcegraph/internal/telemetry"
telemetrygatewayv1 "github.com/sourcegraph/sourcegraph/internal/telemetrygateway/v1"
v1 "github.com/sourcegraph/sourcegraph/internal/telemetrygateway/v1"
telemetrygatewayv1 "github.com/sourcegraph/sourcegraph/lib/telemetrygateway/v1"
v1 "github.com/sourcegraph/sourcegraph/lib/telemetrygateway/v1"
)
// NewRecorder is an simple alias that provides a *telemetry.EventRecorder and

View File

@ -9,8 +9,9 @@ import (
"github.com/stretchr/testify/require"
"github.com/sourcegraph/log/logtest"
"github.com/sourcegraph/sourcegraph/internal/telemetry"
telemetrygatewayv1 "github.com/sourcegraph/sourcegraph/internal/telemetrygateway/v1"
telemetrygatewayv1 "github.com/sourcegraph/sourcegraph/lib/telemetrygateway/v1"
)
func TestFakeTelemetryEventsExportQueueStore(t *testing.T) {

View File

@ -15,9 +15,9 @@ go_library(
"//internal/env",
"//internal/grpc/chunk",
"//internal/grpc/defaults",
"//internal/telemetrygateway/v1:telemetrygateway",
"//internal/trace",
"//lib/errors",
"//lib/telemetrygateway/v1:telemetrygateway",
"@com_github_google_uuid//:uuid",
"@com_github_sourcegraph_log//:log",
"@io_opentelemetry_go_otel//attribute",
@ -27,16 +27,28 @@ go_library(
go_test(
name = "telemetrygateway_test",
srcs = ["identifier_test.go"],
srcs = [
"backcompat_test.go",
"identifier_test.go",
"main_test.go",
],
data = glob(["testdata/**"]),
embed = [":telemetrygateway"],
deps = [
"//internal/conf",
"//internal/conf/conftypes",
"//internal/database",
"//internal/database/dbmocks",
"//internal/trace/tracetest",
"//lib/pointers",
"//lib/telemetrygateway/v1:telemetrygateway",
"//schema",
"@com_github_hexops_autogold_v2//:autogold",
"@com_github_stretchr_testify//assert",
"@com_github_stretchr_testify//require",
"@org_golang_google_protobuf//encoding/protojson",
"@org_golang_google_protobuf//proto",
"@org_golang_google_protobuf//types/known/structpb",
"@org_golang_google_protobuf//types/known/timestamppb",
],
)

View File

@ -1,4 +1,4 @@
package v1_test
package telemetrygateway_test
import (
"context"
@ -18,8 +18,8 @@ import (
"github.com/sourcegraph/sourcegraph/lib/pointers"
telemetrygatewayv1 "github.com/sourcegraph/sourcegraph/internal/telemetrygateway/v1"
"github.com/sourcegraph/sourcegraph/internal/trace/tracetest"
telemetrygatewayv1 "github.com/sourcegraph/sourcegraph/lib/telemetrygateway/v1"
)
var (

View File

@ -0,0 +1,36 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
load("//dev:go_defs.bzl", "go_test")
go_library(
name = "event",
srcs = ["event.go"],
importpath = "github.com/sourcegraph/sourcegraph/internal/telemetrygateway/event",
visibility = ["//:__subpackages__"],
deps = [
"//internal/actor",
"//internal/featureflag",
"//internal/requestclient",
"//internal/requestinteraction",
"//internal/trace",
"//lib/pointers",
"//lib/telemetrygateway/v1:telemetrygateway",
"@org_golang_google_protobuf//types/known/timestamppb",
],
)
go_test(
name = "event_test",
srcs = ["event_test.go"],
embed = [":event"],
deps = [
"//internal/actor",
"//internal/requestclient",
"//internal/trace",
"//internal/trace/tracetest",
"//lib/telemetrygateway/v1:telemetrygateway",
"@com_github_hexops_autogold_v2//:autogold",
"@com_github_stretchr_testify//assert",
"@com_github_stretchr_testify//require",
"@org_golang_google_protobuf//encoding/protojson",
],
)

View File

@ -1,11 +1,10 @@
package v1
package telemetrygatewayevent
import (
"context"
"strconv"
"time"
"github.com/google/uuid"
"google.golang.org/protobuf/types/known/timestamppb"
"github.com/sourcegraph/sourcegraph/internal/actor"
@ -14,23 +13,23 @@ import (
"github.com/sourcegraph/sourcegraph/internal/requestinteraction"
"github.com/sourcegraph/sourcegraph/internal/trace"
"github.com/sourcegraph/sourcegraph/lib/pointers"
telemetrygatewayv1 "github.com/sourcegraph/sourcegraph/lib/telemetrygateway/v1"
)
// DefaultEventIDFunc is the default generator for telemetry event IDs.
// We currently use V7, which is time-ordered, making them useful for event IDs.
// https://datatracker.ietf.org/doc/html/draft-peabody-dispatch-new-uuid-format-03#name-uuid-version-7
var DefaultEventIDFunc = func() string {
return uuid.Must(uuid.NewV7()).String()
}
var DefaultEventIDFunc = telemetrygatewayv1.DefaultEventIDFunc
// NewEventWithDefaults creates a uniform event with defaults filled in. All
// constructors making raw events should start with this. In particular, this
// adds any relevant data required from context.
func NewEventWithDefaults(ctx context.Context, now time.Time, newEventID func() string) *Event {
return &Event{
// New creates a uniform event with defaults filled in, including any relevant
// data required from context. All constructors making raw events for Sourcegraph
// instances to export MUST start with this.
func New(ctx context.Context, now time.Time, newEventID func() string) *telemetrygatewayv1.Event {
return &telemetrygatewayv1.Event{
Id: newEventID(),
Timestamp: timestamppb.New(now),
Interaction: func() *EventInteraction {
Interaction: func() *telemetrygatewayv1.EventInteraction {
// Trace associated with event is the same trace on the event recording
// request where the event is being created, as they should all happen
// within the interaction, even when recording a set of events e.g. from
@ -47,10 +46,10 @@ func NewEventWithDefaults(ctx context.Context, now time.Time, newEventID func()
}
// Get geolocation of request client, if there is one.
var geolocation *EventInteraction_Geolocation
var geolocation *telemetrygatewayv1.EventInteraction_Geolocation
if rc := requestclient.FromContext(ctx); rc != nil {
if cc, err := rc.OriginCountryCode(); err == nil {
geolocation = &EventInteraction_Geolocation{
geolocation = &telemetrygatewayv1.EventInteraction_Geolocation{
CountryCode: cc,
}
}
@ -62,23 +61,23 @@ func NewEventWithDefaults(ctx context.Context, now time.Time, newEventID func()
return nil
}
return &EventInteraction{
return &telemetrygatewayv1.EventInteraction{
TraceId: traceID,
InteractionId: interactionID,
Geolocation: geolocation,
}
}(),
User: func() *EventUser {
User: func() *telemetrygatewayv1.EventUser {
act := actor.FromContext(ctx)
if !act.IsAuthenticated() && act.AnonymousUID == "" {
return nil
}
return &EventUser{
return &telemetrygatewayv1.EventUser{
UserId: pointers.NonZeroPtr(int64(act.UID)),
AnonymousUserId: pointers.NonZeroPtr(act.AnonymousUID),
}
}(),
FeatureFlags: func() *EventFeatureFlags {
FeatureFlags: func() *telemetrygatewayv1.EventFeatureFlags {
flags := featureflag.GetEvaluatedFlagSet(ctx)
if len(flags) == 0 {
return nil
@ -87,7 +86,7 @@ func NewEventWithDefaults(ctx context.Context, now time.Time, newEventID func()
for k, v := range flags {
data[k] = strconv.FormatBool(v)
}
return &EventFeatureFlags{
return &telemetrygatewayv1.EventFeatureFlags{
Flags: data,
}
}(),

View File

@ -1,4 +1,4 @@
package v1_test
package telemetrygatewayevent
import (
"context"
@ -16,7 +16,7 @@ import (
"github.com/sourcegraph/sourcegraph/internal/trace"
"github.com/sourcegraph/sourcegraph/internal/trace/tracetest"
telemetrygatewayv1 "github.com/sourcegraph/sourcegraph/internal/telemetrygateway/v1"
telemetrygatewayv1 "github.com/sourcegraph/sourcegraph/lib/telemetrygateway/v1"
)
func TestDefaultEventIDFunc(t *testing.T) {
@ -25,13 +25,13 @@ func TestDefaultEventIDFunc(t *testing.T) {
assert.NotEmpty(t, id)
}
func TestNewEventWithDefaults(t *testing.T) {
func TestNew(t *testing.T) {
staticTime, err := time.Parse(time.RFC3339, "2023-02-24T14:48:30Z")
require.NoError(t, err)
t.Run("empty context", func(t *testing.T) {
ctx := context.Background()
got := telemetrygatewayv1.NewEventWithDefaults(ctx, staticTime, func() string { return "id" })
got := New(ctx, staticTime, func() string { return "id" })
assert.Nil(t, got.User)
assert.Nil(t, got.Interaction)
assert.Nil(t, got.FeatureFlags)
@ -51,7 +51,7 @@ func TestNewEventWithDefaults(t *testing.T) {
// is not designed for it to easily be stubbed out for testing.
// Since it's used for existing telemetry, we trust it works.
got := telemetrygatewayv1.NewEventWithDefaults(ctx, staticTime, func() string { return "id" })
got := New(ctx, staticTime, func() string { return "id" })
assert.NotNil(t, got.User)
protodata, err := protojson.Marshal(got)
@ -87,7 +87,7 @@ func TestNewEventWithDefaults(t *testing.T) {
// is not designed for it to easily be stubbed out for testing.
// Since it's used for existing telemetry, we trust it works.
got := telemetrygatewayv1.NewEventWithDefaults(ctx, staticTime, func() string { return "id" })
got := New(ctx, staticTime, func() string { return "id" })
assert.NotNil(t, got.Interaction)
protodata, err := protojson.Marshal(got)

View File

@ -16,9 +16,9 @@ import (
"github.com/sourcegraph/sourcegraph/internal/env"
"github.com/sourcegraph/sourcegraph/internal/grpc/chunk"
"github.com/sourcegraph/sourcegraph/internal/grpc/defaults"
telemetrygatewayv1 "github.com/sourcegraph/sourcegraph/internal/telemetrygateway/v1"
"github.com/sourcegraph/sourcegraph/internal/trace"
"github.com/sourcegraph/sourcegraph/lib/errors"
telemetrygatewayv1 "github.com/sourcegraph/sourcegraph/lib/telemetrygateway/v1"
)
type Exporter interface {

View File

@ -5,8 +5,8 @@ import (
"github.com/sourcegraph/sourcegraph/internal/conf/conftypes"
"github.com/sourcegraph/sourcegraph/internal/database"
telemetrygatewayv1 "github.com/sourcegraph/sourcegraph/internal/telemetrygateway/v1"
"github.com/sourcegraph/sourcegraph/lib/errors"
telemetrygatewayv1 "github.com/sourcegraph/sourcegraph/lib/telemetrygateway/v1"
)
func newIdentifier(ctx context.Context, c conftypes.SiteConfigQuerier, g database.GlobalStateStore) (*telemetrygatewayv1.Identifier, error) {

View File

@ -1,4 +1,4 @@
package v1
package telemetrygateway
import (
"flag"

View File

@ -0,0 +1,3 @@
# THIS SERVICE SPECIFICATION HAS MOVED TO [`lib/telemetrygateway/v1`](../../../lib/telemetrygateway/v1/telemetrygateway.proto)
A symlink to the service specification is maintained here to preserve existing documentation links.

View File

@ -1,285 +0,0 @@
// 🔔 IMPORTANT: Be VERY careful not to introduce breaking changes to this
// spec - raw protocol buffer wire format messages are persisted to database
// as a cache, and Sourcegraph instances rely on this format to emit telemetry
// to the managed Sourcegraph Telemetry Gateway service.
//
// Tests in ./internal/telemetrygateway/v1/backcompat_test.go can be used to
// assert compatibility with snapshots created by older versions of this spec.
syntax = "proto3";
package telemetrygateway.v1;
import "google/protobuf/struct.proto";
import "google/protobuf/timestamp.proto";
option go_package = "github.com/sourcegraph/sourcegraph/internal/telemetrygateway/v1";
service TelemeteryGatewayService {
// RecordEvents streams telemetry events in batches to the Telemetry Gateway
// service. Events should only be considered delivered if recording is
// acknowledged in RecordEventsResponse.
//
// This is the preferred mechanism for exporting large volumes of events in
// bulk.
//
// 🚨 SECURITY: Callers exporting for single-tenant Sourcegraph should check
// the attributes of the Event type to ensure that only the appropriate fields
// are exported, as some fields should only be exported on an allowlist basis.
rpc RecordEvents(stream RecordEventsRequest) returns (stream RecordEventsResponse) {}
// RecordEvent records a single telemetry event to the Telemetry Gateway service.
// If the RPC succeeds, then the event was successfully published.
//
// This RPC currently ONLY accepts events published by ManagedServiceIdentifier,
// as this mechanism is intended for low-volume managed services. Higher-volume
// use cases should implement a batching mechanism and use the RecordEvents
// RPC instead.
//
// 🚨 SECURITY: Callers exporting for single-tenant Sourcegraph should check
// the attributes of the Event type to ensure that only the appropriate fields
// are exported, as some fields should only be exported on an allowlist basis.
rpc RecordEvent(RecordEventRequest) returns (RecordEventResponse) {}
}
message Identifier {
message LicensedInstanceIdentifier {
// License key configured in the Sourcegraph instance emitting the event.
string license_key = 1;
// Self-reported Sourcegraph instance identifier.
string instance_id = 2;
// Instance external URL defined in the instance site configuration.
string external_url = 3;
}
message UnlicensedInstanceIdentifier {
// Self-reported Sourcegraph instance identifier.
string instance_id = 1;
// Instance external URL defined in the instance site configuration.
string external_url = 2;
}
message ManagedServiceIdentifier {
// Self-reported service identifier, for example 'my-service'.
string service_id = 1;
// Self-reported service environment, for example 'prod' or 'dev'.
optional string service_environment = 2;
}
oneof identifier {
// A licensed Sourcegraph instance.
LicensedInstanceIdentifier licensed_instance = 1;
// An unlicensed Sourcegraph instance.
UnlicensedInstanceIdentifier unlicensed_instance = 2;
// A service operated and managed by the Sourcegraph team, for example
// a service deployed by MSP: https://handbook.sourcegraph.com/departments/engineering/teams/core-services/managed-services/platform/
//
// Valid SAMS client credentials are required to publish events under a
// managed service identifier. The required scope is
// 'telemetry_gateway::events::publish'. See go/sams-client-credentials and
// go/sams-token-scopes for more information.
ManagedServiceIdentifier managed_service = 3;
}
}
message RecordEventsRequestMetadata {
// Client-provided request identifier for diagnostics purposes.
string request_id = 1;
// Telemetry publisher self-identification - for example, a Sourcegraph
// instance of some other kind of service.
Identifier identifier = 2;
}
message RecordEventsRequest {
message EventsPayload {
repeated Event events = 1;
}
oneof payload {
// Metadata about the events being recorded.
RecordEventsRequestMetadata metadata = 1;
// Batch of events to record in a single request. Clients should aim to
// batch large event backlogs into a series of smaller requests in the
// RecordEvents stream, being mindful of common limits in individual message
// sizes: https://protobuf.dev/programming-guides/api/#bound-req-res-sizes
EventsPayload events = 2;
}
}
message RecordEventsResponse {
// IDs of all events that were successfully recorded in the request.
//
// Note that if succeeded_events is a subset of events that were submitted,
// then some events failed to record and should be retried.
repeated string succeeded_events = 1;
}
message RecordEventRequest {
// Metadata about the events being recorded.
RecordEventsRequestMetadata metadata = 1;
// Event to record.
Event event = 2;
}
message RecordEventResponse {}
message Event {
// Generated ID of the event, currently expected to be UUID v4.
string id = 1;
// Timestamp of when the original event was recorded.
google.protobuf.Timestamp timestamp = 2;
// Feature associated with the event in camelCase, e.g. 'myFeature'.
string feature = 3;
// Action associated with the event in camelCase, e.g. 'pageView'.
string action = 4;
// Source of the event.
EventSource source = 5;
// Parameters of the event.
EventParameters parameters = 6;
// Optional user associated with the event.
//
// This field should be hydrated by the Sourcegraph server, and not provided
// by clients.
optional EventUser user = 7;
// Optional feature flags configured in the context of the event.
optional EventFeatureFlags feature_flags = 8;
// Optional marketing campaign tracking parameters.
//
// 🚨 SECURITY: This metadata is NEVER exported from single-tenant Sourcegraph
// instances, and is only exported for events tracked in the public
// Sourcegraph.com instance and managed services.
optional EventMarketingTracking marketing_tracking = 9;
// Optional metadata identifying the interaction that generated the event.
optional EventInteraction interaction = 10;
}
message EventSource {
message Server {
// Version of the server emitting the event, corresponding to
// RecordEventsRequestMetadata.Identifier. For example, if the Identifier
// indicates the publisher is a Sourcegraph instance, the version represents
// the version of the Sourcegraph server.
string version = 1;
}
message Client {
// Source client of the event.
string name = 1;
// Version of the client.
optional string version = 2;
}
// Information about the server that is publishing the event, based on
// RecordEventsRequestMetadata.Identifier.
Server server = 1;
// Information about the client that generated the event.
optional Client client = 2;
}
message EventParameters {
// Version of the event parameters, used for indicating the "shape" of this
// event's metadata, beginning at 0. Useful for denoting if the shape of
// metadata has changed in any way.
int32 version = 1;
// DEPRECATED, legacy metadata format that only accepted int64 - use the new
// 'metadata' field instead, which accepts float values. Values sent through
// this proto field will be merged into the new metadata attributes.
//
// We don't use a [deprecated = true] tag because we use this field to handle
// accepting exporters sending metadata in this format.
map<string, int64> legacy_metadata = 2;
// Strictly typed metadata, restricted to integer values to avoid accidentally
// exporting sensitive or private data.
map<string, double> metadata = 5;
// Additional potentially sensitive metadata - i.e. not restricted to integer
// values.
//
// 🚨 SECURITY: This metadata is NOT exported from instances by default, as it
// can contain arbitrarily-shaped data that may accidentally contain sensitive
// or private contents.
//
// This metadata is only exported on an allowlist basis based on terms of
// use agreements and combinations of event feature and action, alongside
// careful audit of callsites.
optional google.protobuf.Struct private_metadata = 3;
// Optional billing-related metadata.
optional EventBillingMetadata billing_metadata = 4;
}
message EventBillingMetadata {
// Billing product ID associated with the event.
string product = 1;
// Billing category ID the event falls into.
string category = 2;
}
message EventUser {
// Sourcegraph instance database user ID of the user. User IDs are specific to
// a Sourcegraph instance, and are not universal across Sourcegraph instances.
//
// We use an int64 as an ID because in Sourcegraph, database user IDs are
// always integers.
optional int64 user_id = 1;
// Randomized unique identifier representing the user (typically stored in
// localstorage in web clients, or similar mechanisms elsewhere). This is
// often used for unauthenticated users, but can persist to authenticated
// users as well.
optional string anonymous_user_id = 2;
// Sourcegraph Accounts Management System (SAMS) account associated with the
// user, represented by a SAMS external user ID in a UUID format. This is only
// valid for services leveraging SAMS as an identity provider - in other words,
// traditional Sourcegraph instances will not provide this.
//
// Learn more about SAMS: https://handbook.sourcegraph.com/departments/engineering/teams/core-services/sams
optional string sams_external_id = 3;
}
message EventFeatureFlags {
// Evaluated feature flags. In Soucegraph we currently only support boolean
// feature flags, but in the API we allow arbitrary string values for future
// extensibility.
//
// This field should be hydrated by the Sourcegraph server, and not provided
// by clients.
map<string, string> flags = 1;
}
// Marketing campaign tracking metadata.
//
// 🚨 SECURITY: This metadata is NEVER exported from single-tenant Sourcegraph
// instances, and is only exported for events tracked in the public
// Sourcegraph.com instance and managed services.
message EventMarketingTracking {
// URL the event occurred on.
optional string url = 1;
// Initial URL the user landed on.
optional string first_source_url = 2;
// Cohort ID to identify the user as part of a specific A/B test.
optional string cohort_id = 3;
// Referrer URL that refers the user to Sourcegraph.
optional string referrer = 4;
// Last source URL visited by the user.
optional string last_source_url = 5;
// Device session ID to identify the user's session.
optional string device_session_id = 6;
// Session referrer URL for the user.
optional string session_referrer = 7;
// First URL the user visited in their current session.
optional string session_first_url = 8;
}
message EventInteraction {
// OpenTelemetry trace ID representing the interaction associated with the event.
optional string trace_id = 1;
// Custom interaction ID representing the interaction associated with the event.
optional string interaction_id = 2;
message Geolocation {
// Inferred ISO 3166-1 alpha-2 or alpha-3 country code
string country_code = 1;
}
// Geolocation associated with the interaction, typically inferred from the
// originating client's IP address (which we do not collect).
optional Geolocation geolocation = 3;
}

View File

@ -0,0 +1 @@
../../../lib/telemetrygateway/v1/telemetrygateway.proto

View File

@ -15,6 +15,7 @@ require (
github.com/go-enry/go-enry/v2 v2.8.4
github.com/gobwas/glob v0.2.3
github.com/google/go-cmp v0.6.0
github.com/google/uuid v1.6.0
github.com/grafana/regexp v0.0.0-20221123153739-15dc172cd2db
github.com/hexops/autogold/v2 v2.0.3
github.com/jackc/pgconn v1.14.0
@ -35,6 +36,7 @@ require (
github.com/xeipuuv/gojsonschema v1.2.0
golang.org/x/sys v0.17.0
golang.org/x/term v0.17.0
google.golang.org/grpc v1.59.0
google.golang.org/protobuf v1.31.0
gopkg.in/yaml.v2 v2.4.0
gopkg.in/yaml.v3 v3.0.1
@ -69,7 +71,6 @@ require (
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/uuid v1.4.0 // indirect
github.com/gorilla/css v1.0.0 // indirect
github.com/hexops/gotextdiff v1.0.3 // indirect
github.com/hexops/valast v1.4.3 // indirect
@ -125,7 +126,6 @@ require (
golang.org/x/text v0.14.0 // indirect
golang.org/x/tools v0.18.0 // indirect
google.golang.org/genproto v0.0.0-20231030173426-d783a09b4405 // indirect
google.golang.org/grpc v1.59.0 // indirect
gotest.tools/v3 v3.0.3 // indirect
mvdan.cc/gofumpt v0.4.0 // indirect
)

View File

@ -128,8 +128,8 @@ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/
github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
github.com/grafana/regexp v0.0.0-20221123153739-15dc172cd2db h1:7aN5cccjIqCLTzedH7MZzRZt5/lsAHch6Z3L2ZGn5FA=

View File

@ -64,7 +64,7 @@ require (
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/pprof v0.0.0-20230602150820-91b7bce49751 // indirect
github.com/google/s2a-go v0.1.7 // indirect
github.com/google/uuid v1.5.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
github.com/googleapis/gax-go/v2 v2.12.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 // indirect

View File

@ -142,8 +142,8 @@ github.com/google/pprof v0.0.0-20230602150820-91b7bce49751/go.mod h1:Jh3hGz2jkYa
github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o=
github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs=
github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0=
github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas=

View File

@ -0,0 +1,2 @@
**/* @sourcegraph/core-services
**/* @sourcegraph/data-team

View File

@ -1,4 +1,3 @@
load("//dev:go_defs.bzl", "go_test")
load("@io_bazel_rules_go//go:def.bzl", "go_library")
load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library")
load("@rules_buf//buf:defs.bzl", "buf_lint_test")
@ -8,7 +7,7 @@ load("@rules_proto_grpc//doc:defs.bzl", "doc_template_compile")
proto_library(
name = "v1_proto",
srcs = ["telemetrygateway.proto"],
strip_import_prefix = "/internal", # keep
strip_import_prefix = "/lib", # keep
visibility = ["//visibility:private"],
deps = [
"@com_google_protobuf//:struct_proto",
@ -22,16 +21,16 @@ go_proto_library(
"//:gen-go-grpc",
"@io_bazel_rules_go//proto:go_proto",
],
importpath = "github.com/sourcegraph/sourcegraph/internal/telemetrygateway/v1",
importpath = "github.com/sourcegraph/sourcegraph/lib/telemetrygateway/v1",
proto = ":v1_proto",
visibility = ["//visibility:private"],
)
go_library(
name = "telemetrygateway",
srcs = ["event.go"],
srcs = ["v1.go"],
embed = [":v1_go_proto"],
importpath = "github.com/sourcegraph/sourcegraph/internal/telemetrygateway/v1",
importpath = "github.com/sourcegraph/sourcegraph/lib/telemetrygateway/v1",
visibility = [
"//cmd/frontend/internal/telemetry/resolvers:__pkg__",
"//cmd/telemetry-gateway/internal/events:__pkg__",
@ -49,20 +48,12 @@ go_library(
"//internal/telemetry/teestore:__pkg__",
"//internal/telemetry/telemetrytest:__pkg__",
"//internal/telemetrygateway:__pkg__",
"//internal/telemetrygateway/event:__pkg__",
"//internal/telemetrygateway/gitdomain:__pkg__",
"//internal/telemetrygateway/integration_tests:__pkg__",
"//internal/telemetrygateway/protocol:__pkg__",
],
deps = [
"//internal/actor",
"//internal/featureflag",
"//internal/requestclient",
"//internal/requestinteraction",
"//internal/trace",
"//lib/pointers",
"@com_github_google_uuid//:uuid",
"@org_golang_google_protobuf//types/known/timestamppb",
],
deps = ["@com_github_google_uuid//:uuid"],
)
# See https://github.com/sourcegraph/sourcegraph/issues/50032
@ -79,31 +70,6 @@ buf_lint_test(
targets = [":v1_proto"],
)
go_test(
name = "telemetrygateway_test",
srcs = [
"backcompat_test.go",
"event_test.go",
"main_test.go",
],
data = glob(["testdata/**"]),
embed = [":telemetrygateway"],
deps = [
"//internal/actor",
"//internal/requestclient",
"//internal/trace",
"//internal/trace/tracetest",
"//lib/pointers",
"@com_github_hexops_autogold_v2//:autogold",
"@com_github_stretchr_testify//assert",
"@com_github_stretchr_testify//require",
"@org_golang_google_protobuf//encoding/protojson",
"@org_golang_google_protobuf//proto",
"@org_golang_google_protobuf//types/known/structpb",
"@org_golang_google_protobuf//types/known/timestamppb",
],
)
doc_template_compile(
name = "protocol.md",
protos = [":v1_proto"],

View File

@ -3,7 +3,7 @@
// as a cache, and Sourcegraph instances rely on this format to emit telemetry
// to the managed Sourcegraph Telemetry Gateway service.
//
// Tests in ./internal/telemetrygateway/v1/backcompat_test.go can be used to
// Tests in ./internal/telemetrygateway/backcompat_test.go can be used to
// assert compatibility with snapshots created by older versions of this spec.
// Code generated by protoc-gen-go. DO NOT EDIT.
@ -1749,12 +1749,11 @@ var file_telemetrygateway_proto_rawDesc = []byte{
0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e,
0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79,
0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x41, 0x5a, 0x3f, 0x67, 0x69, 0x74,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x3c, 0x5a, 0x3a, 0x67, 0x69, 0x74,
0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x67, 0x72,
0x61, 0x70, 0x68, 0x2f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x67, 0x72, 0x61, 0x70, 0x68, 0x2f,
0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74,
0x72, 0x79, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x33,
0x6c, 0x69, 0x62, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x67, 0x61, 0x74,
0x65, 0x77, 0x61, 0x79, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (

View File

@ -0,0 +1,285 @@
// 🔔 IMPORTANT: Be VERY careful not to introduce breaking changes to this
// spec - raw protocol buffer wire format messages are persisted to database
// as a cache, and Sourcegraph instances rely on this format to emit telemetry
// to the managed Sourcegraph Telemetry Gateway service.
//
// Tests in ./internal/telemetrygateway/backcompat_test.go can be used to
// assert compatibility with snapshots created by older versions of this spec.
syntax = "proto3";
package telemetrygateway.v1;
import "google/protobuf/struct.proto";
import "google/protobuf/timestamp.proto";
option go_package = "github.com/sourcegraph/sourcegraph/lib/telemetrygateway/v1";
service TelemeteryGatewayService {
// RecordEvents streams telemetry events in batches to the Telemetry Gateway
// service. Events should only be considered delivered if recording is
// acknowledged in RecordEventsResponse.
//
// This is the preferred mechanism for exporting large volumes of events in
// bulk.
//
// 🚨 SECURITY: Callers exporting for single-tenant Sourcegraph should check
// the attributes of the Event type to ensure that only the appropriate fields
// are exported, as some fields should only be exported on an allowlist basis.
rpc RecordEvents(stream RecordEventsRequest) returns (stream RecordEventsResponse) {}
// RecordEvent records a single telemetry event to the Telemetry Gateway service.
// If the RPC succeeds, then the event was successfully published.
//
// This RPC currently ONLY accepts events published by ManagedServiceIdentifier,
// as this mechanism is intended for low-volume managed services. Higher-volume
// use cases should implement a batching mechanism and use the RecordEvents
// RPC instead.
//
// 🚨 SECURITY: Callers exporting for single-tenant Sourcegraph should check
// the attributes of the Event type to ensure that only the appropriate fields
// are exported, as some fields should only be exported on an allowlist basis.
rpc RecordEvent(RecordEventRequest) returns (RecordEventResponse) {}
}
message Identifier {
message LicensedInstanceIdentifier {
// License key configured in the Sourcegraph instance emitting the event.
string license_key = 1;
// Self-reported Sourcegraph instance identifier.
string instance_id = 2;
// Instance external URL defined in the instance site configuration.
string external_url = 3;
}
message UnlicensedInstanceIdentifier {
// Self-reported Sourcegraph instance identifier.
string instance_id = 1;
// Instance external URL defined in the instance site configuration.
string external_url = 2;
}
message ManagedServiceIdentifier {
// Self-reported service identifier, for example 'my-service'.
string service_id = 1;
// Self-reported service environment, for example 'prod' or 'dev'.
optional string service_environment = 2;
}
oneof identifier {
// A licensed Sourcegraph instance.
LicensedInstanceIdentifier licensed_instance = 1;
// An unlicensed Sourcegraph instance.
UnlicensedInstanceIdentifier unlicensed_instance = 2;
// A service operated and managed by the Sourcegraph team, for example
// a service deployed by MSP: https://handbook.sourcegraph.com/departments/engineering/teams/core-services/managed-services/platform/
//
// Valid SAMS client credentials are required to publish events under a
// managed service identifier. The required scope is
// 'telemetry_gateway::events::publish'. See go/sams-client-credentials and
// go/sams-token-scopes for more information.
ManagedServiceIdentifier managed_service = 3;
}
}
message RecordEventsRequestMetadata {
// Client-provided request identifier for diagnostics purposes.
string request_id = 1;
// Telemetry publisher self-identification - for example, a Sourcegraph
// instance of some other kind of service.
Identifier identifier = 2;
}
message RecordEventsRequest {
message EventsPayload {
repeated Event events = 1;
}
oneof payload {
// Metadata about the events being recorded.
RecordEventsRequestMetadata metadata = 1;
// Batch of events to record in a single request. Clients should aim to
// batch large event backlogs into a series of smaller requests in the
// RecordEvents stream, being mindful of common limits in individual message
// sizes: https://protobuf.dev/programming-guides/api/#bound-req-res-sizes
EventsPayload events = 2;
}
}
message RecordEventsResponse {
// IDs of all events that were successfully recorded in the request.
//
// Note that if succeeded_events is a subset of events that were submitted,
// then some events failed to record and should be retried.
repeated string succeeded_events = 1;
}
message RecordEventRequest {
// Metadata about the events being recorded.
RecordEventsRequestMetadata metadata = 1;
// Event to record.
Event event = 2;
}
message RecordEventResponse {}
message Event {
// Generated ID of the event, currently expected to be UUID v4.
string id = 1;
// Timestamp of when the original event was recorded.
google.protobuf.Timestamp timestamp = 2;
// Feature associated with the event in camelCase, e.g. 'myFeature'.
string feature = 3;
// Action associated with the event in camelCase, e.g. 'pageView'.
string action = 4;
// Source of the event.
EventSource source = 5;
// Parameters of the event.
EventParameters parameters = 6;
// Optional user associated with the event.
//
// This field should be hydrated by the Sourcegraph server, and not provided
// by clients.
optional EventUser user = 7;
// Optional feature flags configured in the context of the event.
optional EventFeatureFlags feature_flags = 8;
// Optional marketing campaign tracking parameters.
//
// 🚨 SECURITY: This metadata is NEVER exported from single-tenant Sourcegraph
// instances, and is only exported for events tracked in the public
// Sourcegraph.com instance and managed services.
optional EventMarketingTracking marketing_tracking = 9;
// Optional metadata identifying the interaction that generated the event.
optional EventInteraction interaction = 10;
}
message EventSource {
message Server {
// Version of the server emitting the event, corresponding to
// RecordEventsRequestMetadata.Identifier. For example, if the Identifier
// indicates the publisher is a Sourcegraph instance, the version represents
// the version of the Sourcegraph server.
string version = 1;
}
message Client {
// Source client of the event.
string name = 1;
// Version of the client.
optional string version = 2;
}
// Information about the server that is publishing the event, based on
// RecordEventsRequestMetadata.Identifier.
Server server = 1;
// Information about the client that generated the event.
optional Client client = 2;
}
message EventParameters {
// Version of the event parameters, used for indicating the "shape" of this
// event's metadata, beginning at 0. Useful for denoting if the shape of
// metadata has changed in any way.
int32 version = 1;
// DEPRECATED, legacy metadata format that only accepted int64 - use the new
// 'metadata' field instead, which accepts float values. Values sent through
// this proto field will be merged into the new metadata attributes.
//
// We don't use a [deprecated = true] tag because we use this field to handle
// accepting exporters sending metadata in this format.
map<string, int64> legacy_metadata = 2;
// Strictly typed metadata, restricted to integer values to avoid accidentally
// exporting sensitive or private data.
map<string, double> metadata = 5;
// Additional potentially sensitive metadata - i.e. not restricted to integer
// values.
//
// 🚨 SECURITY: This metadata is NOT exported from instances by default, as it
// can contain arbitrarily-shaped data that may accidentally contain sensitive
// or private contents.
//
// This metadata is only exported on an allowlist basis based on terms of
// use agreements and combinations of event feature and action, alongside
// careful audit of callsites.
optional google.protobuf.Struct private_metadata = 3;
// Optional billing-related metadata.
optional EventBillingMetadata billing_metadata = 4;
}
message EventBillingMetadata {
// Billing product ID associated with the event.
string product = 1;
// Billing category ID the event falls into.
string category = 2;
}
message EventUser {
// Sourcegraph instance database user ID of the user. User IDs are specific to
// a Sourcegraph instance, and are not universal across Sourcegraph instances.
//
// We use an int64 as an ID because in Sourcegraph, database user IDs are
// always integers.
optional int64 user_id = 1;
// Randomized unique identifier representing the user (typically stored in
// localstorage in web clients, or similar mechanisms elsewhere). This is
// often used for unauthenticated users, but can persist to authenticated
// users as well.
optional string anonymous_user_id = 2;
// Sourcegraph Accounts Management System (SAMS) account associated with the
// user, represented by a SAMS external user ID in a UUID format. This is only
// valid for services leveraging SAMS as an identity provider - in other words,
// traditional Sourcegraph instances will not provide this.
//
// Learn more about SAMS: https://handbook.sourcegraph.com/departments/engineering/teams/core-services/sams
optional string sams_external_id = 3;
}
message EventFeatureFlags {
// Evaluated feature flags. In Soucegraph we currently only support boolean
// feature flags, but in the API we allow arbitrary string values for future
// extensibility.
//
// This field should be hydrated by the Sourcegraph server, and not provided
// by clients.
map<string, string> flags = 1;
}
// Marketing campaign tracking metadata.
//
// 🚨 SECURITY: This metadata is NEVER exported from single-tenant Sourcegraph
// instances, and is only exported for events tracked in the public
// Sourcegraph.com instance and managed services.
message EventMarketingTracking {
// URL the event occurred on.
optional string url = 1;
// Initial URL the user landed on.
optional string first_source_url = 2;
// Cohort ID to identify the user as part of a specific A/B test.
optional string cohort_id = 3;
// Referrer URL that refers the user to Sourcegraph.
optional string referrer = 4;
// Last source URL visited by the user.
optional string last_source_url = 5;
// Device session ID to identify the user's session.
optional string device_session_id = 6;
// Session referrer URL for the user.
optional string session_referrer = 7;
// First URL the user visited in their current session.
optional string session_first_url = 8;
}
message EventInteraction {
// OpenTelemetry trace ID representing the interaction associated with the event.
optional string trace_id = 1;
// Custom interaction ID representing the interaction associated with the event.
optional string interaction_id = 2;
message Geolocation {
// Inferred ISO 3166-1 alpha-2 or alpha-3 country code
string country_code = 1;
}
// Geolocation associated with the interaction, typically inferred from the
// originating client's IP address (which we do not collect).
optional Geolocation geolocation = 3;
}

View File

@ -3,7 +3,7 @@
// as a cache, and Sourcegraph instances rely on this format to emit telemetry
// to the managed Sourcegraph Telemetry Gateway service.
//
// Tests in ./internal/telemetrygateway/v1/backcompat_test.go can be used to
// Tests in ./internal/telemetrygateway/backcompat_test.go can be used to
// assert compatibility with snapshots created by older versions of this spec.
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.

View File

@ -0,0 +1,14 @@
// Package v1 publishes telemetrygateway V1 bindings for both internal
// (single-tenant Sourcegraph) and external (standalone Sourcegraph-managed
// services) consumption. This package also includes standard defaults and
// helpers for Telemetry Gateway integrations.
package v1
import "github.com/google/uuid"
// DefaultEventIDFunc is the default generator for telemetry event IDs.
// We currently use V7, which is time-ordered, making them useful for event IDs.
// https://datatracker.ietf.org/doc/html/draft-peabody-dispatch-new-uuid-format-03#name-uuid-version-7
var DefaultEventIDFunc = func() string {
return uuid.Must(uuid.NewV7()).String()
}

View File

@ -46,7 +46,7 @@ require (
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/uuid v1.4.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gosimple/slug v1.12.0 // indirect
github.com/gosimple/unidecode v1.0.1 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect

View File

@ -164,8 +164,8 @@ github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hf
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gosimple/slug v1.1.1/go.mod h1:ER78kgg1Mv0NQGlXiDe57DpCyfbNywXXZ9mIorhxAf0=