tracing

package
v0.0.0-...-8652ae4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 26, 2025 License: AGPL-3.0 Imports: 5 Imported by: 0

README

Tracing Package

OpenTelemetry tracing instrumentation for gitops-repo-api using the functional options pattern.

Overview

This package provides a lightweight wrapper around OpenTelemetry tracing that integrates seamlessly with the gitops-repo-api codebase. It supports both active tracing (when configured) and no-op tracing (when not configured), ensuring zero performance impact when tracing is disabled.

Features

  • Functional Options Pattern: Clean, extensible API using functional options
  • Zero Dependencies When Disabled: No-op tracer has no performance overhead
  • Context Propagation: Proper trace context propagation through concurrent operations
  • Standard Attributes: Pre-defined attribute helpers for consistent naming
  • Backward Compatible: All existing code continues to work without changes

Quick Start

Basic Usage (No Tracing)

By default, all components use a no-op tracer:

import (
    "github.com/codingninja/gitops-repo-api/diff"
    "github.com/codingninja/gitops-repo-api/git"
)

// Works exactly as before - no tracing overhead
preRepo := git.NewRepoSpec("https://github.com/example/repo", nil)
postRepo := git.NewRepoSpec("https://github.com/example/repo", nil)
differ := diff.NewDiffer(preRepo, postRepo, epds)
Enable Tracing

To enable tracing, configure OpenTelemetry and pass a tracer using functional options:

import (
    "go.opentelemetry.io/otel"
    sdktrace "go.opentelemetry.io/otel/sdk/trace"
    "github.com/codingninja/gitops-repo-api/tracing"
    "github.com/codingninja/gitops-repo-api/diff"
    "github.com/codingninja/gitops-repo-api/git"
)

// Setup OpenTelemetry
tp := sdktrace.NewTracerProvider(
    sdktrace.WithSampler(sdktrace.AlwaysSample()),
    // Add exporters here (e.g., Jaeger, Zipkin, etc.)
)
otel.SetTracerProvider(tp)

// Create wrapped tracer
otelTracer := tp.Tracer("gitops-repo-api")
tracer := tracing.NewTracer(otelTracer)

// Use functional options to enable tracing
preRepo := git.NewRepoSpec(
    "https://github.com/example/repo",
    nil,
    git.WithTracer(tracer),
)

postRepo := git.NewRepoSpec(
    "https://github.com/example/repo",
    nil,
    git.WithTracer(tracer),
)

differ := diff.NewDiffer(
    preRepo,
    postRepo,
    epds,
    diff.WithTracer(tracer),
)

Instrumented Operations

Git Operations

The git package instruments:

  • git.open - Opening/cloning repositories
  • git.checkout - Checking out branches
  • git.clone - Cloning operations

Attributes: git.url, git.ref, git.branch, git.hash, git.directory

Diff Operations

The diff package instruments:

  • gitops.diff - Root diff operation comparing two refs
  • gitops.extract - Extracting resources from a single ref
  • gitops.diff.entrypoint - Diffing a specific entrypoint
  • gitops.diff.discover - Discovering entrypoints

Attributes: git.pre_ref, git.post_ref, entrypoint.type, entrypoint.directory, entrypoint.count, diff.count

Resource Rendering

The resource package instruments:

  • resource.terraform.render - Terraform plan generation
  • resource.cdk.render - CDK synthesis
  • resource.kubernetes.render - Kubernetes manifest rendering

Attributes: resource.type, resource.count, dir.working

Context-Based Tracing

For the resource package, tracing uses context propagation:

import (
    "context"
    "github.com/codingninja/gitops-repo-api/resource"
    "github.com/codingninja/gitops-repo-api/tracing"
)

// Add tracer to context
ctx := resource.ContextWithTracer(context.Background(), tracer)

// Functions automatically use tracer from context
resMap, err := resource.RenderKubernetes(ctx, "/path/to/manifests")
plan, err := resource.RenderTerraform(ctx, "/path/to/terraform")
template, err := resource.RenderCdk(ctx, "/path/to/cdk")

Span Attributes

Standard attributes are provided for consistent naming:

