# How to Configure Trace Sampling Strategies in Fabrica-Kit: A Complete Guide

> Learn to configure trace sampling strategies in Fabrica-Kit. Customize OpenTelemetry samplers in trace.go or create a custom tracer provider for granular control over trace retention and performance. Get the complete guide now.

- Repository: [Pantheon/fabrica-kit](https://github.com/go-pantheon/fabrica-kit)
- Tags: how-to-guide
- Published: 2026-03-02

---

**Fabrica-Kit uses a default OpenTelemetry `ParentBased` sampler with 100% trace retention, but you can customize the sampling strategy by modifying the `Init` function in [`trace/trace.go`](https://github.com/go-pantheon/fabrica-kit/blob/main/trace/trace.go) or by creating a custom tracer provider with your preferred sampler.**

Fabrica-Kit provides built-in OpenTelemetry tracing utilities for Go microservices in the `go-pantheon/fabrica-kit` repository. While the default configuration captures every trace for maximum observability, production environments often require **trace sampling strategies** to manage costs and performance overhead.

## Understanding the Default Trace Sampling Behavior

Fabrica-Kit initializes its tracer provider in [`trace/trace.go`](https://github.com/go-pantheon/fabrica-kit/blob/main/trace/trace.go) with a hard-coded sampling configuration that captures all traces.

### The Default Sampler Configuration

According to the source code at [line 27 of [`trace/trace.go`](https://github.com/go-pantheon/fabrica-kit/blob/main/trace/trace.go)](https://github.com/go-pantheon/fabrica-kit/blob/main/trace/trace.go#L27), the default implementation uses:

```go
tracesdk.WithSampler(tracesdk.ParentBased(tracesdk.TraceIDRatioBased(1.0)))

```

This **ParentBased** sampler wraps a **TraceIDRatioBased** sampler set to `1.0`, meaning every trace is recorded regardless of load. The `Init` function defined at [line 17](https://github.com/go-pantheon/fabrica-kit/blob/main/trace/trace.go#L17) applies this configuration automatically when you initialize the tracer.

## Method 1: Modifying the Built-in Init Function

The most direct approach to configure trace sampling is refactoring the `Init` function to accept a sampler parameter instead of using the hard-coded value.

### Code Implementation

Modify [`trace/trace.go`](https://github.com/go-pantheon/fabrica-kit/blob/main/trace/trace.go) to accept a `tracesdk.Sampler` argument:

```go
package trace

import (
	"context"

	"go.opentelemetry.io/otel"
	"go.opentelemetry.io/otel/attribute"
	"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
	"go.opentelemetry.io/otel/sdk/resource"
	tracesdk "go.opentelemetry.io/otel/sdk/trace"
	semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
)

// Init now accepts a sampler argument.
func Init(url, name, profile, color string, sampler tracesdk.Sampler) error {
	exporter, err := otlptracehttp.New(context.Background(),
		otlptracehttp.WithEndpoint(url),
		otlptracehttp.WithInsecure(),
	)
	if err != nil {
		return err
	}

	// Use the caller-provided sampler instead of the hard-coded one.
	tp := tracesdk.NewTracerProvider(
		tracesdk.WithSampler(sampler),               // ← custom sampler
		tracesdk.WithBatcher(exporter),
		tracesdk.WithResource(resource.NewSchemaless(
			semconv.ServiceNameKey.String(name),
			attribute.String("profile", profile),
			attribute.String("color", color),
		)),
	)
	otel.SetTracerProvider(tp)
	return nil
}

```

### Usage Example

Pass your desired sampler when initializing the tracer:

```go
import (
	"go.opentelemetry.io/otel/sdk/trace"
	"github.com/go-pantheon/fabrica-kit/trace"
)

func main() {
	// Sample 50% of traces, but keep the parent-based logic.
	sampler := trace.ParentBased(trace.TraceIDRatioBased(0.5))

	if err := trace.Init(
		"localhost:4318", "my-service", "prod", "blue", sampler,
	); err != nil {
		panic(err)
	}
}

```

## Method 2: Creating a Custom Tracer Provider

If you prefer not to modify the library's source code, create a standalone tracer provider that overrides the global configuration while preserving Fabrica-Kit's instrumentation helpers.

### Standalone Provider Setup

Create your own provider with custom sampling logic:

```go
package main

import (
	"context"

	"go.opentelemetry.io/otel"
	"go.opentelemetry.io/otel/attribute"
	"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
	"go.opentelemetry.io/otel/sdk/resource"
	tracesdk "go.opentelemetry.io/otel/sdk/trace"
	semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
)

func main() {
	exp, _ := otlptracehttp.New(context.Background(),
		otlptracehttp.WithEndpoint("localhost:4318"),
		otlptracehttp.WithInsecure(),
	)

	// Example: always sample (useful in development)
	sampler := tracesdk.AlwaysSample()

	tp := tracesdk.NewTracerProvider(
		tracesdk.WithSampler(sampler),
		tracesdk.WithBatcher(exp),
		tracesdk.WithResource(resource.NewSchemaless(
			semconv.ServiceNameKey.String("my-service"),
			attribute.String("profile", "dev"),
			attribute.String("color", "green"),
		)),
	)
	otel.SetTracerProvider(tp)

	// Existing Fabrica-Kit helpers now pick up this provider automatically.
	// e.g. traceredis.StartConnectionSpan(ctx, "GET", "redis:6379", 0)
}

```

This approach ensures that utilities like `traceredis.StartConnectionSpan` from [`trace/traceredis/redis.go`](https://github.com/go-pantheon/fabrica-kit/blob/main/trace/traceredis/redis.go) and PostgreSQL instrumentation from [`trace/tracepg/postgresql.go`](https://github.com/go-pantheon/fabrica-kit/blob/main/trace/tracepg/postgresql.go) automatically respect your custom sampling configuration.

## Available Sampling Strategies

OpenTelemetry provides several built-in samplers you can use with Fabrica-Kit:

- **`AlwaysSample()`** – Records every span. Best for local development or debugging sessions where complete visibility is required.
- **`AlwaysOff()`** – Drops all spans. Use this in production environments when tracing overhead must be eliminated entirely.
- **`TraceIDRatioBased(p)`** – Records a random fraction of traces based on the probability `p` (e.g., `0.1` for 10%). Ideal for cost-controlled production tracing at scale.
- **`ParentBased(inner)`** – Inherits the sampling decision from the parent span, falling back to the `inner` sampler for root spans. Ensures distributed traces remain complete when any part is sampled.
- **Custom composite samplers** – Combine multiple strategies, such as always sampling error traces while applying ratio-based sampling to successful requests.

## How Sampling Affects Fabrica-Kit Components

When you configure trace sampling strategies in fabrica-kit, the change propagates to all instrumentation that relies on the global tracer provider:

- **[`trace/traceredis/redis.go`](https://github.com/go-pantheon/fabrica-kit/blob/main/trace/traceredis/redis.go)** – Redis client operations will follow your sampling rules automatically
- **[`trace/tracepg/postgresql.go`](https://github.com/go-pantheon/fabrica-kit/blob/main/trace/tracepg/postgresql.go)** – PostgreSQL connection pooling and query spans respect the configured sampler

Because these components use the global provider set via `otel.SetTracerProvider`, no additional configuration is required after you initialize the tracer with your chosen sampler.

## Summary

- Fabrica-Kit defaults to **100% trace sampling** via `ParentBased(TraceIDRatioBased(1.0))` in [`trace/trace.go`](https://github.com/go-pantheon/fabrica-kit/blob/main/trace/trace.go)
- **Modify the `Init` function** to accept sampler parameters for a permanent solution within the library
- **Create a custom provider** to override sampling without modifying source files
- Available strategies include probability-based, parent-based, always-on, and always-off samplers
- All Fabrica-Kit instrumentation utilities automatically respect the global tracer provider configuration

## Frequently Asked Questions

### What is the default trace sampling rate in fabrica-kit?

Fabrica-Kit captures **all traces by default**. The `Init` function in [`trace/trace.go`](https://github.com/go-pantheon/fabrica-kit/blob/main/trace/trace.go) configures a `ParentBased` sampler wrapping a `TraceIDRatioBased(1.0)` sampler, resulting in 100% retention regardless of traffic volume.

### How do I reduce trace volume in production?

Replace the default sampler with `TraceIDRatioBased(0.1)` to sample approximately 10% of traces, or use `ParentBased(TraceIDRatioBased(0.1))` to maintain parent-child trace consistency. Wrap this in the `Init` function or set it in a custom provider before calling `otel.SetTracerProvider`.

### Can I use different sampling strategies for different services?

Yes. Since the sampler is configured per-process during tracer initialization, each service can pass a different `tracesdk.Sampler` to the `Init` function or create its own provider. For example, critical services might use `AlwaysSample()` while high-traffic services use `TraceIDRatioBased(0.05)`.

### Does changing the sampler affect existing instrumentation?

No code changes are required in existing instrumentation. Components like `traceredis` and `tracepg` retrieve the tracer from the global provider via `otel.GetTracerProvider()`. When you update the sampling strategy during initialization, all existing spans automatically follow the new rules.