flowmaster

module
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Nov 9, 2024 License: MIT

README

TraceFlow - Simplified OpenTelemetry Tracing for Go

TraceFlow is a Go package that provides a simple, fluent interface for integrating OpenTelemetry distributed tracing into your Go applications. It abstracts away much of the complexity and boilerplate involved in creating and managing traces, allowing developers to focus on their application logic while still benefiting from powerful distributed tracing features.

Why Use TraceFlow?

In modern, distributed systems, it's crucial to have observability tools like tracing to monitor and debug complex interactions between microservices, databases, and other components. OpenTelemetry is a robust solution for capturing this telemetry data, but its complexity can make initial adoption challenging. This is where TraceFlow comes in.

Key Features and Benefits:

  • Fluent Interface: Chain methods together to add attributes, links, and status information to your trace with minimal code.
    • Example: defer trace.Start("operation").AddAttribute(...).End()
  • Automatic System Information: Automatically capture and add CPU, memory, and disk information to your traces without manual setup.
  • Built-in Best Practices: Enforces best practices such as always ending spans and recording errors and success states, helping you avoid common pitfalls.
  • Extendable: Easily extend the functionality with your own custom attributes or predefined behavior using variadic options.
  • Error and Exception Handling: Capture detailed error and exception information, including stack traces and error messages.
  • Context Propagation: Simplifies passing and extracting trace context across service boundaries (e.g., HTTP requests).

Who Should Use This Package?

  • Go Developers working in microservice-based or distributed systems who want to integrate OpenTelemetry tracing without writing verbose boilerplate code.
  • Teams and companies seeking enhanced observability to monitor their distributed systems in production environments.
  • Developers who want to add tracing to existing services with minimal code changes and minimal learning curve.
  • Anyone who wants a lightweight, flexible tracing solution that can scale with their system and evolve as new OpenTelemetry features are introduced.

Installation

To install TraceFlow, use go get:

go get github.com/wendall-robinson/flowmaster/traceflow

Quick Start

Basic Usage Example

Here's how you can start tracing operations in your Go application using TraceFlow:

package main

import (
    "context"
    "log"

    "github.com/wendall-robinson/flowmaster/traceflow"
)

func main() {
    ctx := context.Background()

    // Initialize OpenTelemetry
    ctx, shutdown, err := traceflow.Init(ctx, "my-service")
    if err != nil {
        log.Fatalf("Failed to initialize OpenTelemetry: %v", err)
    }

    // Make sure to defer the shutdown for proper cleanup
    defer shutdown(ctx)

    // Create a new trace
    trace := traceflow.New(ctx, "example-service")

    // Start a new operation and add attributes
    defer trace.Start("main-operation").End()

    // Add custom attributes
    trace.AddAttribute(
        traceflow.AddString("user_id", "12345"),
        traceflow.AddInt("response_time", 200),
    )

    // Simulate an error and record it
    err := someOperation()
    if err != nil {
        trace.RecordFailure(err, "failed to complete operation")
    }
}
Key Features Demonstrated:
  • Initializing OpenTelemetry: Using traceflow.Init to initialize OpenTelemetry with optional settings (such as a custom logger) and ensure proper tracing setup in your application.
  • Creating and Starting a Trace: traceflow.New and trace.Start.
  • Adding Attributes: trace.AddAttribute to capture custom key-value pairs.
  • Error Handling: Using trace.RecordFailure to record errors and failures within the trace.

Default Context Propagation

By default, when you create a new trace using the New() method, the trace context is automatically copied from the provided context (if it exists). This ensures that the trace is linked to any existing parent trace, making it easier to maintain the trace chain across distributed services.

Example usage:

// Incoming request with an existing trace context
ctx := r.Context() // from an HTTP request

// Create a new trace, linked to the parent trace (if it exists)
trace := traceflow.New(ctx, "my-service")
trace.Start("operation").End()

OpenTelemetry Initialization

To make it easier for users to initialize OpenTelemetry tracing in their services, traceflow provides an Init function that sets up OpenTelemetry with default or custom options.

Basic Usage:

package main

import (
    "context"
    "log"

    "github.com/yourusername/traceflow"
)

func main() {
    ctx := context.Background()

    // Initialize OpenTelemetry
    ctx, shutdown, err := traceflow.Init(ctx, "my-service")
    if err != nil {
        log.Fatalf("Failed to initialize OpenTelemetry: %v", err)
    }

    // Make sure to defer the shutdown for proper cleanup
    defer shutdown(ctx)

    // Your application logic...
}