Git Attributes
tracing.GitURL(url)           // git.url
tracing.GitRef(ref)           // git.ref
tracing.GitBranch(branch)     // git.branch
tracing.GitHash(hash)         // git.hash
tracing.GitDirectory(dir)     // git.directory
tracing.GitPreRef(ref)        // git.pre_ref
tracing.GitPostRef(ref)       // git.post_ref
Entrypoint Attributes
tracing.EntrypointType(type)      // entrypoint.type
tracing.EntrypointDirectory(dir)  // entrypoint.directory
tracing.EntrypointCount(count)    // entrypoint.count
Resource Attributes
tracing.ResourceType(type)      // resource.type
tracing.ResourceCount(count)    // resource.count
tracing.ResourceName(name)      // resource.name
Diff Attributes
tracing.DiffCount(count)        // diff.count
tracing.DiffCreated(count)      // diff.created
tracing.DiffUpdated(count)      // diff.updated
tracing.DiffDeleted(count)      // diff.deleted
tracing.DiffReplaced(count)     // diff.replaced
Directory Attributes
tracing.WorkingDir(dir)     // dir.working
tracing.PreDir(dir)         // dir.pre
tracing.PostDir(dir)        // dir.post

Error Handling

Errors are automatically recorded on spans with proper status codes:

ctx, span := tracer.Start(ctx, "operation")
defer span.End()

result, err := doSomething()
if err != nil {
    span.RecordError(err)
    span.SetStatus(codes.Error, err.Error())
    return err
}

Concurrent Operations

The implementation properly propagates trace context through goroutines:

// Parent span
ctx, span := tracer.Start(ctx, "parent.operation")
defer span.End()

var wg sync.WaitGroup
for _, item := range items {
    wg.Add(1)
    go func(ctx context.Context, item Item) {
        defer wg.Done()
        // Child span - properly linked to parent
        ctx, childSpan := tracer.Start(ctx, "child.operation")
        defer childSpan.End()

        processItem(ctx, item)
    }(ctx, item) // Pass context to preserve trace
}
wg.Wait()

Performance Considerations

  • No-Op Tracer: Zero overhead when tracing is disabled
  • Lazy Attribute Setting: Attributes are only computed when spans are active
  • Minimal Allocations: Efficient attribute creation and span management
  • Async Export: OpenTelemetry batches span exports asynchronously

Integration with Observability Platforms

The implementation works with any OpenTelemetry-compatible backend:

Jaeger
import "go.opentelemetry.io/otel/exporters/jaeger"

exporter, err := jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint("http://localhost:14268/api/traces")))
tp := sdktrace.NewTracerProvider(sdktrace.WithBatcher(exporter))
Zipkin
import "go.opentelemetry.io/otel/exporters/zipkin"

exporter, err := zipkin.New("http://localhost:9411/api/v2/spans")
tp := sdktrace.NewTracerProvider(sdktrace.WithBatcher(exporter))
Cloud Providers
  • AWS X-Ray
  • Google Cloud Trace
  • Azure Monitor
  • Datadog
  • New Relic
  • Honeycomb

Best Practices

  1. Always defer span.End(): Ensures spans are closed even on panics
  2. Set meaningful attributes: Help with debugging and filtering traces
  3. Record errors on spans: Provides visibility into failures
  4. Propagate context: Pass ctx to child operations for proper trace linking
  5. Use standard attributes: Maintains consistency across the codebase
  6. Sample appropriately: Configure sampling rates based on traffic volume

Migration Guide

Existing code requires no changes. To add tracing:

  1. Configure OpenTelemetry in your application entry point
  2. Create a tracing.Tracer instance
  3. Pass it via functional options when creating components
  4. For resource functions, add tracer to context

Example:

// Before
preRepo := git.NewRepoSpec(url, auth)

// After (with tracing)
preRepo := git.NewRepoSpec(url, auth, git.WithTracer(tracer))

// Before
differ := diff.NewDiffer(preRepo, postRepo, epds)

// After (with tracing)
differ := diff.NewDiffer(preRepo, postRepo, epds, diff.WithTracer(tracer))

Architecture

┌─────────────────────────────────────────┐
│   Application (main.go)                 │
│   - Configures OpenTelemetry            │
│   - Creates tracer instances            │
└──────────────┬──────────────────────────┘
               │
               ▼
┌─────────────────────────────────────────┐
│   tracing.Tracer (wrapper)              │
│   - Wraps OTel trace.Tracer             │
│   - Provides no-op fallback             │
│   - Helper methods                      │
└──────────────┬──────────────────────────┘
               │
    ┌──────────┼──────────┐
    ▼          ▼          ▼
