logger/signoz_traces.go

277 lines
7.9 KiB
Go

package logger
import (
"context"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
"google.golang.org/grpc/credentials"
"math/rand"
"time"
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc"
api "go.opentelemetry.io/otel/metric"
metricsdk "go.opentelemetry.io/otel/sdk/metric"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
"log"
)
func InitTracer() func(ctx context.Context) error {
var (
secureOption otlptracegrpc.Option
collectorURL = cfgSignoz.Traces.GRPCEndPoint
serviceName = cfgSignoz.ServiceName
)
if !cfgSignoz.Traces.Insecure {
secureOption = otlptracegrpc.WithTLSCredentials(credentials.NewClientTLSFromCert(nil, ""))
} else {
secureOption = otlptracegrpc.WithInsecure()
}
exporter, err := otlptrace.New(
context.Background(),
otlptracegrpc.NewClient(
secureOption,
otlptracegrpc.WithEndpoint(collectorURL),
),
)
if err != nil {
panic(err)
}
resources, err := resource.New(
context.Background(),
resource.WithAttributes(
attribute.String("service.name", serviceName),
attribute.String("library.language", "go"),
),
)
if err != nil {
panic(err)
}
otel.SetTracerProvider(
sdktrace.NewTracerProvider(
sdktrace.WithSampler(sdktrace.AlwaysSample()),
sdktrace.WithBatcher(exporter),
sdktrace.WithResource(resources),
),
)
return exporter.Shutdown
}
func exceptionsCounter(meter api.Meter) {
counter, err := meter.Int64Counter("exceptions", api.WithUnit("1"),
api.WithDescription("Counts exceptions since start"),
)
if err != nil {
log.Fatal("Error in creating exceptions counter: ", err)
}
for {
// Increment the counter by 1.
// The attributes describe the exception.
counter.Add(context.Background(), 1, api.WithAttributes(attribute.KeyValue{
Key: attribute.Key("exception_type"), Value: attribute.StringValue("NullPointerException"),
}))
time.Sleep(time.Duration(rand.Int63n(5)) * time.Millisecond)
}
}
// Example use cases for async counter
// - count the number of page faults
// - CPU time, which could be reported for each thread, each process or the
// entire system. For example "the CPU time for process
// A running in user mode, measured in seconds".
//
// Basically, any value that is monotonically increasing and happens in the background.
// The increments should be non-negative.
func pageFaultsCounter(meter api.Meter) {
counter, err := meter.Int64ObservableCounter(
"page_faults",
api.WithUnit("1"),
api.WithDescription("Counts page faults since start"),
)
if err != nil {
log.Fatal("Error in creating page faults counter: ", err)
}
_, err = meter.RegisterCallback(
func(_ context.Context, o api.Observer) error {
attrSet := attribute.NewSet(attribute.String("process", "boo"))
withAttrSet := api.WithAttributeSet(attrSet)
o.ObserveInt64(counter, rand.Int63n(100), withAttrSet)
return nil
},
counter,
)
if err != nil {
log.Fatal("Error in registering callback: ", err)
}
}
// Example use cases for Histogram
// - the request duration
// - the size of the response payload
func requestDurationHistogram(meter api.Meter) {
histogram, err := meter.Int64Histogram(
"http_request_duration",
api.WithUnit("ms"),
api.WithDescription("The HTTP request duration in milliseconds"),
)
if err != nil {
log.Fatal("Error in creating request duration histogram: ", err)
}
for {
histogram.Record(context.Background(), rand.Int63n(1000), api.WithAttributes(attribute.String("path", "/api/boo")))
time.Sleep(time.Duration(rand.Int63n(5)) * time.Millisecond)
}
}
// Asynchronous Gauge is an Instrument
// which reports non-additive value(s)
// (e.g. the room temperature - it makes no sense to report the
// temperature value from multiple rooms and sum them up) when the
// instrument is being observed.
// Example use cases for Async Gauge
// - the current room temperature
// - the CPU fan speed
// Note: if the values are additive (e.g. the process heap size -
// it makes sense to report the heap size from multiple processes and sum them up,
// so we get the total heap usage),
// use Asynchronous Counter or Asynchronous UpDownCounter.
func roomTemperatureGauge(meter api.Meter) {
gauge, err := meter.Float64ObservableGauge(
"room_temperature",
api.WithUnit("1"),
api.WithDescription("The room temperature in celsius"),
)
if err != nil {
log.Fatal("Error in creating room temperature gauge: ", err)
}
_, err = meter.RegisterCallback(
func(_ context.Context, o api.Observer) error {
attrSet := attribute.NewSet(attribute.String("process", "boo"))
withAttrSet := api.WithAttributeSet(attrSet)
o.ObserveFloat64(gauge, rand.Float64()*100, withAttrSet)
return nil
},
gauge,
)
if err != nil {
log.Fatal("Error in registering callback: ", err)
}
}
// UpDownCounter is an Instrument which supports increments and decrements.
// if the value is monotonically increasing, use Counter instead.
// Example use cases for UpDownCounter
// - the number of active requests
// - the number of items in a queue
func itemsInQueueUpDownCounter(meter api.Meter) {
counter, err := meter.Int64UpDownCounter(
"items_in_queue",
api.WithUnit("1"),
api.WithDescription("The number of items in the queue"),
)
if err != nil {
log.Fatal("Error in creating items in queue up down counter: ", err)
}
for {
counter.Add(context.Background(), rand.Int63n(100), api.WithAttributes(attribute.String("queue", "A")))
time.Sleep(time.Duration(rand.Int63n(5)) * time.Millisecond)
}
}
// Asynchronous UpDownCounter is an asynchronous Instrument
// which reports additive value(s)
// (e.g. the process heap size - it makes sense to report the heap size
// from multiple processes and sum them up, so we get the total heap usage)
// when the instrument is being observed.
//
// Example use cases for Asynchronous UpDownCounter
// - the process heap size
// - the approximate number of items in a lock-free circular buffer
func processHeapSizeUpDownCounter(meter api.Meter) {
counter, err := meter.Float64ObservableUpDownCounter(
"process_heap_size",
api.WithUnit("1"),
api.WithDescription("The process heap size"),
)
if err != nil {
log.Fatal("Error in creating process heap size up down counter: ", err)
}
_, err = meter.RegisterCallback(
func(_ context.Context, o api.Observer) error {
attrSet := attribute.NewSet(attribute.String("process", "boo"))
withAttrSet := api.WithAttributeSet(attrSet)
o.ObserveFloat64(counter, rand.Float64()*100, withAttrSet)
return nil
},
counter,
)
if err != nil {
log.Fatal("Error in registering callback: ", err)
}
}
func InitMeter() (*metricsdk.MeterProvider, api.Meter) {
var (
secureOption otlpmetricgrpc.Option
)
if !cfgSignoz.Traces.Insecure {
secureOption = otlpmetricgrpc.WithTLSCredentials(credentials.NewClientTLSFromCert(nil, ""))
} else {
secureOption = otlpmetricgrpc.WithInsecure()
}
exporter, err := otlpmetricgrpc.New(
context.Background(),
secureOption,
otlpmetricgrpc.WithEndpoint(cfgSignoz.Traces.GRPCEndPoint),
)
if err != nil {
log.Fatalf("Failed to create exporter: %v", err)
}
res, err := resource.New(
context.Background(),
resource.WithAttributes(
attribute.String("service.name", cfgSignoz.ServiceName),
attribute.String("library.language", "go"),
),
)
if err != nil {
log.Fatalf("Could not set resources: %v", err)
}
// Register the exporter with an SDK via a periodic reader.
provider := metricsdk.NewMeterProvider(
metricsdk.WithResource(res),
metricsdk.WithReader(metricsdk.NewPeriodicReader(exporter)),
)
return provider, provider.Meter(cfgSignoz.ServiceName)
}
func GenerateMetrics(meter api.Meter) {
go exceptionsCounter(meter)
go pageFaultsCounter(meter)
go requestDurationHistogram(meter)
go roomTemperatureGauge(meter)
go itemsInQueueUpDownCounter(meter)
go processHeapSizeUpDownCounter(meter)
}