1
0
mirror of https://github.com/imgproxy/imgproxy.git synced 2025-12-23 22:11:10 +02:00
Files
imgproxy/monitoring/monitoring.go
Victor Sokolov 69c7d2f117 IMG-76: new linter config & fixes (#1587)
* IMG-76: new linter config & fixes

* Upgrade golangci-lint to 2.7.2
2025-12-10 13:46:22 +01:00

189 lines
4.6 KiB
Go

package monitoring
import (
"context"
"net/http"
"github.com/imgproxy/imgproxy/v3/errctx"
"github.com/imgproxy/imgproxy/v3/monitoring/cloudwatch"
"github.com/imgproxy/imgproxy/v3/monitoring/datadog"
"github.com/imgproxy/imgproxy/v3/monitoring/newrelic"
"github.com/imgproxy/imgproxy/v3/monitoring/otel"
"github.com/imgproxy/imgproxy/v3/monitoring/prometheus"
"github.com/imgproxy/imgproxy/v3/monitoring/stats"
)
// monitor is an interface for monitoring services
type monitor interface {
Stop(ctx context.Context)
StartRequest(
ctx context.Context,
rw http.ResponseWriter,
r *http.Request,
) (context.Context, context.CancelFunc, http.ResponseWriter)
StartSpan(
ctx context.Context,
name string,
meta map[string]any,
) (context.Context, context.CancelFunc)
SetMetadata(ctx context.Context, key string, value any)
SendError(ctx context.Context, errType string, err errctx.Error)
InjectHeaders(ctx context.Context, headers http.Header)
}
// Monitoring holds all monitoring service instances
type Monitoring struct {
config *Config
stats *stats.Stats
monitors []monitor
}
// New creates a new Monitoring instance
func New(ctx context.Context, config *Config, workersNumber int) (*Monitoring, error) {
if err := config.Validate(); err != nil {
return nil, err
}
st := stats.New(workersNumber)
monitors := make([]monitor, 0)
if m, err := prometheus.New(&config.Prometheus, st); err != nil {
return nil, err
} else if m != nil {
monitors = append(monitors, m)
}
if m, err := newrelic.New(&config.NewRelic, st); err != nil {
return nil, err
} else if m != nil {
monitors = append(monitors, m)
}
if m, err := datadog.New(&config.DataDog, st); err != nil {
return nil, err
} else if m != nil {
monitors = append(monitors, m)
}
if m, err := otel.New(&config.OpenTelemetry, st); err != nil {
return nil, err
} else if m != nil {
monitors = append(monitors, m)
}
if m, err := cloudwatch.New(ctx, &config.CloudWatch, st); err != nil {
return nil, err
} else if m != nil {
monitors = append(monitors, m)
}
m := &Monitoring{
config: config,
stats: st,
monitors: monitors,
}
return m, nil
}
// Enabled returns true if at least one monitoring service is enabled
func (m *Monitoring) Enabled() bool {
return len(m.monitors) > 0
}
// Stats returns the stats instance
func (m *Monitoring) Stats() *stats.Stats {
return m.stats
}
// Stop stops all monitoring services
func (m *Monitoring) Stop(ctx context.Context) {
for _, monitor := range m.monitors {
monitor.Stop(ctx)
}
}
// StartPrometheus starts the Prometheus metrics server
func (m *Monitoring) StartPrometheus(cancel context.CancelFunc) error {
for _, monitor := range m.monitors {
if prom, ok := monitor.(*prometheus.Prometheus); ok {
return prom.StartServer(cancel)
}
}
return nil
}
// StartRequest starts a new request span
func (m *Monitoring) StartRequest(
ctx context.Context,
rw http.ResponseWriter,
r *http.Request,
) (context.Context, context.CancelFunc, http.ResponseWriter) {
cancels := make([]context.CancelFunc, 0, len(m.monitors))
for _, monitor := range m.monitors {
var cancel context.CancelFunc
//nolint:fatcontext
ctx, cancel, rw = monitor.StartRequest(ctx, rw, r)
cancels = append(cancels, cancel)
}
cancel := func() {
for _, c := range cancels {
c()
}
}
return ctx, cancel, rw
}
// SetMetadata sets metadata key-value pair for all monitoring services
func (m *Monitoring) SetMetadata(ctx context.Context, meta Meta) {
for _, monitor := range m.monitors {
for key, value := range meta {
monitor.SetMetadata(ctx, key, value)
}
}
}
// StartSpan starts a new trace span as child of the current span
func (m *Monitoring) StartSpan(
ctx context.Context,
name string,
meta Meta,
) (context.Context, context.CancelFunc) {
cancels := make([]context.CancelFunc, 0, len(m.monitors))
for _, monitor := range m.monitors {
var cancel context.CancelFunc
//nolint:fatcontext
ctx, cancel = monitor.StartSpan(ctx, name, meta)
cancels = append(cancels, cancel)
}
cancel := func() {
for _, c := range cancels {
c()
}
}
return ctx, cancel
}
// SendError sends an error to all monitoring services
func (m *Monitoring) SendError(ctx context.Context, errType string, err errctx.Error) {
for _, monitor := range m.monitors {
monitor.SendError(ctx, errType, err)
}
}
// InjectHeaders adds monitoring headers to the provided HTTP headers.
// These headers can be used to correlate requests across different services.
func (m *Monitoring) InjectHeaders(ctx context.Context, headers http.Header) {
for _, monitor := range m.monitors {
monitor.InjectHeaders(ctx, headers)
}
}