mirror of
https://github.com/prometheus/prometheus.git
synced 2025-01-12 22:37:27 -08:00
Avoid inter-component blocking if ingestion/scraping blocks.
Appending to the storage can block for a long time. Timing out scrapes can also cause longer blocks. This commit avoids that those blocks affect other compnents than the target itself. Also the Target interface was removed.
This commit is contained in:
parent
1a2d57b45c
commit
385919a65a
|
@ -67,72 +67,35 @@ func init() {
|
||||||
prometheus.MustRegister(targetIntervalLength)
|
prometheus.MustRegister(targetIntervalLength)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TargetState describes the state of a Target.
|
// TargetHealth describes the health state of a target.
|
||||||
type TargetState int
|
type TargetHealth int
|
||||||
|
|
||||||
func (t TargetState) String() string {
|
func (t TargetHealth) String() string {
|
||||||
switch t {
|
switch t {
|
||||||
case Unknown:
|
case HealthUnknown:
|
||||||
return "UNKNOWN"
|
return "UNKNOWN"
|
||||||
case Healthy:
|
case HealthGood:
|
||||||
return "HEALTHY"
|
return "HEALTHY"
|
||||||
case Unhealthy:
|
case HealthBad:
|
||||||
return "UNHEALTHY"
|
return "UNHEALTHY"
|
||||||
}
|
}
|
||||||
|
|
||||||
panic("unknown state")
|
panic("unknown state")
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// Unknown is the state of a Target before it is first scraped.
|
// Unknown is the state of a Target before it is first scraped.
|
||||||
Unknown TargetState = iota
|
HealthUnknown TargetHealth = iota
|
||||||
// Healthy is the state of a Target that has been successfully scraped.
|
// Healthy is the state of a Target that has been successfully scraped.
|
||||||
Healthy
|
HealthGood
|
||||||
// Unhealthy is the state of a Target that was scraped unsuccessfully.
|
// Unhealthy is the state of a Target that was scraped unsuccessfully.
|
||||||
Unhealthy
|
HealthBad
|
||||||
)
|
)
|
||||||
|
|
||||||
// A Target represents an endpoint that should be interrogated for metrics.
|
|
||||||
//
|
|
||||||
// The protocol described by this type will likely change in future iterations,
|
|
||||||
// as it offers no good support for aggregated targets and fan out. Thusly,
|
|
||||||
// it is likely that the current Target and target uses will be
|
|
||||||
// wrapped with some resolver type.
|
|
||||||
//
|
|
||||||
// For the future, the Target protocol will abstract away the exact means that
|
|
||||||
// metrics are retrieved and deserialized from the given instance to which it
|
|
||||||
// refers.
|
|
||||||
//
|
|
||||||
// Target implements extraction.Ingester.
|
|
||||||
type Target interface {
|
|
||||||
extraction.Ingester
|
|
||||||
|
|
||||||
// Status returns the current status of the target.
|
|
||||||
Status() *TargetStatus
|
|
||||||
// The URL to which the Target corresponds. Out of all of the available
|
|
||||||
// points in this interface, this one is the best candidate to change given
|
|
||||||
// the ways to express the endpoint.
|
|
||||||
URL() string
|
|
||||||
// Used to populate the `instance` label in metrics.
|
|
||||||
InstanceIdentifier() string
|
|
||||||
// Return the labels describing the targets. These are the base labels
|
|
||||||
// as well as internal labels.
|
|
||||||
fullLabels() clientmodel.LabelSet
|
|
||||||
// Return the target's base labels.
|
|
||||||
BaseLabels() clientmodel.LabelSet
|
|
||||||
// Start scraping the target in regular intervals.
|
|
||||||
RunScraper(storage.SampleAppender)
|
|
||||||
// Stop scraping, synchronous.
|
|
||||||
StopScraper()
|
|
||||||
// Update the target's state.
|
|
||||||
Update(*config.ScrapeConfig, clientmodel.LabelSet)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TargetStatus contains information about the current status of a scrape target.
|
// TargetStatus contains information about the current status of a scrape target.
|
||||||
type TargetStatus struct {
|
type TargetStatus struct {
|
||||||
lastError error
|
lastError error
|
||||||
lastScrape time.Time
|
lastScrape time.Time
|
||||||
state TargetState
|
health TargetHealth
|
||||||
|
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
}
|
}
|
||||||
|
@ -151,11 +114,11 @@ func (ts *TargetStatus) LastScrape() time.Time {
|
||||||
return ts.lastScrape
|
return ts.lastScrape
|
||||||
}
|
}
|
||||||
|
|
||||||
// State returns the last known health state of the target.
|
// Health returns the last known health state of the target.
|
||||||
func (ts *TargetStatus) State() TargetState {
|
func (ts *TargetStatus) Health() TargetHealth {
|
||||||
ts.mu.RLock()
|
ts.mu.RLock()
|
||||||
defer ts.mu.RUnlock()
|
defer ts.mu.RUnlock()
|
||||||
return ts.state
|
return ts.health
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ts *TargetStatus) setLastScrape(t time.Time) {
|
func (ts *TargetStatus) setLastScrape(t time.Time) {
|
||||||
|
@ -168,15 +131,20 @@ func (ts *TargetStatus) setLastError(err error) {
|
||||||
ts.mu.Lock()
|
ts.mu.Lock()
|
||||||
defer ts.mu.Unlock()
|
defer ts.mu.Unlock()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
ts.state = Healthy
|
ts.health = HealthGood
|
||||||
} else {
|
} else {
|
||||||
ts.state = Unhealthy
|
ts.health = HealthBad
|
||||||
}
|
}
|
||||||
ts.lastError = err
|
ts.lastError = err
|
||||||
}
|
}
|
||||||
|
|
||||||
// target is a Target that refers to a singular HTTP or HTTPS endpoint.
|
// Target refers to a singular HTTP or HTTPS endpoint.
|
||||||
type target struct {
|
type Target struct {
|
||||||
|
// The status object for the target. It is only set once on initialization.
|
||||||
|
status *TargetStatus
|
||||||
|
// The HTTP client used to scrape the target's endpoint.
|
||||||
|
httpClient *http.Client
|
||||||
|
|
||||||
// Closing scraperStopping signals that scraping should stop.
|
// Closing scraperStopping signals that scraping should stop.
|
||||||
scraperStopping chan struct{}
|
scraperStopping chan struct{}
|
||||||
// Closing scraperStopped signals that scraping has been stopped.
|
// Closing scraperStopped signals that scraping has been stopped.
|
||||||
|
@ -184,14 +152,9 @@ type target struct {
|
||||||
// Channel to buffer ingested samples.
|
// Channel to buffer ingested samples.
|
||||||
ingestedSamples chan clientmodel.Samples
|
ingestedSamples chan clientmodel.Samples
|
||||||
|
|
||||||
// The status object for the target. It is only set once on initialization.
|
|
||||||
status *TargetStatus
|
|
||||||
// The HTTP client used to scrape the target's endpoint.
|
|
||||||
httpClient *http.Client
|
|
||||||
|
|
||||||
// Mutex protects the members below.
|
// Mutex protects the members below.
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
|
// url is the URL to be scraped. Its host is immutable.
|
||||||
url *url.URL
|
url *url.URL
|
||||||
// Any base labels that are added to this target and its metrics.
|
// Any base labels that are added to this target and its metrics.
|
||||||
baseLabels clientmodel.LabelSet
|
baseLabels clientmodel.LabelSet
|
||||||
|
@ -202,8 +165,8 @@ type target struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTarget creates a reasonably configured target for querying.
|
// NewTarget creates a reasonably configured target for querying.
|
||||||
func NewTarget(cfg *config.ScrapeConfig, baseLabels clientmodel.LabelSet) Target {
|
func NewTarget(cfg *config.ScrapeConfig, baseLabels clientmodel.LabelSet) *Target {
|
||||||
t := &target{
|
t := &Target{
|
||||||
url: &url.URL{
|
url: &url.URL{
|
||||||
Host: string(baseLabels[clientmodel.AddressLabel]),
|
Host: string(baseLabels[clientmodel.AddressLabel]),
|
||||||
},
|
},
|
||||||
|
@ -215,14 +178,14 @@ func NewTarget(cfg *config.ScrapeConfig, baseLabels clientmodel.LabelSet) Target
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
|
||||||
// Status implements the Target interface.
|
// Status returns the status of the target.
|
||||||
func (t *target) Status() *TargetStatus {
|
func (t *Target) Status() *TargetStatus {
|
||||||
return t.status
|
return t.status
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update overwrites settings in the target that are derived from the job config
|
// Update overwrites settings in the target that are derived from the job config
|
||||||
// it belongs to.
|
// it belongs to.
|
||||||
func (t *target) Update(cfg *config.ScrapeConfig, baseLabels clientmodel.LabelSet) {
|
func (t *Target) Update(cfg *config.ScrapeConfig, baseLabels clientmodel.LabelSet) {
|
||||||
t.Lock()
|
t.Lock()
|
||||||
defer t.Unlock()
|
defer t.Unlock()
|
||||||
|
|
||||||
|
@ -248,12 +211,15 @@ func (t *target) Update(cfg *config.ScrapeConfig, baseLabels clientmodel.LabelSe
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *target) String() string {
|
func (t *Target) String() string {
|
||||||
return t.url.Host
|
return t.url.Host
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ingest implements Target and extraction.Ingester.
|
// Ingest implements an extraction.Ingester.
|
||||||
func (t *target) Ingest(s clientmodel.Samples) error {
|
func (t *Target) Ingest(s clientmodel.Samples) error {
|
||||||
|
t.RLock()
|
||||||
|
deadline := t.deadline
|
||||||
|
t.RUnlock()
|
||||||
// Since the regular case is that ingestedSamples is ready to receive,
|
// Since the regular case is that ingestedSamples is ready to receive,
|
||||||
// first try without setting a timeout so that we don't need to allocate
|
// first try without setting a timeout so that we don't need to allocate
|
||||||
// a timer most of the time.
|
// a timer most of the time.
|
||||||
|
@ -264,14 +230,17 @@ func (t *target) Ingest(s clientmodel.Samples) error {
|
||||||
select {
|
select {
|
||||||
case t.ingestedSamples <- s:
|
case t.ingestedSamples <- s:
|
||||||
return nil
|
return nil
|
||||||
case <-time.After(t.deadline / 10):
|
case <-time.After(deadline / 10):
|
||||||
return errIngestChannelFull
|
return errIngestChannelFull
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure that Target implements extraction.Ingester at compile time.
|
||||||
|
var _ extraction.Ingester = (*Target)(nil)
|
||||||
|
|
||||||
// RunScraper implements Target.
|
// RunScraper implements Target.
|
||||||
func (t *target) RunScraper(sampleAppender storage.SampleAppender) {
|
func (t *Target) RunScraper(sampleAppender storage.SampleAppender) {
|
||||||
defer close(t.scraperStopped)
|
defer close(t.scraperStopped)
|
||||||
|
|
||||||
t.RLock()
|
t.RLock()
|
||||||
|
@ -316,7 +285,7 @@ func (t *target) RunScraper(sampleAppender storage.SampleAppender) {
|
||||||
|
|
||||||
intervalStr := lastScrapeInterval.String()
|
intervalStr := lastScrapeInterval.String()
|
||||||
|
|
||||||
t.Lock()
|
t.RLock()
|
||||||
// On changed scrape interval the new interval becomes effective
|
// On changed scrape interval the new interval becomes effective
|
||||||
// after the next scrape.
|
// after the next scrape.
|
||||||
if lastScrapeInterval != t.scrapeInterval {
|
if lastScrapeInterval != t.scrapeInterval {
|
||||||
|
@ -324,7 +293,7 @@ func (t *target) RunScraper(sampleAppender storage.SampleAppender) {
|
||||||
ticker = time.NewTicker(t.scrapeInterval)
|
ticker = time.NewTicker(t.scrapeInterval)
|
||||||
lastScrapeInterval = t.scrapeInterval
|
lastScrapeInterval = t.scrapeInterval
|
||||||
}
|
}
|
||||||
t.Unlock()
|
t.RUnlock()
|
||||||
|
|
||||||
targetIntervalLength.WithLabelValues(intervalStr).Observe(
|
targetIntervalLength.WithLabelValues(intervalStr).Observe(
|
||||||
float64(took) / float64(time.Second), // Sub-second precision.
|
float64(took) / float64(time.Second), // Sub-second precision.
|
||||||
|
@ -336,7 +305,7 @@ func (t *target) RunScraper(sampleAppender storage.SampleAppender) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// StopScraper implements Target.
|
// StopScraper implements Target.
|
||||||
func (t *target) StopScraper() {
|
func (t *Target) StopScraper() {
|
||||||
glog.V(1).Infof("Stopping scraper for target %v...", t)
|
glog.V(1).Infof("Stopping scraper for target %v...", t)
|
||||||
|
|
||||||
close(t.scraperStopping)
|
close(t.scraperStopping)
|
||||||
|
@ -347,18 +316,16 @@ func (t *target) StopScraper() {
|
||||||
|
|
||||||
const acceptHeader = `application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=delimited;q=0.7,text/plain;version=0.0.4;q=0.3,application/json;schema="prometheus/telemetry";version=0.0.2;q=0.2,*/*;q=0.1`
|
const acceptHeader = `application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=delimited;q=0.7,text/plain;version=0.0.4;q=0.3,application/json;schema="prometheus/telemetry";version=0.0.2;q=0.2,*/*;q=0.1`
|
||||||
|
|
||||||
func (t *target) scrape(sampleAppender storage.SampleAppender) (err error) {
|
func (t *Target) scrape(sampleAppender storage.SampleAppender) (err error) {
|
||||||
t.RLock()
|
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
baseLabels := t.BaseLabels()
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
t.RUnlock()
|
|
||||||
|
|
||||||
t.status.setLastError(err)
|
t.status.setLastError(err)
|
||||||
t.recordScrapeHealth(sampleAppender, clientmodel.TimestampFromTime(start), time.Since(start))
|
recordScrapeHealth(sampleAppender, clientmodel.TimestampFromTime(start), baseLabels, t.status.Health(), time.Since(start))
|
||||||
}()
|
}()
|
||||||
|
|
||||||
req, err := http.NewRequest("GET", t.url.String(), nil)
|
req, err := http.NewRequest("GET", t.URL(), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@ -390,7 +357,7 @@ func (t *target) scrape(sampleAppender storage.SampleAppender) (err error) {
|
||||||
|
|
||||||
for samples := range t.ingestedSamples {
|
for samples := range t.ingestedSamples {
|
||||||
for _, s := range samples {
|
for _, s := range samples {
|
||||||
s.Metric.MergeFromLabelSet(t.baseLabels, clientmodel.ExporterLabelPrefix)
|
s.Metric.MergeFromLabelSet(baseLabels, clientmodel.ExporterLabelPrefix)
|
||||||
sampleAppender.Append(s)
|
sampleAppender.Append(s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -398,19 +365,19 @@ func (t *target) scrape(sampleAppender storage.SampleAppender) (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// URL implements Target.
|
// URL implements Target.
|
||||||
func (t *target) URL() string {
|
func (t *Target) URL() string {
|
||||||
t.RLock()
|
t.RLock()
|
||||||
defer t.RUnlock()
|
defer t.RUnlock()
|
||||||
return t.url.String()
|
return t.url.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
// InstanceIdentifier implements Target.
|
// InstanceIdentifier returns the identifier for the target.
|
||||||
func (t *target) InstanceIdentifier() string {
|
func (t *Target) InstanceIdentifier() string {
|
||||||
return t.url.Host
|
return t.url.Host
|
||||||
}
|
}
|
||||||
|
|
||||||
// fullLabels implements Target.
|
// fullLabels returns the base labels plus internal labels defining the target.
|
||||||
func (t *target) fullLabels() clientmodel.LabelSet {
|
func (t *Target) fullLabels() clientmodel.LabelSet {
|
||||||
t.RLock()
|
t.RLock()
|
||||||
defer t.RUnlock()
|
defer t.RUnlock()
|
||||||
lset := make(clientmodel.LabelSet, len(t.baseLabels)+2)
|
lset := make(clientmodel.LabelSet, len(t.baseLabels)+2)
|
||||||
|
@ -422,8 +389,8 @@ func (t *target) fullLabels() clientmodel.LabelSet {
|
||||||
return lset
|
return lset
|
||||||
}
|
}
|
||||||
|
|
||||||
// BaseLabels implements Target.
|
// BaseLabels returns a copy of the target's base labels.
|
||||||
func (t *target) BaseLabels() clientmodel.LabelSet {
|
func (t *Target) BaseLabels() clientmodel.LabelSet {
|
||||||
t.RLock()
|
t.RLock()
|
||||||
defer t.RUnlock()
|
defer t.RUnlock()
|
||||||
lset := make(clientmodel.LabelSet, len(t.baseLabels))
|
lset := make(clientmodel.LabelSet, len(t.baseLabels))
|
||||||
|
@ -433,22 +400,26 @@ func (t *target) BaseLabels() clientmodel.LabelSet {
|
||||||
return lset
|
return lset
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *target) recordScrapeHealth(sampleAppender storage.SampleAppender, timestamp clientmodel.Timestamp, scrapeDuration time.Duration) {
|
func recordScrapeHealth(
|
||||||
t.RLock()
|
sampleAppender storage.SampleAppender,
|
||||||
healthMetric := make(clientmodel.Metric, len(t.baseLabels)+1)
|
timestamp clientmodel.Timestamp,
|
||||||
durationMetric := make(clientmodel.Metric, len(t.baseLabels)+1)
|
baseLabels clientmodel.LabelSet,
|
||||||
|
health TargetHealth,
|
||||||
|
scrapeDuration time.Duration,
|
||||||
|
) {
|
||||||
|
healthMetric := make(clientmodel.Metric, len(baseLabels)+1)
|
||||||
|
durationMetric := make(clientmodel.Metric, len(baseLabels)+1)
|
||||||
|
|
||||||
healthMetric[clientmodel.MetricNameLabel] = clientmodel.LabelValue(scrapeHealthMetricName)
|
healthMetric[clientmodel.MetricNameLabel] = clientmodel.LabelValue(scrapeHealthMetricName)
|
||||||
durationMetric[clientmodel.MetricNameLabel] = clientmodel.LabelValue(scrapeDurationMetricName)
|
durationMetric[clientmodel.MetricNameLabel] = clientmodel.LabelValue(scrapeDurationMetricName)
|
||||||
|
|
||||||
for label, value := range t.baseLabels {
|
for label, value := range baseLabels {
|
||||||
healthMetric[label] = value
|
healthMetric[label] = value
|
||||||
durationMetric[label] = value
|
durationMetric[label] = value
|
||||||
}
|
}
|
||||||
t.RUnlock()
|
|
||||||
|
|
||||||
healthValue := clientmodel.SampleValue(0)
|
healthValue := clientmodel.SampleValue(0)
|
||||||
if t.status.State() == Healthy {
|
if health == HealthGood {
|
||||||
healthValue = clientmodel.SampleValue(1)
|
healthValue = clientmodel.SampleValue(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,10 +29,6 @@ import (
|
||||||
"github.com/prometheus/prometheus/utility"
|
"github.com/prometheus/prometheus/utility"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestTargetInterface(t *testing.T) {
|
|
||||||
var _ Target = &target{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBaseLabels(t *testing.T) {
|
func TestBaseLabels(t *testing.T) {
|
||||||
target := newTestTarget("example.com:80", 0, clientmodel.LabelSet{"job": "some_job", "foo": "bar"})
|
target := newTestTarget("example.com:80", 0, clientmodel.LabelSet{"job": "some_job", "foo": "bar"})
|
||||||
want := clientmodel.LabelSet{
|
want := clientmodel.LabelSet{
|
||||||
|
@ -50,8 +46,8 @@ func TestTargetScrapeUpdatesState(t *testing.T) {
|
||||||
testTarget := newTestTarget("bad schema", 0, nil)
|
testTarget := newTestTarget("bad schema", 0, nil)
|
||||||
|
|
||||||
testTarget.scrape(nopAppender{})
|
testTarget.scrape(nopAppender{})
|
||||||
if testTarget.status.State() != Unhealthy {
|
if testTarget.status.Health() != HealthBad {
|
||||||
t.Errorf("Expected target state %v, actual: %v", Unhealthy, testTarget.status.State())
|
t.Errorf("Expected target state %v, actual: %v", HealthBad, testTarget.status.Health())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,8 +69,8 @@ func TestTargetScrapeWithFullChannel(t *testing.T) {
|
||||||
testTarget := newTestTarget(server.URL, 10*time.Millisecond, clientmodel.LabelSet{"dings": "bums"})
|
testTarget := newTestTarget(server.URL, 10*time.Millisecond, clientmodel.LabelSet{"dings": "bums"})
|
||||||
|
|
||||||
testTarget.scrape(slowAppender{})
|
testTarget.scrape(slowAppender{})
|
||||||
if testTarget.status.State() != Unhealthy {
|
if testTarget.status.Health() != HealthBad {
|
||||||
t.Errorf("Expected target state %v, actual: %v", Unhealthy, testTarget.status.State())
|
t.Errorf("Expected target state %v, actual: %v", HealthBad, testTarget.status.Health())
|
||||||
}
|
}
|
||||||
if testTarget.status.LastError() != errIngestChannelFull {
|
if testTarget.status.LastError() != errIngestChannelFull {
|
||||||
t.Errorf("Expected target error %q, actual: %q", errIngestChannelFull, testTarget.status.LastError())
|
t.Errorf("Expected target error %q, actual: %q", errIngestChannelFull, testTarget.status.LastError())
|
||||||
|
@ -86,7 +82,8 @@ func TestTargetRecordScrapeHealth(t *testing.T) {
|
||||||
|
|
||||||
now := clientmodel.Now()
|
now := clientmodel.Now()
|
||||||
appender := &collectResultAppender{}
|
appender := &collectResultAppender{}
|
||||||
testTarget.recordScrapeHealth(appender, now, 2*time.Second)
|
testTarget.status.setLastError(nil)
|
||||||
|
recordScrapeHealth(appender, now, testTarget.BaseLabels(), testTarget.status.Health(), 2*time.Second)
|
||||||
|
|
||||||
result := appender.result
|
result := appender.result
|
||||||
|
|
||||||
|
@ -138,13 +135,13 @@ func TestTargetScrapeTimeout(t *testing.T) {
|
||||||
)
|
)
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
|
|
||||||
var testTarget Target = newTestTarget(server.URL, 10*time.Millisecond, clientmodel.LabelSet{})
|
testTarget := newTestTarget(server.URL, 10*time.Millisecond, clientmodel.LabelSet{})
|
||||||
|
|
||||||
appender := nopAppender{}
|
appender := nopAppender{}
|
||||||
|
|
||||||
// scrape once without timeout
|
// scrape once without timeout
|
||||||
signal <- true
|
signal <- true
|
||||||
if err := testTarget.(*target).scrape(appender); err != nil {
|
if err := testTarget.scrape(appender); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,12 +150,12 @@ func TestTargetScrapeTimeout(t *testing.T) {
|
||||||
|
|
||||||
// now scrape again
|
// now scrape again
|
||||||
signal <- true
|
signal <- true
|
||||||
if err := testTarget.(*target).scrape(appender); err != nil {
|
if err := testTarget.scrape(appender); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// now timeout
|
// now timeout
|
||||||
if err := testTarget.(*target).scrape(appender); err == nil {
|
if err := testTarget.scrape(appender); err == nil {
|
||||||
t.Fatal("expected scrape to timeout")
|
t.Fatal("expected scrape to timeout")
|
||||||
} else {
|
} else {
|
||||||
signal <- true // let handler continue
|
signal <- true // let handler continue
|
||||||
|
@ -166,7 +163,7 @@ func TestTargetScrapeTimeout(t *testing.T) {
|
||||||
|
|
||||||
// now scrape again without timeout
|
// now scrape again without timeout
|
||||||
signal <- true
|
signal <- true
|
||||||
if err := testTarget.(*target).scrape(appender); err != nil {
|
if err := testTarget.scrape(appender); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -224,28 +221,26 @@ func BenchmarkScrape(b *testing.B) {
|
||||||
)
|
)
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
|
|
||||||
var testTarget Target = newTestTarget(server.URL, 100*time.Millisecond, clientmodel.LabelSet{"dings": "bums"})
|
testTarget := newTestTarget(server.URL, 100*time.Millisecond, clientmodel.LabelSet{"dings": "bums"})
|
||||||
appender := nopAppender{}
|
appender := nopAppender{}
|
||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
if err := testTarget.(*target).scrape(appender); err != nil {
|
if err := testTarget.scrape(appender); err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTestTarget(targetURL string, deadline time.Duration, baseLabels clientmodel.LabelSet) *target {
|
func newTestTarget(targetURL string, deadline time.Duration, baseLabels clientmodel.LabelSet) *Target {
|
||||||
t := &target{
|
t := &Target{
|
||||||
url: &url.URL{
|
url: &url.URL{
|
||||||
Scheme: "http",
|
Scheme: "http",
|
||||||
Host: strings.TrimLeft(targetURL, "http://"),
|
Host: strings.TrimLeft(targetURL, "http://"),
|
||||||
Path: "/metrics",
|
Path: "/metrics",
|
||||||
},
|
},
|
||||||
deadline: deadline,
|
deadline: deadline,
|
||||||
status: &TargetStatus{
|
status: &TargetStatus{},
|
||||||
state: Healthy,
|
|
||||||
},
|
|
||||||
scrapeInterval: 1 * time.Millisecond,
|
scrapeInterval: 1 * time.Millisecond,
|
||||||
httpClient: utility.NewDeadlineClient(deadline),
|
httpClient: utility.NewDeadlineClient(deadline),
|
||||||
scraperStopping: make(chan struct{}),
|
scraperStopping: make(chan struct{}),
|
||||||
|
|
|
@ -56,7 +56,7 @@ type TargetManager struct {
|
||||||
running bool
|
running bool
|
||||||
|
|
||||||
// Targets by their source ID.
|
// Targets by their source ID.
|
||||||
targets map[string][]Target
|
targets map[string][]*Target
|
||||||
// Providers by the scrape configs they are derived from.
|
// Providers by the scrape configs they are derived from.
|
||||||
providers map[*config.ScrapeConfig][]TargetProvider
|
providers map[*config.ScrapeConfig][]TargetProvider
|
||||||
}
|
}
|
||||||
|
@ -65,7 +65,7 @@ type TargetManager struct {
|
||||||
func NewTargetManager(sampleAppender storage.SampleAppender) *TargetManager {
|
func NewTargetManager(sampleAppender storage.SampleAppender) *TargetManager {
|
||||||
tm := &TargetManager{
|
tm := &TargetManager{
|
||||||
sampleAppender: sampleAppender,
|
sampleAppender: sampleAppender,
|
||||||
targets: make(map[string][]Target),
|
targets: make(map[string][]*Target),
|
||||||
}
|
}
|
||||||
return tm
|
return tm
|
||||||
}
|
}
|
||||||
|
@ -165,7 +165,7 @@ func (tm *TargetManager) removeTargets(f func(string) bool) {
|
||||||
}
|
}
|
||||||
wg.Add(len(targets))
|
wg.Add(len(targets))
|
||||||
for _, target := range targets {
|
for _, target := range targets {
|
||||||
go func(t Target) {
|
go func(t *Target) {
|
||||||
t.StopScraper()
|
t.StopScraper()
|
||||||
wg.Done()
|
wg.Done()
|
||||||
}(target)
|
}(target)
|
||||||
|
@ -197,7 +197,7 @@ func (tm *TargetManager) updateTargetGroup(tgroup *config.TargetGroup, cfg *conf
|
||||||
// Replace the old targets with the new ones while keeping the state
|
// Replace the old targets with the new ones while keeping the state
|
||||||
// of intersecting targets.
|
// of intersecting targets.
|
||||||
for i, tnew := range newTargets {
|
for i, tnew := range newTargets {
|
||||||
var match Target
|
var match *Target
|
||||||
for j, told := range oldTargets {
|
for j, told := range oldTargets {
|
||||||
if told == nil {
|
if told == nil {
|
||||||
continue
|
continue
|
||||||
|
@ -214,7 +214,7 @@ func (tm *TargetManager) updateTargetGroup(tgroup *config.TargetGroup, cfg *conf
|
||||||
// Updating is blocked during a scrape. We don't want those wait times
|
// Updating is blocked during a scrape. We don't want those wait times
|
||||||
// to build up.
|
// to build up.
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func(t Target) {
|
go func(t *Target) {
|
||||||
match.Update(cfg, t.fullLabels())
|
match.Update(cfg, t.fullLabels())
|
||||||
wg.Done()
|
wg.Done()
|
||||||
}(tnew)
|
}(tnew)
|
||||||
|
@ -227,7 +227,7 @@ func (tm *TargetManager) updateTargetGroup(tgroup *config.TargetGroup, cfg *conf
|
||||||
for _, told := range oldTargets {
|
for _, told := range oldTargets {
|
||||||
if told != nil {
|
if told != nil {
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func(t Target) {
|
go func(t *Target) {
|
||||||
t.StopScraper()
|
t.StopScraper()
|
||||||
wg.Done()
|
wg.Done()
|
||||||
}(told)
|
}(told)
|
||||||
|
@ -250,11 +250,11 @@ func (tm *TargetManager) updateTargetGroup(tgroup *config.TargetGroup, cfg *conf
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pools returns the targets currently being scraped bucketed by their job name.
|
// Pools returns the targets currently being scraped bucketed by their job name.
|
||||||
func (tm *TargetManager) Pools() map[string][]Target {
|
func (tm *TargetManager) Pools() map[string][]*Target {
|
||||||
tm.m.RLock()
|
tm.m.RLock()
|
||||||
defer tm.m.RUnlock()
|
defer tm.m.RUnlock()
|
||||||
|
|
||||||
pools := map[string][]Target{}
|
pools := map[string][]*Target{}
|
||||||
|
|
||||||
for _, ts := range tm.targets {
|
for _, ts := range tm.targets {
|
||||||
for _, t := range ts {
|
for _, t := range ts {
|
||||||
|
@ -287,11 +287,11 @@ func (tm *TargetManager) ApplyConfig(cfg *config.Config) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// targetsFromGroup builds targets based on the given TargetGroup and config.
|
// targetsFromGroup builds targets based on the given TargetGroup and config.
|
||||||
func (tm *TargetManager) targetsFromGroup(tg *config.TargetGroup, cfg *config.ScrapeConfig) ([]Target, error) {
|
func (tm *TargetManager) targetsFromGroup(tg *config.TargetGroup, cfg *config.ScrapeConfig) ([]*Target, error) {
|
||||||
tm.m.RLock()
|
tm.m.RLock()
|
||||||
defer tm.m.RUnlock()
|
defer tm.m.RUnlock()
|
||||||
|
|
||||||
targets := make([]Target, 0, len(tg.Targets))
|
targets := make([]*Target, 0, len(tg.Targets))
|
||||||
for i, labels := range tg.Targets {
|
for i, labels := range tg.Targets {
|
||||||
addr := string(labels[clientmodel.AddressLabel])
|
addr := string(labels[clientmodel.AddressLabel])
|
||||||
// If no port was provided, infer it based on the used scheme.
|
// If no port was provided, infer it based on the used scheme.
|
||||||
|
|
|
@ -45,7 +45,7 @@ func TestTargetManagerChan(t *testing.T) {
|
||||||
providers: map[*config.ScrapeConfig][]TargetProvider{
|
providers: map[*config.ScrapeConfig][]TargetProvider{
|
||||||
testJob1: []TargetProvider{prov1},
|
testJob1: []TargetProvider{prov1},
|
||||||
},
|
},
|
||||||
targets: make(map[string][]Target),
|
targets: make(map[string][]*Target),
|
||||||
}
|
}
|
||||||
go targetManager.Run()
|
go targetManager.Run()
|
||||||
defer targetManager.Stop()
|
defer targetManager.Stop()
|
||||||
|
|
|
@ -32,18 +32,18 @@ type PrometheusStatusHandler struct {
|
||||||
Flags map[string]string
|
Flags map[string]string
|
||||||
|
|
||||||
RuleManager *rules.Manager
|
RuleManager *rules.Manager
|
||||||
TargetPools func() map[string][]retrieval.Target
|
TargetPools func() map[string][]*retrieval.Target
|
||||||
|
|
||||||
Birth time.Time
|
Birth time.Time
|
||||||
PathPrefix string
|
PathPrefix string
|
||||||
}
|
}
|
||||||
|
|
||||||
// TargetStateToClass returns a map of TargetState to the name of a Bootstrap CSS class.
|
// TargetHealthToClass returns a map of TargetHealth to the name of a Bootstrap CSS class.
|
||||||
func (h *PrometheusStatusHandler) TargetStateToClass() map[retrieval.TargetState]string {
|
func (h *PrometheusStatusHandler) TargetHealthToClass() map[retrieval.TargetHealth]string {
|
||||||
return map[retrieval.TargetState]string{
|
return map[retrieval.TargetHealth]string{
|
||||||
retrieval.Unknown: "warning",
|
retrieval.HealthUnknown: "warning",
|
||||||
retrieval.Healthy: "success",
|
retrieval.HealthBad: "success",
|
||||||
retrieval.Unhealthy: "danger",
|
retrieval.HealthGood: "danger",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,6 @@
|
||||||
|
|
||||||
<h2>Targets</h2>
|
<h2>Targets</h2>
|
||||||
<table class="table table-condensed table-bordered table-striped table-hover">
|
<table class="table table-condensed table-bordered table-striped table-hover">
|
||||||
{{$stateToClass := .TargetStateToClass}}
|
|
||||||
{{range $job, $pool := call .TargetPools}}
|
{{range $job, $pool := call .TargetPools}}
|
||||||
<thead>
|
<thead>
|
||||||
<tr><th colspan="5" class="job_header">{{$job}}</th></tr>
|
<tr><th colspan="5" class="job_header">{{$job}}</th></tr>
|
||||||
|
@ -51,7 +50,7 @@
|
||||||
<a href="{{.URL | globalURL}}">{{.URL}}</a>
|
<a href="{{.URL | globalURL}}">{{.URL}}</a>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<span class="alert alert-{{index $stateToClass .Status.State}} target_status_alert">
|
<span class="alert alert-{{index .TargetHealthToClass .Status.State}} target_status_alert">
|
||||||
{{.Status.State}}
|
{{.Status.State}}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
|
|
|
@ -193,7 +193,7 @@ func getTemplate(name string, pathPrefix string) (*template.Template, error) {
|
||||||
|
|
||||||
file, err = getTemplateFile(name)
|
file, err = getTemplateFile(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Error("Could not read template %d: ", name, err)
|
glog.Error("Could not read template %s: %s", name, err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
t, err = t.Parse(file)
|
t, err = t.Parse(file)
|
||||||
|
|
Loading…
Reference in a new issue