┌────────┐ ┌──────┐  ┌──────────┐
│  git   │ │ diff │  │ resource │
│package │ │package│ │ package  │
└────────┘ └──────┘  └──────────┘
     │         │           │
     └─────────┴───────────┘
               │
               ▼
    ┌────────────────────┐
    │ OpenTelemetry SDK  │
    │ - Sampling         │
    │ - Batching         │
    │ - Export           │
    └────────┬───────────┘
             │
             ▼
    ┌────────────────┐
    │   Backend      │
    │ (Jaeger, etc.) │
    └────────────────┘

License

Same as gitops-repo-api project.

Documentation

Index

Examples

Constants

View Source
const (
	// Git operation attributes
	AttrGitURL       = "git.url"
	AttrGitRef       = "git.ref"
	AttrGitBranch    = "git.branch"
	AttrGitHash      = "git.hash"
	AttrGitDirectory = "git.directory"
	AttrGitPreRef    = "git.pre_ref"
	AttrGitPostRef   = "git.post_ref"

	// Entrypoint attributes
	AttrEntrypointType      = "entrypoint.type"
	AttrEntrypointDirectory = "entrypoint.directory"
	AttrEntrypointCount     = "entrypoint.count"

	// Resource attributes
	AttrResourceType  = "resource.type"
	AttrResourceCount = "resource.count"
	AttrResourceName  = "resource.name"
	AttrDiffCount     = "diff.count"
	AttrDiffCreated   = "diff.created"
	AttrDiffUpdated   = "diff.updated"
	AttrDiffDeleted   = "diff.deleted"
	AttrDiffReplaced  = "diff.replaced"

	// Directory attributes
	AttrWorkingDir = "dir.working"
	AttrPreDir     = "dir.pre"
	AttrPostDir    = "dir.post"

	// Operation result attributes
	AttrErrorOccurred = "error.occurred"
	AttrErrorMessage  = "error.message"
)

Standard attribute keys for gitops operations. These provide consistent naming across the codebase.

Variables

This section is empty.

Functions

func DiffCount

func DiffCount(count int) attribute.KeyValue

DiffCount creates an attribute for total diff count.

func DiffCreated

func DiffCreated(count int) attribute.KeyValue

DiffCreated creates an attribute for created resource count.

func DiffDeleted

func DiffDeleted(count int) attribute.KeyValue

DiffDeleted creates an attribute for deleted resource count.

func DiffReplaced

func DiffReplaced(count int) attribute.KeyValue

DiffReplaced creates an attribute for replaced resource count.

func DiffUpdated

func DiffUpdated(count int) attribute.KeyValue

DiffUpdated creates an attribute for updated resource count.

func EntrypointCount

func EntrypointCount(count int) attribute.KeyValue

EntrypointCount creates an attribute for entrypoint count.

func EntrypointDirectory

func EntrypointDirectory(dir string) attribute.KeyValue

EntrypointDirectory creates an attribute for entrypoint directory.

func EntrypointType

func EntrypointType(epType string) attribute.KeyValue

EntrypointType creates an attribute for entrypoint type.

func ErrorMessage

func ErrorMessage(msg string) attribute.KeyValue

ErrorMessage creates an attribute for error message.

func ErrorOccurred

func ErrorOccurred(occurred bool) attribute.KeyValue

ErrorOccurred creates an attribute indicating whether an error occurred.

func GitBranch

func GitBranch(branch string) attribute.KeyValue

GitBranch creates an attribute for git branch.

func GitDirectory

func GitDirectory(dir string) attribute.KeyValue

GitDirectory creates an attribute for git directory.

func GitHash

func GitHash(hash string) attribute.KeyValue

GitHash creates an attribute for git commit hash.

func GitPostRef

func GitPostRef(ref string) attribute.KeyValue

GitPostRef creates an attribute for post-change git reference.

func GitPreRef

func GitPreRef(ref string) attribute.KeyValue

GitPreRef creates an attribute for pre-change git reference.

func GitRef

func GitRef(ref string) attribute.KeyValue

GitRef creates an attribute for git reference.

func GitURL

func GitURL(url string) attribute.KeyValue

GitURL creates an attribute for git repository URL.

func PostDir

func PostDir(dir string) attribute.KeyValue

