package myfunction
import (
"context"
"fmt"
"log"
"net/http"
"os"
"github.com/GoogleCloudPlatform/functions-framework-go/functions"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
"go.opentelemetry.io/otel/propagation"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
)
var tracerProvider *sdktrace.TracerProvider
func init() {
var err error
tracerProvider, err = initTracer()
if err != nil {
log.Fatalf("Failed to initialize OpenTelemetry: %v", err)
}
// Register the wrapped HTTP function with the Functions Framework
functions.HTTP("HelloHTTP", otelWrapper(helloHTTP))
}
func initTracer() (*sdktrace.TracerProvider, error) {
ctx := context.Background()
apiKey := os.Getenv("ATATUS_API_KEY")
serviceName := os.Getenv("ATATUS_APP_NAME")
if serviceName == "" {
serviceName = "gcp-cloud-function"
}
// Configure OTLP HTTP Exporter pointing to Atatus collector
exporter, err := otlptracehttp.New(ctx,
otlptracehttp.WithEndpoint("otel-rx.atatus.com"),
otlptracehttp.WithURLPath("/v1/traces"),
otlptracehttp.WithHeaders(map[string]string{
"api-key": apiKey,
}),
)
if err != nil {
return nil, fmt.Errorf("creating OTLP trace exporter: %w", err)
}
res, err := resource.New(ctx,
resource.WithAttributes(
semconv.ServiceNameKey.String(serviceName),
attribute.String("cloud.provider", "gcp"),
attribute.String("cloud.platform", "gcp_cloud_functions"),
),
)
if err != nil {
return nil, fmt.Errorf("creating resource: %w", err)
}
tp := sdktrace.NewTracerProvider(
sdktrace.WithBatcher(exporter),
sdktrace.WithResource(res),
)
otel.SetTracerProvider(tp)
otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(
propagation.TraceContext{},
propagation.Baggage{},
))
return tp, nil
}
// otelWrapper traces the request and ensures traces are flushed before the runtime freezes CPU
func otelWrapper(next http.HandlerFunc) http.HandlerFunc {
wrapped := otelhttp.NewHandler(next, "gcp-cloud-function")
return func(w http.ResponseWriter, r *http.Request) {
defer func() {
if tracerProvider != nil {
if err := tracerProvider.ForceFlush(r.Context()); err != nil {
log.Printf("Failed to flush OpenTelemetry traces: %v", err)
}
}
}()
wrapped.ServeHTTP(w, r)
}
}
func helloHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Hello, World!")
}