Advanced Features

Advanced Features: Starting a Fresh Trace Without Context Propagation

If you want to start a fresh trace and not propagate the existing trace context, use the NewWithoutPropagation() method. This allows you to create an independent trace.

Example usage:

// Start a new trace without copying the existing trace context
trace := traceflow.NewWithoutPropagation(ctx, "my-service")
trace.Start("operation").End()
Advanced Features: System Information
  • Adding System Information: Automatically add CPU, memory, and disk usage to your traces:
    trace.AddCpuInfo().AddMemoryInfo().AddDiskInfo()
    
  • Add all System Information: (adds CPU, Memory and Disk info)
    trace.WithSystemInfo()
    
Advanced Features: Injecting Trace Context into HTTP Requests

In distributed systems, it's important to propagate the trace context across service boundaries, allowing each service to continue a trace. This is particularly useful in microservice architectures, where HTTP requests are often used to communicate between services.

With the InjectHTTPContext method, you can easily inject the trace context into the headers of an outgoing HTTP request, ensuring that the trace information is passed along to downstream services.

InjectHttpContext
package main

import (
	"context"
	"fmt"
	"net/http"
	"log"
	"time"

	"github.com/wendall-robinson/flowmaster/traceflow"
)

func main() {
	// Create a new trace
	ctx := context.Background()
	trace := traceflow.New(ctx, "example-service")

	// Start a new span for the operation
	defer trace.Start("http-outgoing-request").End()

	// Create an HTTP request to be sent to another service
	req, err := http.NewRequest("GET", "http://example.com", nil)
	if err != nil {
		log.Fatalf("Failed to create HTTP request: %v", err)
	}

	// Inject the trace context into the request headers
	trace.InjectHTTPContext(req)

	// Send the request using an HTTP client
	client := &http.Client{Timeout: 10 * time.Second}

	resp, err := client.Do(req)
	if err != nil {
		log.Fatalf("Failed to send request: %v", err)
	}

	defer resp.Body.Close()

	fmt.Printf("Response status: %s\n", resp.Status)
}

How It Works:

  • A new trace is started with traceflow.New().
  • The InjectHTTPContext method automatically injects the trace context into the request headers, making it possible for the receiving service to extract and continue the trace.
  • The downstream service can use the corresponding ExtractHTTPContext method to continue the trace.

This pattern ensures seamless propagation of trace context between services, providing end-to-end visibility in distributed tracing.

ExtractHTTPContext

With the ExtractHTTPContext method, you can easily extract the trace context from incoming request headers and continue the trace.

package main

import (
	"context"
	"fmt"
	"net/http"
	"log"
	"github.com/wendall-robinson/flowmaster/traceflow"
)

func main() {
	// Start an HTTP server to receive requests
	http.HandleFunc("/process", func(w http.ResponseWriter, req *http.Request) {
		// Create a new trace, using the incoming request's context
		ctx := context.Background()
		trace := traceflow.New(ctx, "example-service")

		// Extract the trace context from the incoming request headers
		trace.ExtractHTTPContext(req)

		// Start a new span for this operation
		defer trace.Start("process-request").End()

		// Simulate some processing
		fmt.Fprintln(w, "Processing request...")

		log.Println("Request processed, trace continued.")
	})

	// Start the HTTP server
	log.Println("Server started on :8080")
	log.Fatal(http.ListenAndServe(":8080", nil))
}

How It Works:

  • A new trace is initialized with traceflow.New().
  • The ExtractHTTPContext method extracts the trace context from the incoming HTTP request's headers. This allows the trace context to be continued from where the upstream service left off.
  • A new span is started for the current operation (process-request) and ended after the operation completes.

Usage:

When the client service sends a request (such as in the earlier example with InjectHTTPContext), the trace context is passed along in the request headers. The receiving service extracts the context with ExtractHTTPContext and can continue the trace, creating a new span for the current operation.

Directories

Path Synopsis
examples
Package traceflow provides a simple wrapper around OpenTelemetry to make it easier to create and manage traces.
Package traceflow provides a simple wrapper around OpenTelemetry to make it easier to create and manage traces.
internal/errors
Package errors is a custom package that holds error definitions for traceflow.
Package errors is a custom package that holds error definitions for traceflow.

Jump to

Keyboard shortcuts

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