PostDir creates an attribute for post-change directory.

func PreDir

func PreDir(dir string) attribute.KeyValue

PreDir creates an attribute for pre-change directory.

func ResourceCount

func ResourceCount(count int) attribute.KeyValue

ResourceCount creates an attribute for resource count.

func ResourceName

func ResourceName(name string) attribute.KeyValue

ResourceName creates an attribute for resource name.

func ResourceType

func ResourceType(resType string) attribute.KeyValue

ResourceType creates an attribute for resource type.

func WorkingDir

func WorkingDir(dir string) attribute.KeyValue

WorkingDir creates an attribute for working directory.

Types

type Span

type Span struct {
	trace.Span
}

Span is a convenience wrapper around trace.Span that provides helper methods for common tracing operations.

func (Span) RecordError

func (s Span) RecordError(err error)

RecordError records an error on the span and sets the span status to error. This is a convenience method that combines RecordError and SetStatus.

func (Span) SetAttributes

func (s Span) SetAttributes(attrs ...attribute.KeyValue)

SetAttributes sets multiple attributes on the span at once.

func (Span) SetStatus

func (s Span) SetStatus(code codes.Code, description string)

SetStatus sets the status of the span.

type Tracer

type Tracer struct {
	// contains filtered or unexported fields
}

Tracer is a wrapper around OpenTelemetry's trace.Tracer interface that provides a simplified API for creating and managing spans.

Example

ExampleTracer demonstrates basic usage of the tracing package.

package main

import (
	"context"
	"fmt"

	"github.com/codingninja/gitops-repo-api/tracing"
)

func main() {
	// Create a no-op tracer (tracing disabled)
	tracer := tracing.NoOpTracer()

	ctx := context.Background()
	ctx, span := tracer.Start(ctx, "example.operation")
	defer span.End()

	// Add attributes to the span
	span.SetAttributes(
		tracing.GitURL("https://github.com/example/repo"),
		tracing.GitRef("main"),
	)

	fmt.Println("Operation traced")
}
Output:

Operation traced

func NewTracer

func NewTracer(tracer trace.Tracer) *Tracer

NewTracer creates a new Tracer instance wrapping the provided trace.Tracer. If tracer is nil, a no-op tracer is used, making tracing optional.

Example

ExampleNewTracer shows how to create a tracer with OpenTelemetry.

package main

import (
	"context"
	"fmt"

	"github.com/codingninja/gitops-repo-api/tracing"
	"go.opentelemetry.io/otel"
	"go.opentelemetry.io/otel/exporters/stdout/stdouttrace"

	sdktrace "go.opentelemetry.io/otel/sdk/trace"
)

func main() {
	// Create an OpenTelemetry tracer provider
	exporter, err := stdouttrace.New(stdouttrace.WithPrettyPrint())
	if err != nil {
		panic(err)
	}

	tp := sdktrace.NewTracerProvider(
		sdktrace.WithSampler(sdktrace.AlwaysSample()),
		sdktrace.WithBatcher(exporter),
	)
	defer tp.Shutdown(context.Background())

	// Set as global tracer provider
	otel.SetTracerProvider(tp)

	// Create a tracer
	otelTracer := tp.Tracer("gitops-repo-api")
	tracer := tracing.NewTracer(otelTracer)

	// Use the tracer
	ctx := context.Background()
	ctx, span := tracer.Start(ctx, "example.operation")
	defer span.End()

	fmt.Println("Traced with OpenTelemetry")
}
Output:

Traced with OpenTelemetry

func NoOpTracer

func NoOpTracer() *Tracer

NoOpTracer returns a tracer that performs no operations. This is useful for testing or when tracing is disabled.

func (*Tracer) Start

func (t *Tracer) Start(ctx context.Context, spanName string, opts ...trace.SpanStartOption) (context.Context, trace.Span)

Start creates a new span and returns both the updated context and the span. The span must be ended with span.End() when the operation completes. It's idiomatic to defer span.End() immediately after calling Start.

Example:

ctx, span := tracer.Start(ctx, "operation.name")
defer span.End()

func (*Tracer) StartSpan

func (t *Tracer) StartSpan(ctx context.Context, spanName string, opts ...trace.SpanStartOption) (context.Context, Span)

StartSpan is a helper function that creates a span and returns a wrapped Span. This provides access to the helper methods defined on Span.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL