Files
query-orchestration/internal/serviceconfig/observability/config.go
T
Jay Brown 8e4d66a998 Merged in feature/versionTimestamps (pull request #98)
add GetVersionTimestamp

* add GetVersionTimestamp

* docs
2025-03-10 20:16:44 +00:00

152 lines
3.3 KiB
Go

package observability
import (
"context"
"log/slog"
"sync"
"time"
"runtime/debug"
"queryorchestration/internal/serviceconfig/observability/prometheus"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
)
const (
SystemName = "doczy"
)
var startupTime time.Time
var startupTimeOnce sync.Once
func GetVersionTimestamp() time.Time {
startupTimeOnce.Do(func() {
info, ok := debug.ReadBuildInfo()
if !ok {
startupTime = time.Now()
return
}
// Look for vcs.time in build settings
for _, setting := range info.Settings {
if setting.Key == "vcs.time" {
// Parse the build timestamp
buildTime, err := time.Parse(time.RFC3339, setting.Value)
if err == nil {
startupTime = buildTime
return
}
break
}
}
// Return current time if no build time found
startupTime = time.Now()
})
return startupTime
}
// GetVersion returns the version of the application in the Golang build info format.
// This is a raw format and will change depending on the build environment and the git tags.
// When building from a commit with an associated Git tag (e.g., v1.2.3),
//
// the runtime version uses the tag name without a timestamp.
//
// The "+dirty" suffix is appended when there are uncommitted changes
//
// present in the version control system (VCS) at the time of building.
//
// An example of a version v0.0.2-0.20250310175756-a91372dc0602+dirty.
func GetVersion() string {
info, ok := debug.ReadBuildInfo()
if !ok {
return "unknown"
}
return info.Main.Version
}
type ConfigProvider interface {
IsOtelEnabled() bool
SetOtel(context.Context) func() error
GetMetrics() *prometheus.Metrics
SetMetrics(metrics *prometheus.Metrics)
}
type ObsConfig struct {
EnableOtel bool `env:"ENABLE_OTEL" envDefault:"false"`
metrics *prometheus.Metrics
}
func (o *ObsConfig) GetMetrics() *prometheus.Metrics {
return o.metrics
}
func (o *ObsConfig) SetMetrics(metrics *prometheus.Metrics) {
o.metrics = metrics
}
func (o *ObsConfig) IsOtelEnabled() bool {
return o.EnableOtel
}
func (o *ObsConfig) SetOtel(ctx context.Context) func() error {
if !o.EnableOtel {
slog.Warn("OpenTelemetry is disabled. Set ENABLE_OTEL to true to enable.")
return func() error { return nil }
}
exporter, err := otlptracegrpc.New(ctx)
if err != nil {
return func() error { return nil }
}
tp := sdktrace.NewTracerProvider(
sdktrace.WithBatcher(exporter),
)
otel.SetTracerProvider(tp)
return func() error {
err := tp.Shutdown(ctx)
if err != nil {
return err
}
return nil
}
}
const (
SubSystem = "RunnerService"
)
func InitializeMetrics(ctx context.Context, cfg ConfigProvider) error {
standardDefinitions := prometheus.NewStandardMetricDefinitions()
config := prometheus.MetricsConfig{
Port: 8080,
Namespace: SystemName,
Subsystem: SubSystem,
ServeHttp: true,
Definitions: standardDefinitions,
}
metrics, err := prometheus.New(ctx, config)
if err != nil {
slog.Error("Failed to create metrics object", "error", err)
return err
}
slog.Info("Metrics server started on port 8080")
cfg.SetMetrics(metrics)
err = cfg.GetMetrics().RecordLifecycleEvent(1.0, "startup", "main")
if err != nil {
slog.Error("Failed to create metrics at startup", "error", err)
return err
}
return nil
}