Path: blob/main/component/prometheus/exporter/exporter.go
4095 views
package exporter12import (3"context"4"fmt"5"net/http"6"path"7"sync"89"github.com/go-kit/log/level"10"github.com/grafana/agent/component"11"github.com/grafana/agent/component/discovery"12"github.com/grafana/agent/pkg/integrations"13"github.com/prometheus/common/model"14)1516// Creator is a function provided by an implementation to create a concrete exporter instance.17type Creator func(component.Options, component.Arguments) (integrations.Integration, error)1819// Exports are simply a list of targets for a scraper to consume.20type Exports struct {21Targets []discovery.Target `river:"targets,attr"`22}2324type Component struct {25opts component.Options2627mut sync.Mutex2829reload chan struct{}3031creator Creator32multiTargetFunc func(discovery.Target, component.Arguments) []discovery.Target33baseTarget discovery.Target3435exporter integrations.Integration36metricsHandler http.Handler37}3839// New creates a new exporter component.40func New(creator Creator, name string) func(component.Options, component.Arguments) (component.Component, error) {41return newExporter(creator, name, nil)42}4344// NewMultiTarget creates a new exporter component that supports multiple targets.45func NewMultiTarget(creator Creator, name string, multiTargetFunc func(discovery.Target, component.Arguments) []discovery.Target) func(component.Options, component.Arguments) (component.Component, error) {46return newExporter(creator, name, multiTargetFunc)47}4849func newExporter(creator Creator, name string, multiTargetFunc func(discovery.Target, component.Arguments) []discovery.Target) func(component.Options, component.Arguments) (component.Component, error) {50return func(opts component.Options, args component.Arguments) (component.Component, error) {51c := &Component{52opts: opts,53reload: make(chan struct{}, 1),54creator: creator,55multiTargetFunc: multiTargetFunc,56}57jobName := fmt.Sprintf("integrations/%s", name)58c.baseTarget = discovery.Target{59model.AddressLabel: opts.HTTPListenAddr,60model.SchemeLabel: "http",61model.MetricsPathLabel: path.Join(opts.HTTPPath, "metrics"),62"instance": opts.ID,63"job": jobName,64"__meta_agent_integration_name": jobName,65"__meta_agent_integration_instance": opts.ID,66}6768// Call to Update() to set the output once at the start.69if err := c.Update(args); err != nil {70return nil, err71}7273return c, nil74}75}7677// Run implements component.Component.78func (c *Component) Run(ctx context.Context) error {79var cancel context.CancelFunc80for {81select {82case <-ctx.Done():83return nil84case <-c.reload:85// cancel any previously running exporter86if cancel != nil {87cancel()88}89// create new context so we can cancel it if we get any future updates90// since it is derived from the main run context, it only needs to be91// canceled directly if we receive new updates92newCtx, cancelFunc := context.WithCancel(ctx)93cancel = cancelFunc9495// finally create and run new exporter96c.mut.Lock()97exporter := c.exporter98c.metricsHandler = c.getHttpHandler(exporter)99c.mut.Unlock()100go func() {101if err := exporter.Run(newCtx); err != nil {102level.Error(c.opts.Logger).Log("msg", "error running exporter", "err", err)103}104}()105}106}107}108109// Update implements component.Component.110func (c *Component) Update(args component.Arguments) error {111exporter, err := c.creator(c.opts, args)112if err != nil {113return err114}115c.mut.Lock()116c.exporter = exporter117118var targets []discovery.Target119if c.multiTargetFunc == nil {120targets = []discovery.Target{c.baseTarget}121} else {122targets = c.multiTargetFunc(c.baseTarget, args)123}124125c.opts.OnStateChange(Exports{126Targets: targets,127})128c.mut.Unlock()129select {130case c.reload <- struct{}{}:131default:132}133return err134}135136// get the http handler once and save it, so we don't create extra garbage137func (c *Component) getHttpHandler(integration integrations.Integration) http.Handler {138h, err := integration.MetricsHandler()139if err != nil {140level.Error(c.opts.Logger).Log("msg", "failed to creating metrics handler", "err", err)141return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {142w.WriteHeader(http.StatusInternalServerError)143})144}145return h146}147148// Handler serves metrics endpoint from the integration implementation.149func (c *Component) Handler() http.Handler {150c.mut.Lock()151defer c.mut.Unlock()152return c.metricsHandler153}154155156