Repository: getsentry/sentry-go

Issue:
panic because `nil` client in `DynamicSamplingContextFromTransaction`
## Summary
In a context where we do not have initialized sentry, when finishing a transaction that have `Sampled = sentry.SampledFalse` will trigger a panic. I have a piece of code that conditionally force the sampling of a given transaction, this is working well but if I run my unit tests on the function that panics.

In general, I think the SDK should not panic if sentry is not configured.

```
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x635330]

goroutine 1 [running]:
github.com/getsentry/sentry-go.DynamicSamplingContextFromTransaction(0xc00015c000)
	/tmp/gopath1614006584/pkg/mod/github.com/getsentry/sentry-go@v0.16.0/dynamic_sampling_context.go:46 +0xb0
github.com/getsentry/sentry-go.(*Span).toEvent(0xc00015c000)
	/tmp/gopath1614006584/pkg/mod/github.com/getsentry/sentry-go@v0.16.0/tracing.go:405 +0x2e5
github.com/getsentry/sentry-go.(*Span).Finish(0xc00015c000)
	/tmp/gopath1614006584/pkg/mod/github.com/getsentry/sentry-go@v0.16.0/tracing.go:173 +0xb5
```

## Steps To Reproduce
https://go.dev/play/p/vzesJ4l9TJa

## Expected Behavior
No panic on `Finish()`

### SDK
 - `sentry-go` version: `v0.16.0`
 - Go version: 1.19
 - Using Go Modules? yes

### Sentry
 - Using hosted Sentry in sentry.io? yes

Happy to shoot a PR if you quickly explain how would you like to handle that 🙏🏻 


Discussion:
@tonyo: Thanks for the report @eliecharra, can confirm that it's crashing, which is totally undesirable.

We'll look into this (most likely won't happen in December, though), but in the meantime, could you perhaps initialize the Sentry SDK with empty DSN in your tests as a workaround?
E.g. something like
```go
sentry.Init(sentry.ClientOptions{})
```
@eliecharra: > We'll look into this (most likely won't happen in December, though)

Sure sure no worries this is not a blocker at all 👍🏻 Also the workaround is perfectly acceptable so far 🙏🏻

Thanks for your work.@cleptric: @eliecharra Fixed in https://github.com/getsentry/sentry-go/releases/tag/v0.17.0

Solution patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ca66fa5..4442cb5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,12 @@
 # Changelog
 
+## Unreleased
+
+### Bug Fixes
+
+- fix(dynamic-sampling): Do not crash in Span.Finish() when Client is empty [#520](https://github.com/getsentry/sentry-go/pull/520)
+  - Fixes [#518](https://github.com/getsentry/sentry-go/issues/518)
+
 ## 0.16.0
 
 The Sentry SDK team is happy to announce the immediate availability of Sentry Go SDK v0.16.0.
diff --git a/dynamic_sampling_context.go b/dynamic_sampling_context.go
index 3e54839..06feb21 100644
--- a/dynamic_sampling_context.go
+++ b/dynamic_sampling_context.go
@@ -43,6 +43,14 @@ func DynamicSamplingContextFromTransaction(span *Span) DynamicSamplingContext {
 	hub := hubFromContext(span.Context())
 	scope := hub.Scope()
 	client := hub.Client()
+
+	if client == nil || scope == nil {
+		return DynamicSamplingContext{
+			Entries: map[string]string{},
+			Frozen:  false,
+		}
+	}
+
 	options := client.Options()
 
 	if traceID := span.TraceID.String(); traceID != "" {



Test patch:
diff --git a/dynamic_sampling_context_test.go b/dynamic_sampling_context_test.go
index 6cca565..5299bc1 100644
--- a/dynamic_sampling_context_test.go
+++ b/dynamic_sampling_context_test.go
@@ -1,6 +1,7 @@
 package sentry
 
 import (
+	"context"
 	"strings"
 	"testing"
 )
@@ -108,6 +109,18 @@ func TestDynamicSamplingContextFromTransaction(t *testing.T) {
 				},
 			},
 		},
+		// Empty context without a valid Client
+		{
+			input: func() *Span {
+				ctx := context.Background()
+				tx := StartTransaction(ctx, "op")
+				return tx
+			}(),
+			want: DynamicSamplingContext{
+				Frozen:  false,
+				Entries: map[string]string{},
+			},
+		},
 	}
 
 	for _, tc := range tests {
diff --git a/tracing_test.go b/tracing_test.go
index 8902f75..3cc13df 100644
--- a/tracing_test.go
+++ b/tracing_test.go
@@ -567,3 +567,12 @@ func TestSample(t *testing.T) {
 		t.Fatalf("got %s, want %s", got, SampledTrue)
 	}
 }
+
+func TestDoesNotCrashWithEmptyContext(t *testing.T) {
+	// This test makes sure that we can still start and finish transactions
+	// with empty context (for example, when Sentry SDK is not initialized)
+	ctx := context.Background()
+	tx := StartTransaction(ctx, "op")
+	tx.Sampled = SampledTrue
+	tx.Finish()
+}