Repository: getsentry/sentry-go

Issue:
Send span origin
Trace origin indicates what created a trace or a span. Not all traces and spans contain enough information to tell whether the user or what precisely in the SDK created it. Origin solves this problem. Origin can be sent with the [trace context](https://develop.sentry.dev/sdk/event-payloads/contexts/#trace-context) or [spans](https://develop.sentry.dev/sdk/event-payloads/span/). The SDKs must set this value automatically. Users are not expected to set it manually. For more information, visit the [develop docs](https://develop.sentry.dev/sdk/performance/trace-origin/).

As the origin for trace context and spans is optional, every SDK can decide whether to start sending the origin for everything or just selected transactions and spans. This issue is important for internal data analysis to understand the quality of our traces.

Hints from issue comments:
@natikgadzhi: @greywolve @cleptric cool if I work on this? I'm about to work on the Ruby equivalent of this, so I'm more or less in that context anyway.

Solution patch:
diff --git a/echo/sentryecho.go b/echo/sentryecho.go
index 2b039ec..26972d9 100644
--- a/echo/sentryecho.go
+++ b/echo/sentryecho.go
@@ -73,6 +73,7 @@ func (h *handler) handle(next echo.HandlerFunc) echo.HandlerFunc {
 			sentry.WithOpName("http.server"),
 			sentry.ContinueFromRequest(r),
 			sentry.WithTransactionSource(transactionSource),
+			sentry.WithSpanOrigin(sentry.SpanOriginEcho),
 		}
 
 		transaction := sentry.StartTransaction(
diff --git a/fasthttp/sentryfasthttp.go b/fasthttp/sentryfasthttp.go
index 9ab638e..f8a5bf5 100644
--- a/fasthttp/sentryfasthttp.go
+++ b/fasthttp/sentryfasthttp.go
@@ -70,6 +70,7 @@ func (h *Handler) Handle(handler fasthttp.RequestHandler) fasthttp.RequestHandle
 			sentry.WithOpName("http.server"),
 			sentry.ContinueFromRequest(convertedHTTPRequest),
 			sentry.WithTransactionSource(sentry.SourceRoute),
+			sentry.WithSpanOrigin(sentry.SpanOriginFastHTTP),
 		}
 
 		method := string(ctx.Method())
diff --git a/fiber/sentryfiber.go b/fiber/sentryfiber.go
index 3e74b87..604c5cf 100644
--- a/fiber/sentryfiber.go
+++ b/fiber/sentryfiber.go
@@ -71,6 +71,7 @@ func (h *handler) handle(ctx *fiber.Ctx) error {
 		sentry.WithOpName("http.server"),
 		sentry.ContinueFromRequest(convertedHTTPRequest),
 		sentry.WithTransactionSource(transactionSource),
+		sentry.WithSpanOrigin(sentry.SpanOriginFiber),
 	}
 
 	transaction := sentry.StartTransaction(
diff --git a/gin/sentrygin.go b/gin/sentrygin.go
index 3444c82..1ccc1d1 100644
--- a/gin/sentrygin.go
+++ b/gin/sentrygin.go
@@ -74,6 +74,7 @@ func (h *handler) handle(c *gin.Context) {
 		sentry.WithOpName("http.server"),
 		sentry.ContinueFromRequest(c.Request),
 		sentry.WithTransactionSource(transactionSource),
+		sentry.WithSpanOrigin(sentry.SpanOriginGin),
 	}
 
 	transaction := sentry.StartTransaction(ctx,
diff --git a/http/sentryhttp.go b/http/sentryhttp.go
index edbdb9f..a8720a2 100644
--- a/http/sentryhttp.go
+++ b/http/sentryhttp.go
@@ -98,6 +98,7 @@ func (h *Handler) handle(handler http.Handler) http.HandlerFunc {
 			sentry.WithOpName("http.server"),
 			sentry.ContinueFromRequest(r),
 			sentry.WithTransactionSource(sentry.SourceURL),
+			sentry.WithSpanOrigin(sentry.SpanOriginStdLib),
 		}
 
 		transaction := sentry.StartTransaction(ctx,
diff --git a/iris/sentryiris.go b/iris/sentryiris.go
index 6006611..eb00e30 100644
--- a/iris/sentryiris.go
+++ b/iris/sentryiris.go
@@ -66,6 +66,7 @@ func (h *handler) handle(ctx iris.Context) {
 		sentry.WithOpName("http.server"),
 		sentry.ContinueFromRequest(ctx.Request()),
 		sentry.WithTransactionSource(sentry.SourceRoute),
+		sentry.WithSpanOrigin(sentry.SpanOriginIris),
 	}
 
 	currentRoute := ctx.GetCurrentRoute()
diff --git a/negroni/sentrynegroni.go b/negroni/sentrynegroni.go
index 0111599..2a49695 100644
--- a/negroni/sentrynegroni.go
+++ b/negroni/sentrynegroni.go
@@ -82,6 +82,7 @@ func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.Ha
 		sentry.WithOpName("http.server"),
 		sentry.ContinueFromRequest(r),
 		sentry.WithTransactionSource(sentry.SourceURL),
+		sentry.WithSpanOrigin(sentry.SpanOriginNegroni),
 	}
 	// We don't mind getting an existing transaction back so we don't need to
 	// check if it is.
diff --git a/tracing.go b/tracing.go
index 1bb53d0..d8077c1 100644
--- a/tracing.go
+++ b/tracing.go
@@ -18,6 +18,20 @@ const (
 	SentryBaggageHeader = "baggage"
 )
 
+// SpanOrigin indicates what created a trace or a span. See: https://develop.sentry.dev/sdk/performance/trace-origin/
+type SpanOrigin string
+
+const (
+	SpanOriginManual   = "manual"
+	SpanOriginEcho     = "auto.http.echo"
+	SpanOriginFastHTTP = "auto.http.fasthttp"
+	SpanOriginFiber    = "auto.http.fiber"
+	SpanOriginGin      = "auto.http.gin"
+	SpanOriginStdLib   = "auto.http.stdlib"
+	SpanOriginIris     = "auto.http.iris"
+	SpanOriginNegroni  = "auto.http.negroni"
+)
+
 // A Span is the building block of a Sentry transaction. Spans build up a tree
 // structure of timed operations. The span tree makes up a transaction event
 // that is sent to Sentry when the root span is finished.
@@ -37,6 +51,7 @@ type Span struct { //nolint: maligned // prefer readability over optimal memory
 	Data         map[string]interface{} `json:"data,omitempty"`
 	Sampled      Sampled                `json:"-"`
 	Source       TransactionSource      `json:"-"`
+	Origin       SpanOrigin             `json:"origin,omitempty"`
 
 	// mu protects concurrent writes to map fields
 	mu sync.RWMutex
@@ -113,11 +128,19 @@ func StartSpan(ctx context.Context, operation string, options ...SpanOption) *Sp
 		parent: parent,
 	}
 
+	_, err := rand.Read(span.SpanID[:])
+	if err != nil {
+		panic(err)
+	}
+
 	if hasParent {
 		span.TraceID = parent.TraceID
+		span.ParentSpanID = parent.SpanID
+		span.Origin = parent.Origin
 	} else {
 		// Only set the Source if this is a transaction
 		span.Source = SourceCustom
+		span.Origin = SpanOriginManual
 
 		// Implementation note:
 		//
@@ -154,13 +177,6 @@ func StartSpan(ctx context.Context, operation string, options ...SpanOption) *Sp
 			panic(err)
 		}
 	}
-	_, err := rand.Read(span.SpanID[:])
-	if err != nil {
-		panic(err)
-	}
-	if hasParent {
-		span.ParentSpanID = parent.SpanID
-	}
 
 	// Apply options to override defaults.
 	for _, option := range options {
@@ -870,6 +886,13 @@ func WithSpanSampled(sampled Sampled) SpanOption {
 	}
 }
 
+// WithSpanOrigin sets the origin of the span.
+func WithSpanOrigin(origin SpanOrigin) SpanOption {
+	return func(s *Span) {
+		s.Origin = origin
+	}
+}
+
 // ContinueFromRequest returns a span option that updates the span to continue
 // an existing trace. If it cannot detect an existing trace in the request, the
 // span will be left unchanged.



Test patch:
diff --git a/tracing_test.go b/tracing_test.go
index 1addd83..c4c6358 100644
--- a/tracing_test.go
+++ b/tracing_test.go
@@ -211,6 +211,7 @@ func TestStartChild(t *testing.T) {
 				ParentSpanID: child.ParentSpanID,
 				Op:           child.Op,
 				Sampled:      SampledTrue,
+				Origin:       SpanOriginManual,
 			},
 		},
 		TransactionInfo: &TransactionInfo{