mirror of
https://github.com/prometheus/prometheus.git
synced 2024-12-26 14:09:41 -08:00
Merge pull request #421 from prometheus/u-c-l/code-cleanup
Apply a giant code cleanup.
This commit is contained in:
commit
cf781eff37
|
@ -14,16 +14,17 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"code.google.com/p/goprotobuf/proto"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"time"
|
||||
|
||||
"code.google.com/p/goprotobuf/proto"
|
||||
|
||||
clientmodel "github.com/prometheus/client_golang/model"
|
||||
|
||||
pb "github.com/prometheus/prometheus/config/generated"
|
||||
|
||||
"github.com/prometheus/prometheus/utility"
|
||||
|
||||
pb "github.com/prometheus/prometheus/config/generated"
|
||||
)
|
||||
|
||||
var jobNameRE = regexp.MustCompile("^[a-zA-Z_][a-zA-Z0-9_-]*$")
|
||||
|
@ -48,7 +49,7 @@ func (c Config) validateLabels(labels *pb.LabelPairs) error {
|
|||
}
|
||||
for _, label := range labels.Label {
|
||||
if !labelNameRE.MatchString(label.GetName()) {
|
||||
return fmt.Errorf("Invalid label name '%s'", label.GetName())
|
||||
return fmt.Errorf("invalid label name '%s'", label.GetName())
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
@ -59,42 +60,42 @@ func (c Config) Validate() error {
|
|||
// Check the global configuration section for validity.
|
||||
global := c.Global
|
||||
if _, err := utility.StringToDuration(global.GetScrapeInterval()); err != nil {
|
||||
return fmt.Errorf("Invalid global scrape interval: %s", err)
|
||||
return fmt.Errorf("invalid global scrape interval: %s", err)
|
||||
}
|
||||
if _, err := utility.StringToDuration(global.GetEvaluationInterval()); err != nil {
|
||||
return fmt.Errorf("Invalid rule evaluation interval: %s", err)
|
||||
return fmt.Errorf("invalid rule evaluation interval: %s", err)
|
||||
}
|
||||
if err := c.validateLabels(global.Labels); err != nil {
|
||||
return fmt.Errorf("Invalid global labels: %s", err)
|
||||
return fmt.Errorf("invalid global labels: %s", err)
|
||||
}
|
||||
|
||||
// Check each job configuration for validity.
|
||||
jobNames := map[string]bool{}
|
||||
for _, job := range c.Job {
|
||||
if jobNames[job.GetName()] {
|
||||
return fmt.Errorf("Found multiple jobs configured with the same name: '%s'", job.GetName())
|
||||
return fmt.Errorf("found multiple jobs configured with the same name: '%s'", job.GetName())
|
||||
}
|
||||
jobNames[job.GetName()] = true
|
||||
|
||||
if !jobNameRE.MatchString(job.GetName()) {
|
||||
return fmt.Errorf("Invalid job name '%s'", job.GetName())
|
||||
return fmt.Errorf("invalid job name '%s'", job.GetName())
|
||||
}
|
||||
if _, err := utility.StringToDuration(job.GetScrapeInterval()); err != nil {
|
||||
return fmt.Errorf("Invalid scrape interval for job '%s': %s", job.GetName(), err)
|
||||
return fmt.Errorf("invalid scrape interval for job '%s': %s", job.GetName(), err)
|
||||
}
|
||||
if _, err := utility.StringToDuration(job.GetSdRefreshInterval()); err != nil {
|
||||
return fmt.Errorf("Invalid SD refresh interval for job '%s': %s", job.GetName(), err)
|
||||
return fmt.Errorf("invalid SD refresh interval for job '%s': %s", job.GetName(), err)
|
||||
}
|
||||
if _, err := utility.StringToDuration(job.GetScrapeTimeout()); err != nil {
|
||||
return fmt.Errorf("Invalid scrape timeout for job '%s': %s", job.GetName(), err)
|
||||
return fmt.Errorf("invalid scrape timeout for job '%s': %s", job.GetName(), err)
|
||||
}
|
||||
for _, targetGroup := range job.TargetGroup {
|
||||
if err := c.validateLabels(targetGroup.Labels); err != nil {
|
||||
return fmt.Errorf("Invalid labels for job '%s': %s", job.GetName(), err)
|
||||
return fmt.Errorf("invalid labels for job '%s': %s", job.GetName(), err)
|
||||
}
|
||||
}
|
||||
if job.SdName != nil && len(job.TargetGroup) > 0 {
|
||||
return fmt.Errorf("Specified both DNS-SD name and target group for job: %s", job.GetName())
|
||||
return fmt.Errorf("specified both DNS-SD name and target group for job: %s", job.GetName())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -111,7 +112,7 @@ func (c Config) GetJobByName(name string) *JobConfig {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Return the global labels as a LabelSet.
|
||||
// GlobalLabels returns the global labels as a LabelSet.
|
||||
func (c Config) GlobalLabels() clientmodel.LabelSet {
|
||||
labels := clientmodel.LabelSet{}
|
||||
if c.Global.Labels != nil {
|
||||
|
@ -155,12 +156,12 @@ type JobConfig struct {
|
|||
pb.JobConfig
|
||||
}
|
||||
|
||||
// EvaluationInterval gets the scrape interval for a job.
|
||||
// ScrapeInterval gets the scrape interval for a job.
|
||||
func (c JobConfig) ScrapeInterval() time.Duration {
|
||||
return stringToDuration(c.GetScrapeInterval())
|
||||
}
|
||||
|
||||
// EvaluationInterval gets the scrape interval for a job.
|
||||
// ScrapeTimeout gets the scrape timeout for a job.
|
||||
func (c JobConfig) ScrapeTimeout() time.Duration {
|
||||
return stringToDuration(c.GetScrapeInterval())
|
||||
}
|
||||
|
|
|
@ -43,27 +43,27 @@ var configTests = []struct {
|
|||
{
|
||||
inputFile: "invalid_scrape_interval.conf.input",
|
||||
shouldFail: true,
|
||||
errContains: "Invalid global scrape interval",
|
||||
errContains: "invalid global scrape interval",
|
||||
},
|
||||
{
|
||||
inputFile: "invalid_job_name.conf.input",
|
||||
shouldFail: true,
|
||||
errContains: "Invalid job name",
|
||||
errContains: "invalid job name",
|
||||
},
|
||||
{
|
||||
inputFile: "invalid_label_name.conf.input",
|
||||
shouldFail: true,
|
||||
errContains: "Invalid label name",
|
||||
errContains: "invalid label name",
|
||||
},
|
||||
{
|
||||
inputFile: "mixing_sd_and_manual_targets.conf.input",
|
||||
shouldFail: true,
|
||||
errContains: "Specified both DNS-SD name and target group",
|
||||
errContains: "specified both DNS-SD name and target group",
|
||||
},
|
||||
{
|
||||
inputFile: "repeated_job_name.conf.input",
|
||||
shouldFail: true,
|
||||
errContains: "Found multiple jobs configured with the same name: 'testjob1'",
|
||||
errContains: "found multiple jobs configured with the same name: 'testjob1'",
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -14,11 +14,14 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"code.google.com/p/goprotobuf/proto"
|
||||
pb "github.com/prometheus/prometheus/config/generated"
|
||||
"io/ioutil"
|
||||
|
||||
"code.google.com/p/goprotobuf/proto"
|
||||
|
||||
pb "github.com/prometheus/prometheus/config/generated"
|
||||
)
|
||||
|
||||
// LoadFromString returns a config parsed from the provided string.
|
||||
func LoadFromString(configStr string) (Config, error) {
|
||||
configProto := pb.PrometheusConfig{}
|
||||
if err := proto.UnmarshalText(configStr, &configProto); err != nil {
|
||||
|
@ -39,6 +42,7 @@ func LoadFromString(configStr string) (Config, error) {
|
|||
return config, err
|
||||
}
|
||||
|
||||
// LoadFromFile returns a config parsed from the file of the provided name.
|
||||
func LoadFromFile(fileName string) (Config, error) {
|
||||
configStr, err := ioutil.ReadFile(fileName)
|
||||
if err != nil {
|
||||
|
|
3
main.go
3
main.go
|
@ -15,6 +15,7 @@ package main
|
|||
|
||||
import (
|
||||
"flag"
|
||||
_ "net/http/pprof" // Comment this line to disable pprof endpoint.
|
||||
"os"
|
||||
"os/signal"
|
||||
"sync"
|
||||
|
@ -130,7 +131,7 @@ func NewPrometheus() *prometheus {
|
|||
NotificationHandler: notificationHandler,
|
||||
EvaluationInterval: conf.EvaluationInterval(),
|
||||
Storage: memStorage,
|
||||
PrometheusUrl: web.MustBuildServerUrl(),
|
||||
PrometheusURL: web.MustBuildServerURL(),
|
||||
})
|
||||
if err := ruleManager.AddRulesFromConfig(conf); err != nil {
|
||||
glog.Fatal("Error loading rule files: ", err)
|
||||
|
|
|
@ -31,8 +31,8 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
alertmanagerApiEventsPath = "/api/alerts"
|
||||
contentTypeJson = "application/json"
|
||||
alertmanagerAPIEventsPath = "/api/alerts"
|
||||
contentTypeJSON = "application/json"
|
||||
)
|
||||
|
||||
// String constants for instrumentation.
|
||||
|
@ -50,8 +50,8 @@ var (
|
|||
deadline = flag.Duration("alertmanager.http-deadline", 10*time.Second, "Alert manager HTTP API timeout.")
|
||||
)
|
||||
|
||||
// A request for sending a notification to the alert manager for a single alert
|
||||
// vector element.
|
||||
// NotificationReq is a request for sending a notification to the alert manager
|
||||
// for a single alert vector element.
|
||||
type NotificationReq struct {
|
||||
// Short-form alert summary. May contain text/template-style interpolations.
|
||||
Summary string
|
||||
|
@ -69,6 +69,9 @@ type NotificationReq struct {
|
|||
GeneratorURL string
|
||||
}
|
||||
|
||||
// NotificationReqs is just a short-hand for []*NotificationReq. No methods
|
||||
// attached. Arguably, it's more confusing than helpful. Perhaps we should
|
||||
// remove it...
|
||||
type NotificationReqs []*NotificationReq
|
||||
|
||||
type httpPoster interface {
|
||||
|
@ -79,7 +82,7 @@ type httpPoster interface {
|
|||
// alert manager service.
|
||||
type NotificationHandler struct {
|
||||
// The URL of the alert manager to send notifications to.
|
||||
alertmanagerUrl string
|
||||
alertmanagerURL string
|
||||
// Buffer of notifications that have not yet been sent.
|
||||
pendingNotifications chan NotificationReqs
|
||||
// HTTP client with custom timeout settings.
|
||||
|
@ -92,10 +95,10 @@ type NotificationHandler struct {
|
|||
stopped chan struct{}
|
||||
}
|
||||
|
||||
// Construct a new NotificationHandler.
|
||||
func NewNotificationHandler(alertmanagerUrl string, notificationQueueCapacity int) *NotificationHandler {
|
||||
// NewNotificationHandler constructs a new NotificationHandler.
|
||||
func NewNotificationHandler(alertmanagerURL string, notificationQueueCapacity int) *NotificationHandler {
|
||||
return &NotificationHandler{
|
||||
alertmanagerUrl: alertmanagerUrl,
|
||||
alertmanagerURL: alertmanagerURL,
|
||||
pendingNotifications: make(chan NotificationReqs, notificationQueueCapacity),
|
||||
|
||||
httpClient: utility.NewDeadlineClient(*deadline),
|
||||
|
@ -150,8 +153,8 @@ func (n *NotificationHandler) sendNotifications(reqs NotificationReqs) error {
|
|||
}
|
||||
glog.V(1).Infoln("Sending notifications to alertmanager:", string(buf))
|
||||
resp, err := n.httpClient.Post(
|
||||
n.alertmanagerUrl+alertmanagerApiEventsPath,
|
||||
contentTypeJson,
|
||||
n.alertmanagerURL+alertmanagerAPIEventsPath,
|
||||
contentTypeJSON,
|
||||
bytes.NewBuffer(buf),
|
||||
)
|
||||
if err != nil {
|
||||
|
@ -170,7 +173,7 @@ func (n *NotificationHandler) sendNotifications(reqs NotificationReqs) error {
|
|||
// Run dispatches notifications continuously.
|
||||
func (n *NotificationHandler) Run() {
|
||||
for reqs := range n.pendingNotifications {
|
||||
if n.alertmanagerUrl == "" {
|
||||
if n.alertmanagerURL == "" {
|
||||
glog.Warning("No alert manager configured, not dispatching notification")
|
||||
n.notificationLatency.WithLabelValues(dropped).Observe(0)
|
||||
continue
|
||||
|
|
|
@ -24,12 +24,12 @@ import (
|
|||
clientmodel "github.com/prometheus/client_golang/model"
|
||||
)
|
||||
|
||||
type testHttpPoster struct {
|
||||
type testHTTPPoster struct {
|
||||
message string
|
||||
receivedPost chan<- bool
|
||||
}
|
||||
|
||||
func (p *testHttpPoster) Post(url string, bodyType string, body io.Reader) (*http.Response, error) {
|
||||
func (p *testHTTPPoster) Post(url string, bodyType string, body io.Reader) (*http.Response, error) {
|
||||
var buf bytes.Buffer
|
||||
buf.ReadFrom(body)
|
||||
p.message = buf.String()
|
||||
|
@ -50,7 +50,7 @@ func (s *testNotificationScenario) test(i int, t *testing.T) {
|
|||
defer h.Stop()
|
||||
|
||||
receivedPost := make(chan bool, 1)
|
||||
poster := testHttpPoster{receivedPost: receivedPost}
|
||||
poster := testHTTPPoster{receivedPost: receivedPost}
|
||||
h.httpClient = &poster
|
||||
|
||||
go h.Run()
|
||||
|
|
|
@ -29,6 +29,8 @@ type MergeLabelsIngester struct {
|
|||
Ingester extraction.Ingester
|
||||
}
|
||||
|
||||
// Ingest ingests the provided extraction result by merging in i.Labels and then
|
||||
// handing it over to i.Ingester.
|
||||
func (i *MergeLabelsIngester) Ingest(r *extraction.Result) error {
|
||||
for _, s := range r.Samples {
|
||||
s.Metric.MergeFromLabelSet(i.Labels, i.CollisionPrefix)
|
||||
|
@ -40,6 +42,7 @@ func (i *MergeLabelsIngester) Ingest(r *extraction.Result) error {
|
|||
// ChannelIngester feeds results into a channel without modifying them.
|
||||
type ChannelIngester chan<- *extraction.Result
|
||||
|
||||
// Ingest ingests the provided extraction result by sending it to i.
|
||||
func (i ChannelIngester) Ingest(r *extraction.Result) error {
|
||||
i <- r
|
||||
return nil
|
||||
|
|
|
@ -32,8 +32,10 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
// InstanceLabel is the label value used for the instance label.
|
||||
InstanceLabel clientmodel.LabelName = "instance"
|
||||
// The metric name for the synthetic health variable.
|
||||
// ScrapeHealthMetricName is the metric name for the synthetic health
|
||||
// variable.
|
||||
ScrapeHealthMetricName clientmodel.LabelValue = "up"
|
||||
|
||||
// Constants for instrumentation.
|
||||
|
@ -74,16 +76,16 @@ func init() {
|
|||
prometheus.MustRegister(targetIntervalLength)
|
||||
}
|
||||
|
||||
// The state of the given Target.
|
||||
// TargetState describes the state of a Target.
|
||||
type TargetState int
|
||||
|
||||
func (t TargetState) String() string {
|
||||
switch t {
|
||||
case UNKNOWN:
|
||||
case Unknown:
|
||||
return "UNKNOWN"
|
||||
case ALIVE:
|
||||
case Alive:
|
||||
return "ALIVE"
|
||||
case UNREACHABLE:
|
||||
case Unreachable:
|
||||
return "UNREACHABLE"
|
||||
}
|
||||
|
||||
|
@ -91,14 +93,16 @@ func (t TargetState) String() string {
|
|||
}
|
||||
|
||||
const (
|
||||
// The Target has not been seen; we know nothing about it, except that it is
|
||||
// on our docket for examination.
|
||||
UNKNOWN TargetState = iota
|
||||
// The Target has been found and successfully queried.
|
||||
ALIVE
|
||||
// The Target was either historically found or not found and then determined
|
||||
// to be unhealthy by either not responding or disappearing.
|
||||
UNREACHABLE
|
||||
// Unknown is the state of a Target that has not been seen; we know
|
||||
// nothing about it, except that it is on our docket for examination.
|
||||
Unknown TargetState = iota
|
||||
// Alive is the state of a Target that has been found and successfully
|
||||
// queried.
|
||||
Alive
|
||||
// Unreachable is the state of a Target that was either historically
|
||||
// found or not found and then determined to be unhealthy by either not
|
||||
// responding or disappearing.
|
||||
Unreachable
|
||||
)
|
||||
|
||||
// A Target represents an endpoint that should be interrogated for metrics.
|
||||
|
@ -170,7 +174,7 @@ type target struct {
|
|||
sync.Mutex
|
||||
}
|
||||
|
||||
// Furnish a reasonably configured target for querying.
|
||||
// NewTarget creates a reasonably configured target for querying.
|
||||
func NewTarget(address string, deadline time.Duration, baseLabels clientmodel.LabelSet) Target {
|
||||
target := &target{
|
||||
address: address,
|
||||
|
@ -296,10 +300,10 @@ func (t *target) scrape(ingester extraction.Ingester) (err error) {
|
|||
}
|
||||
t.Lock() // Writing t.state and t.lastError requires the lock.
|
||||
if err == nil {
|
||||
t.state = ALIVE
|
||||
t.state = Alive
|
||||
labels[outcome] = failure
|
||||
} else {
|
||||
t.state = UNREACHABLE
|
||||
t.state = Unreachable
|
||||
}
|
||||
t.lastError = err
|
||||
t.Unlock()
|
||||
|
|
|
@ -62,7 +62,7 @@ type sdTargetProvider struct {
|
|||
refreshInterval time.Duration
|
||||
}
|
||||
|
||||
// Constructs a new sdTargetProvider for a job.
|
||||
// NewSdTargetProvider constructs a new sdTargetProvider for a job.
|
||||
func NewSdTargetProvider(job config.JobConfig) *sdTargetProvider {
|
||||
i, err := utility.StringToDuration(job.GetSdRefreshInterval())
|
||||
if err != nil {
|
||||
|
@ -125,7 +125,7 @@ func (p *sdTargetProvider) Targets() ([]Target, error) {
|
|||
func lookupSRV(name string) (*dns.Msg, error) {
|
||||
conf, err := dns.ClientConfigFromFile(resolvConf)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Couldn't load resolv.conf: %s", err)
|
||||
return nil, fmt.Errorf("couldn't load resolv.conf: %s", err)
|
||||
}
|
||||
|
||||
client := &dns.Client{}
|
||||
|
@ -140,7 +140,7 @@ func lookupSRV(name string) (*dns.Msg, error) {
|
|||
return response, nil
|
||||
}
|
||||
} else {
|
||||
glog.Warningf("Resolving %s.%s failed: %s", name, suffix, err)
|
||||
glog.Warningf("resolving %s.%s failed: %s", name, suffix, err)
|
||||
}
|
||||
}
|
||||
response, err = lookup(name, dns.TypeSRV, client, servAddr, "", false)
|
||||
|
@ -148,7 +148,7 @@ func lookupSRV(name string) (*dns.Msg, error) {
|
|||
return response, nil
|
||||
}
|
||||
}
|
||||
return response, fmt.Errorf("Couldn't resolve %s: No server responded", name)
|
||||
return response, fmt.Errorf("couldn't resolve %s: No server responded", name)
|
||||
}
|
||||
|
||||
func lookup(name string, queryType uint16, client *dns.Client, servAddr string, suffix string, edns bool) (*dns.Msg, error) {
|
||||
|
@ -178,7 +178,7 @@ func lookup(name string, queryType uint16, client *dns.Client, servAddr string,
|
|||
|
||||
if response.MsgHdr.Truncated {
|
||||
if client.Net == "tcp" {
|
||||
return nil, fmt.Errorf("Got truncated message on tcp")
|
||||
return nil, fmt.Errorf("got truncated message on tcp")
|
||||
}
|
||||
|
||||
if edns { // Truncated even though EDNS is used
|
||||
|
|
|
@ -38,13 +38,13 @@ func (i *collectResultIngester) Ingest(r *extraction.Result) error {
|
|||
|
||||
func TestTargetScrapeUpdatesState(t *testing.T) {
|
||||
testTarget := target{
|
||||
state: UNKNOWN,
|
||||
state: Unknown,
|
||||
address: "bad schema",
|
||||
httpClient: utility.NewDeadlineClient(0),
|
||||
}
|
||||
testTarget.scrape(nopIngester{})
|
||||
if testTarget.state != UNREACHABLE {
|
||||
t.Errorf("Expected target state %v, actual: %v", UNREACHABLE, testTarget.state)
|
||||
if testTarget.state != Unreachable {
|
||||
t.Errorf("Expected target state %v, actual: %v", Unreachable, testTarget.state)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -146,7 +146,7 @@ func TestTargetScrape404(t *testing.T) {
|
|||
|
||||
func TestTargetRunScraperScrapes(t *testing.T) {
|
||||
testTarget := target{
|
||||
state: UNKNOWN,
|
||||
state: Unknown,
|
||||
address: "bad schema",
|
||||
httpClient: utility.NewDeadlineClient(0),
|
||||
scraperStopping: make(chan struct{}),
|
||||
|
|
|
@ -39,6 +39,7 @@ type targetManager struct {
|
|||
ingester extraction.Ingester
|
||||
}
|
||||
|
||||
// NewTargetManager returns a newly initialized TargetManager ready to use.
|
||||
func NewTargetManager(ingester extraction.Ingester) TargetManager {
|
||||
return &targetManager{
|
||||
ingester: ingester,
|
||||
|
@ -50,7 +51,7 @@ func (m *targetManager) targetPoolForJob(job config.JobConfig) *TargetPool {
|
|||
targetPool, ok := m.poolsByJob[job.GetName()]
|
||||
|
||||
if !ok {
|
||||
var provider TargetProvider = nil
|
||||
var provider TargetProvider
|
||||
if job.SdName != nil {
|
||||
provider = NewSdTargetProvider(job)
|
||||
}
|
||||
|
|
|
@ -73,7 +73,7 @@ func (t fakeTarget) StopScraper() {
|
|||
}
|
||||
|
||||
func (t fakeTarget) State() TargetState {
|
||||
return ALIVE
|
||||
return Alive
|
||||
}
|
||||
|
||||
func (t *fakeTarget) SetBaseLabelsFrom(newTarget Target) {}
|
||||
|
|
|
@ -27,10 +27,10 @@ const (
|
|||
targetReplaceQueueSize = 1
|
||||
)
|
||||
|
||||
// TargetPool is a pool of targets for the same job.
|
||||
type TargetPool struct {
|
||||
sync.RWMutex
|
||||
|
||||
done chan chan struct{}
|
||||
manager TargetManager
|
||||
targetsByAddress map[string]Target
|
||||
interval time.Duration
|
||||
|
@ -38,8 +38,11 @@ type TargetPool struct {
|
|||
addTargetQueue chan Target
|
||||
|
||||
targetProvider TargetProvider
|
||||
|
||||
stopping, stopped chan struct{}
|
||||
}
|
||||
|
||||
// NewTargetPool creates a TargetPool, ready to be started by calling Run.
|
||||
func NewTargetPool(m TargetManager, p TargetProvider, ing extraction.Ingester, i time.Duration) *TargetPool {
|
||||
return &TargetPool{
|
||||
manager: m,
|
||||
|
@ -48,10 +51,13 @@ func NewTargetPool(m TargetManager, p TargetProvider, ing extraction.Ingester, i
|
|||
targetsByAddress: make(map[string]Target),
|
||||
addTargetQueue: make(chan Target, targetAddQueueSize),
|
||||
targetProvider: p,
|
||||
done: make(chan chan struct{}),
|
||||
stopping: make(chan struct{}),
|
||||
stopped: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
// Run starts the target pool. It returns when the target pool has stopped
|
||||
// (after calling Stop). Run is usually called as a goroutine.
|
||||
func (p *TargetPool) Run() {
|
||||
ticker := time.NewTicker(p.interval)
|
||||
defer ticker.Stop()
|
||||
|
@ -69,20 +75,21 @@ func (p *TargetPool) Run() {
|
|||
}
|
||||
case newTarget := <-p.addTargetQueue:
|
||||
p.addTarget(newTarget)
|
||||
case stopped := <-p.done:
|
||||
case <-p.stopping:
|
||||
p.ReplaceTargets([]Target{})
|
||||
close(stopped)
|
||||
close(p.stopped)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Stop stops the target pool and returns once the shutdown is complete.
|
||||
func (p *TargetPool) Stop() {
|
||||
stopped := make(chan struct{})
|
||||
p.done <- stopped
|
||||
<-stopped
|
||||
close(p.stopping)
|
||||
<-p.stopped
|
||||
}
|
||||
|
||||
// AddTarget adds a target by queuing it in the target queue.
|
||||
func (p *TargetPool) AddTarget(target Target) {
|
||||
p.addTargetQueue <- target
|
||||
}
|
||||
|
@ -95,13 +102,13 @@ func (p *TargetPool) addTarget(target Target) {
|
|||
go target.RunScraper(p.ingester, p.interval)
|
||||
}
|
||||
|
||||
// ReplaceTargets replaces the old targets by the provided new ones but reuses
|
||||
// old targets that are also present in newTargets to preserve scheduling and
|
||||
// health state. Targets no longer present are stopped.
|
||||
func (p *TargetPool) ReplaceTargets(newTargets []Target) {
|
||||
p.Lock()
|
||||
defer p.Unlock()
|
||||
|
||||
// Replace old target list by new one, but reuse those targets from the old
|
||||
// list of targets which are also in the new list (to preserve scheduling and
|
||||
// health state).
|
||||
newTargetAddresses := make(utility.Set)
|
||||
for _, newTarget := range newTargets {
|
||||
newTargetAddresses.Add(newTarget.Address())
|
||||
|
@ -113,7 +120,7 @@ func (p *TargetPool) ReplaceTargets(newTargets []Target) {
|
|||
go newTarget.RunScraper(p.ingester, p.interval)
|
||||
}
|
||||
}
|
||||
// Stop any targets no longer present.
|
||||
|
||||
var wg sync.WaitGroup
|
||||
for k, oldTarget := range p.targetsByAddress {
|
||||
if !newTargetAddresses.Has(k) {
|
||||
|
@ -130,6 +137,7 @@ func (p *TargetPool) ReplaceTargets(newTargets []Target) {
|
|||
wg.Wait()
|
||||
}
|
||||
|
||||
// Targets returns a copy of the current target list.
|
||||
func (p *TargetPool) Targets() []Target {
|
||||
p.RLock()
|
||||
defer p.RUnlock()
|
||||
|
|
|
@ -115,7 +115,7 @@ func TestTargetPoolReplaceTargets(t *testing.T) {
|
|||
pool := NewTargetPool(nil, nil, nopIngester{}, time.Duration(1))
|
||||
oldTarget1 := &target{
|
||||
address: "example1",
|
||||
state: UNREACHABLE,
|
||||
state: Unreachable,
|
||||
scraperStopping: make(chan struct{}),
|
||||
scraperStopped: make(chan struct{}),
|
||||
newBaseLabels: make(chan clientmodel.LabelSet, 1),
|
||||
|
@ -123,7 +123,7 @@ func TestTargetPoolReplaceTargets(t *testing.T) {
|
|||
}
|
||||
oldTarget2 := &target{
|
||||
address: "example2",
|
||||
state: UNREACHABLE,
|
||||
state: Unreachable,
|
||||
scraperStopping: make(chan struct{}),
|
||||
scraperStopped: make(chan struct{}),
|
||||
newBaseLabels: make(chan clientmodel.LabelSet, 1),
|
||||
|
@ -131,7 +131,7 @@ func TestTargetPoolReplaceTargets(t *testing.T) {
|
|||
}
|
||||
newTarget1 := &target{
|
||||
address: "example1",
|
||||
state: ALIVE,
|
||||
state: Alive,
|
||||
scraperStopping: make(chan struct{}),
|
||||
scraperStopped: make(chan struct{}),
|
||||
newBaseLabels: make(chan clientmodel.LabelSet, 1),
|
||||
|
@ -139,7 +139,7 @@ func TestTargetPoolReplaceTargets(t *testing.T) {
|
|||
}
|
||||
newTarget2 := &target{
|
||||
address: "example3",
|
||||
state: ALIVE,
|
||||
state: Alive,
|
||||
scraperStopping: make(chan struct{}),
|
||||
scraperStopped: make(chan struct{}),
|
||||
newBaseLabels: make(chan clientmodel.LabelSet, 1),
|
||||
|
|
|
@ -16,6 +16,7 @@ package rules
|
|||
import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"reflect"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
|
@ -28,25 +29,25 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
// The metric name for synthetic alert timeseries.
|
||||
// AlertMetricName is the metric name for synthetic alert timeseries.
|
||||
AlertMetricName clientmodel.LabelValue = "ALERTS"
|
||||
|
||||
// The label name indicating the name of an alert.
|
||||
// AlertNameLabel is the label name indicating the name of an alert.
|
||||
AlertNameLabel clientmodel.LabelName = "alertname"
|
||||
// The label name indicating the state of an alert.
|
||||
// AlertStateLabel is the label name indicating the state of an alert.
|
||||
AlertStateLabel clientmodel.LabelName = "alertstate"
|
||||
)
|
||||
|
||||
// States that active alerts can be in.
|
||||
// AlertState denotes the state of an active alert.
|
||||
type AlertState int
|
||||
|
||||
func (s AlertState) String() string {
|
||||
switch s {
|
||||
case INACTIVE:
|
||||
case Inactive:
|
||||
return "inactive"
|
||||
case PENDING:
|
||||
case Pending:
|
||||
return "pending"
|
||||
case FIRING:
|
||||
case Firing:
|
||||
return "firing"
|
||||
default:
|
||||
panic("undefined")
|
||||
|
@ -54,9 +55,14 @@ func (s AlertState) String() string {
|
|||
}
|
||||
|
||||
const (
|
||||
INACTIVE AlertState = iota
|
||||
PENDING
|
||||
FIRING
|
||||
// Inactive alerts are neither firing nor pending.
|
||||
Inactive AlertState = iota
|
||||
// Pending alerts have been active for less than the configured
|
||||
// threshold duration.
|
||||
Pending
|
||||
// Firing alerts have been active for longer than the configured
|
||||
// threshold duration.
|
||||
Firing
|
||||
)
|
||||
|
||||
// Alert is used to track active (pending/firing) alerts over time.
|
||||
|
@ -65,9 +71,9 @@ type Alert struct {
|
|||
Name string
|
||||
// The vector element labelset triggering this alert.
|
||||
Labels clientmodel.LabelSet
|
||||
// The state of the alert (PENDING or FIRING).
|
||||
// The state of the alert (Pending or Firing).
|
||||
State AlertState
|
||||
// The time when the alert first transitioned into PENDING state.
|
||||
// The time when the alert first transitioned into Pending state.
|
||||
ActiveSince clientmodel.Timestamp
|
||||
// The value of the alert expression for this vector element.
|
||||
Value clientmodel.SampleValue
|
||||
|
@ -91,14 +97,14 @@ func (a Alert) sample(timestamp clientmodel.Timestamp, value clientmodel.SampleV
|
|||
}
|
||||
}
|
||||
|
||||
// An alerting rule generates alerts from its vector expression.
|
||||
// An AlertingRule generates alerts from its vector expression.
|
||||
type AlertingRule struct {
|
||||
// The name of the alert.
|
||||
name string
|
||||
// The vector expression from which to generate alerts.
|
||||
Vector ast.VectorNode
|
||||
// The duration for which a labelset needs to persist in the expression
|
||||
// output vector before an alert transitions from PENDING to FIRING state.
|
||||
// output vector before an alert transitions from Pending to Firing state.
|
||||
holdDuration time.Duration
|
||||
// Extra labels to attach to the resulting alert sample vectors.
|
||||
Labels clientmodel.LabelSet
|
||||
|
@ -109,21 +115,24 @@ type AlertingRule struct {
|
|||
|
||||
// Protects the below.
|
||||
mutex sync.Mutex
|
||||
// A map of alerts which are currently active (PENDING or FIRING), keyed by
|
||||
// A map of alerts which are currently active (Pending or Firing), keyed by
|
||||
// the fingerprint of the labelset they correspond to.
|
||||
activeAlerts map[clientmodel.Fingerprint]*Alert
|
||||
}
|
||||
|
||||
// Name returns the name of the alert.
|
||||
func (rule *AlertingRule) Name() string {
|
||||
return rule.name
|
||||
}
|
||||
|
||||
// EvalRaw returns the raw value of the rule expression, without creating alerts.
|
||||
func (rule *AlertingRule) EvalRaw(timestamp clientmodel.Timestamp, storage local.Storage) (ast.Vector, error) {
|
||||
return ast.EvalVectorInstant(rule.Vector, timestamp, storage, stats.NewTimerGroup())
|
||||
}
|
||||
|
||||
// Eval evaluates the rule expression and then creates pending alerts and fires
|
||||
// or removes previously pending alerts accordingly.
|
||||
func (rule *AlertingRule) Eval(timestamp clientmodel.Timestamp, storage local.Storage) (ast.Vector, error) {
|
||||
// Get the raw value of the rule expression.
|
||||
exprResult, err := rule.EvalRaw(timestamp, storage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -150,7 +159,7 @@ func (rule *AlertingRule) Eval(timestamp clientmodel.Timestamp, storage local.St
|
|||
rule.activeAlerts[*fp] = &Alert{
|
||||
Name: rule.name,
|
||||
Labels: labels,
|
||||
State: PENDING,
|
||||
State: Pending,
|
||||
ActiveSince: timestamp,
|
||||
Value: sample.Value,
|
||||
}
|
||||
|
@ -169,9 +178,9 @@ func (rule *AlertingRule) Eval(timestamp clientmodel.Timestamp, storage local.St
|
|||
continue
|
||||
}
|
||||
|
||||
if activeAlert.State == PENDING && timestamp.Sub(activeAlert.ActiveSince) >= rule.holdDuration {
|
||||
if activeAlert.State == Pending && timestamp.Sub(activeAlert.ActiveSince) >= rule.holdDuration {
|
||||
vector = append(vector, activeAlert.sample(timestamp, 0))
|
||||
activeAlert.State = FIRING
|
||||
activeAlert.State = Firing
|
||||
}
|
||||
|
||||
vector = append(vector, activeAlert.sample(timestamp, 1))
|
||||
|
@ -180,12 +189,17 @@ func (rule *AlertingRule) Eval(timestamp clientmodel.Timestamp, storage local.St
|
|||
return vector, nil
|
||||
}
|
||||
|
||||
// ToDotGraph returns the text representation of a dot graph.
|
||||
func (rule *AlertingRule) ToDotGraph() string {
|
||||
graph := fmt.Sprintf(`digraph "Rules" {
|
||||
graph := fmt.Sprintf(
|
||||
`digraph "Rules" {
|
||||
%#p[shape="box",label="ALERT %s IF FOR %s"];
|
||||
%#p -> %#p;
|
||||
%#p -> %x;
|
||||
%s
|
||||
}`, &rule, rule.name, utility.DurationToString(rule.holdDuration), &rule, rule.Vector, rule.Vector.NodeTreeToDotGraph())
|
||||
}`,
|
||||
&rule, rule.name, utility.DurationToString(rule.holdDuration),
|
||||
&rule, reflect.ValueOf(rule.Vector).Pointer(),
|
||||
rule.Vector.NodeTreeToDotGraph())
|
||||
return graph
|
||||
}
|
||||
|
||||
|
@ -193,6 +207,7 @@ func (rule *AlertingRule) String() string {
|
|||
return fmt.Sprintf("ALERT %s IF %s FOR %s WITH %s", rule.name, rule.Vector, utility.DurationToString(rule.holdDuration), rule.Labels)
|
||||
}
|
||||
|
||||
// HTMLSnippet returns an HTML snippet representing this alerting rule.
|
||||
func (rule *AlertingRule) HTMLSnippet() template.HTML {
|
||||
alertMetric := clientmodel.Metric{
|
||||
clientmodel.MetricNameLabel: AlertMetricName,
|
||||
|
@ -208,11 +223,12 @@ func (rule *AlertingRule) HTMLSnippet() template.HTML {
|
|||
rule.Labels))
|
||||
}
|
||||
|
||||
// State returns the "maximum" state: firing > pending > inactive.
|
||||
func (rule *AlertingRule) State() AlertState {
|
||||
rule.mutex.Lock()
|
||||
defer rule.mutex.Unlock()
|
||||
|
||||
maxState := INACTIVE
|
||||
maxState := Inactive
|
||||
for _, activeAlert := range rule.activeAlerts {
|
||||
if activeAlert.State > maxState {
|
||||
maxState = activeAlert.State
|
||||
|
@ -221,6 +237,7 @@ func (rule *AlertingRule) State() AlertState {
|
|||
return maxState
|
||||
}
|
||||
|
||||
// ActiveAlerts returns a slice of active alerts.
|
||||
func (rule *AlertingRule) ActiveAlerts() []Alert {
|
||||
rule.mutex.Lock()
|
||||
defer rule.mutex.Unlock()
|
||||
|
@ -232,7 +249,7 @@ func (rule *AlertingRule) ActiveAlerts() []Alert {
|
|||
return alerts
|
||||
}
|
||||
|
||||
// Construct a new AlertingRule.
|
||||
// NewAlertingRule constructs a new AlertingRule.
|
||||
func NewAlertingRule(name string, vector ast.VectorNode, holdDuration time.Duration, labels clientmodel.LabelSet, summary string, description string) *AlertingRule {
|
||||
return &AlertingRule{
|
||||
name: name,
|
||||
|
|
|
@ -17,6 +17,7 @@ import (
|
|||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
|
@ -223,7 +224,7 @@ func EvalToVector(node Node, timestamp clientmodel.Timestamp, storage local.Stor
|
|||
evalTimer.Stop()
|
||||
return vector, nil
|
||||
case MATRIX:
|
||||
return nil, errors.New("Matrices not supported by EvalToVector")
|
||||
return nil, errors.New("matrices not supported by EvalToVector")
|
||||
case STRING:
|
||||
str := node.(StringNode).Eval(timestamp)
|
||||
evalTimer.Stop()
|
||||
|
@ -242,7 +243,7 @@ func (node *ScalarLiteral) NodeTreeToDotGraph() string {
|
|||
func functionArgsToDotGraph(node Node, args []Node) string {
|
||||
graph := ""
|
||||
for _, arg := range args {
|
||||
graph += fmt.Sprintf("%#p -> %#p;\n", node, arg)
|
||||
graph += fmt.Sprintf("%x -> %x;\n", reflect.ValueOf(node).Pointer(), reflect.ValueOf(arg).Pointer())
|
||||
}
|
||||
for _, arg := range args {
|
||||
graph += arg.NodeTreeToDotGraph()
|
||||
|
@ -260,13 +261,21 @@ func (node *ScalarFunctionCall) NodeTreeToDotGraph() string {
|
|||
|
||||
// NodeTreeToDotGraph returns a DOT representation of the expression.
|
||||
func (node *ScalarArithExpr) NodeTreeToDotGraph() string {
|
||||
graph := fmt.Sprintf(`
|
||||
%#p[label="%s"];
|
||||
%#p -> %#p;
|
||||
%#p -> %#p;
|
||||
nodeAddr := reflect.ValueOf(node).Pointer()
|
||||
graph := fmt.Sprintf(
|
||||
`
|
||||
%x[label="%s"];
|
||||
%x -> %x;
|
||||
%x -> %x;
|
||||
%s
|
||||
%s
|
||||
}`, node, node.opType, node, node.lhs, node, node.rhs, node.lhs.NodeTreeToDotGraph(), node.rhs.NodeTreeToDotGraph())
|
||||
}`,
|
||||
nodeAddr, node.opType,
|
||||
nodeAddr, reflect.ValueOf(node.lhs).Pointer(),
|
||||
nodeAddr, reflect.ValueOf(node.rhs).Pointer(),
|
||||
node.lhs.NodeTreeToDotGraph(),
|
||||
node.rhs.NodeTreeToDotGraph(),
|
||||
)
|
||||
return graph
|
||||
}
|
||||
|
||||
|
@ -295,20 +304,28 @@ func (node *VectorAggregation) NodeTreeToDotGraph() string {
|
|||
node,
|
||||
node.aggrType,
|
||||
strings.Join(groupByStrings, ", "))
|
||||
graph += fmt.Sprintf("%#p -> %#p;\n", node, node.vector)
|
||||
graph += fmt.Sprintf("%#p -> %x;\n", node, reflect.ValueOf(node.vector).Pointer())
|
||||
graph += node.vector.NodeTreeToDotGraph()
|
||||
return graph
|
||||
}
|
||||
|
||||
// NodeTreeToDotGraph returns a DOT representation of the expression.
|
||||
func (node *VectorArithExpr) NodeTreeToDotGraph() string {
|
||||
graph := fmt.Sprintf(`
|
||||
%#p[label="%s"];
|
||||
%#p -> %#p;
|
||||
%#p -> %#p;
|
||||
nodeAddr := reflect.ValueOf(node).Pointer()
|
||||
graph := fmt.Sprintf(
|
||||
`
|
||||
%x[label="%s"];
|
||||
%x -> %x;
|
||||
%x -> %x;
|
||||
%s
|
||||
%s
|
||||
`, node, node.opType, node, node.lhs, node, node.rhs, node.lhs.NodeTreeToDotGraph(), node.rhs.NodeTreeToDotGraph())
|
||||
}`,
|
||||
nodeAddr, node.opType,
|
||||
nodeAddr, reflect.ValueOf(node.lhs).Pointer(),
|
||||
nodeAddr, reflect.ValueOf(node.rhs).Pointer(),
|
||||
node.lhs.NodeTreeToDotGraph(),
|
||||
node.rhs.NodeTreeToDotGraph(),
|
||||
)
|
||||
return graph
|
||||
}
|
||||
|
||||
|
|
|
@ -25,16 +25,23 @@ import (
|
|||
"github.com/prometheus/prometheus/utility"
|
||||
)
|
||||
|
||||
// CreateRecordingRule is a convenience function to create a recording rule.
|
||||
func CreateRecordingRule(name string, labels clientmodel.LabelSet, expr ast.Node, permanent bool) (*RecordingRule, error) {
|
||||
if _, ok := expr.(ast.VectorNode); !ok {
|
||||
return nil, fmt.Errorf("Recording rule expression %v does not evaluate to vector type", expr)
|
||||
return nil, fmt.Errorf("recording rule expression %v does not evaluate to vector type", expr)
|
||||
}
|
||||
return NewRecordingRule(name, labels, expr.(ast.VectorNode), permanent), nil
|
||||
return &RecordingRule{
|
||||
name: name,
|
||||
labels: labels,
|
||||
vector: expr.(ast.VectorNode),
|
||||
permanent: permanent,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// CreateAlertingRule is a convenience function to create a new alerting rule.
|
||||
func CreateAlertingRule(name string, expr ast.Node, holdDurationStr string, labels clientmodel.LabelSet, summary string, description string) (*AlertingRule, error) {
|
||||
if _, ok := expr.(ast.VectorNode); !ok {
|
||||
return nil, fmt.Errorf("Alert rule expression %v does not evaluate to vector type", expr)
|
||||
return nil, fmt.Errorf("alert rule expression %v does not evaluate to vector type", expr)
|
||||
}
|
||||
holdDuration, err := utility.StringToDuration(holdDurationStr)
|
||||
if err != nil {
|
||||
|
@ -43,10 +50,11 @@ func CreateAlertingRule(name string, expr ast.Node, holdDurationStr string, labe
|
|||
return NewAlertingRule(name, expr.(ast.VectorNode), holdDuration, labels, summary, description), nil
|
||||
}
|
||||
|
||||
// NewFunctionCall is a convenience function to create a new AST function-call node.
|
||||
func NewFunctionCall(name string, args []ast.Node) (ast.Node, error) {
|
||||
function, err := ast.GetFunction(name)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Unknown function \"%v\"", name)
|
||||
return nil, fmt.Errorf("unknown function %q", name)
|
||||
}
|
||||
functionCall, err := ast.NewFunctionCall(function, args)
|
||||
if err != nil {
|
||||
|
@ -55,9 +63,10 @@ func NewFunctionCall(name string, args []ast.Node) (ast.Node, error) {
|
|||
return functionCall, nil
|
||||
}
|
||||
|
||||
// NewVectorAggregation is a convenience function to create a new AST vector aggregation.
|
||||
func NewVectorAggregation(aggrTypeStr string, vector ast.Node, groupBy clientmodel.LabelNames, keepExtraLabels bool) (*ast.VectorAggregation, error) {
|
||||
if _, ok := vector.(ast.VectorNode); !ok {
|
||||
return nil, fmt.Errorf("Operand of %v aggregation must be of vector type", aggrTypeStr)
|
||||
return nil, fmt.Errorf("operand of %v aggregation must be of vector type", aggrTypeStr)
|
||||
}
|
||||
var aggrTypes = map[string]ast.AggrType{
|
||||
"SUM": ast.SUM,
|
||||
|
@ -68,11 +77,12 @@ func NewVectorAggregation(aggrTypeStr string, vector ast.Node, groupBy clientmod
|
|||
}
|
||||
aggrType, ok := aggrTypes[aggrTypeStr]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("Unknown aggregation type '%v'", aggrTypeStr)
|
||||
return nil, fmt.Errorf("unknown aggregation type %q", aggrTypeStr)
|
||||
}
|
||||
return ast.NewVectorAggregation(aggrType, vector.(ast.VectorNode), groupBy, keepExtraLabels), nil
|
||||
}
|
||||
|
||||
// NewArithExpr is a convenience function to create a new AST arithmetic expression.
|
||||
func NewArithExpr(opTypeStr string, lhs ast.Node, rhs ast.Node) (ast.Node, error) {
|
||||
var opTypes = map[string]ast.BinOpType{
|
||||
"+": ast.ADD,
|
||||
|
@ -91,7 +101,7 @@ func NewArithExpr(opTypeStr string, lhs ast.Node, rhs ast.Node) (ast.Node, error
|
|||
}
|
||||
opType, ok := opTypes[opTypeStr]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("Invalid binary operator \"%v\"", opTypeStr)
|
||||
return nil, fmt.Errorf("invalid binary operator %q", opTypeStr)
|
||||
}
|
||||
expr, err := ast.NewArithExpr(opType, lhs, rhs)
|
||||
if err != nil {
|
||||
|
@ -100,6 +110,7 @@ func NewArithExpr(opTypeStr string, lhs ast.Node, rhs ast.Node) (ast.Node, error
|
|||
return expr, nil
|
||||
}
|
||||
|
||||
// NewMatrixSelector is a convenience function to create a new AST matrix selector.
|
||||
func NewMatrixSelector(vector ast.Node, intervalStr string) (ast.MatrixNode, error) {
|
||||
switch vector.(type) {
|
||||
case *ast.VectorSelector:
|
||||
|
@ -107,7 +118,7 @@ func NewMatrixSelector(vector ast.Node, intervalStr string) (ast.MatrixNode, err
|
|||
break
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("Intervals are currently only supported for vector selectors.")
|
||||
return nil, fmt.Errorf("intervals are currently only supported for vector selectors")
|
||||
}
|
||||
interval, err := utility.StringToDuration(intervalStr)
|
||||
if err != nil {
|
||||
|
@ -126,11 +137,13 @@ func newLabelMatcher(matchTypeStr string, name clientmodel.LabelName, value clie
|
|||
}
|
||||
matchType, ok := matchTypes[matchTypeStr]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("Invalid label matching operator \"%v\"", matchTypeStr)
|
||||
return nil, fmt.Errorf("invalid label matching operator %q", matchTypeStr)
|
||||
}
|
||||
return metric.NewLabelMatcher(matchType, name, value)
|
||||
}
|
||||
|
||||
// TableLinkForExpression creates an escaped relative link to the table view of
|
||||
// the provided expression.
|
||||
func TableLinkForExpression(expr string) string {
|
||||
// url.QueryEscape percent-escapes everything except spaces, for which it
|
||||
// uses "+". However, in the non-query part of a URI, only percent-escaped
|
||||
|
@ -143,6 +156,8 @@ func TableLinkForExpression(expr string) string {
|
|||
return fmt.Sprintf("/graph#%s", strings.Replace(urlData, "+", "%20", -1))
|
||||
}
|
||||
|
||||
// GraphLinkForExpression creates an escaped relative link to the graph view of
|
||||
// the provided expression.
|
||||
func GraphLinkForExpression(expr string) string {
|
||||
urlData := url.QueryEscape(fmt.Sprintf(`[{"expr":%q}]`, expr))
|
||||
return fmt.Sprintf("/graph#%s", strings.Replace(urlData, "+", "%20", -1))
|
||||
|
|
|
@ -26,6 +26,7 @@ import (
|
|||
"github.com/prometheus/prometheus/rules/ast"
|
||||
)
|
||||
|
||||
// RulesLexer is the lexer for rule expressions.
|
||||
type RulesLexer struct {
|
||||
// Errors encountered during parsing.
|
||||
errors []string
|
||||
|
@ -94,38 +95,37 @@ func newRulesLexer(src io.Reader, singleExpr bool) *RulesLexer {
|
|||
return lexer
|
||||
}
|
||||
|
||||
func LoadFromReader(rulesReader io.Reader, singleExpr bool) (interface{}, error) {
|
||||
func lexAndParse(rulesReader io.Reader, singleExpr bool) (*RulesLexer, error) {
|
||||
lexer := newRulesLexer(rulesReader, singleExpr)
|
||||
ret := yyParse(lexer)
|
||||
if ret != 0 && len(lexer.errors) == 0 {
|
||||
lexer.Error("Unknown parser error")
|
||||
lexer.Error("unknown parser error")
|
||||
}
|
||||
|
||||
if len(lexer.errors) > 0 {
|
||||
err := errors.New(strings.Join(lexer.errors, "\n"))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if singleExpr {
|
||||
return lexer.parsedExpr, nil
|
||||
} else {
|
||||
return lexer.parsedRules, nil
|
||||
}
|
||||
return lexer, nil
|
||||
}
|
||||
|
||||
// LoadRulesFromReader parses rules from the provided reader and returns them.
|
||||
func LoadRulesFromReader(rulesReader io.Reader) ([]Rule, error) {
|
||||
expr, err := LoadFromReader(rulesReader, false)
|
||||
lexer, err := lexAndParse(rulesReader, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return expr.([]Rule), err
|
||||
return lexer.parsedRules, err
|
||||
}
|
||||
|
||||
// LoadRulesFromString parses rules from the provided string returns them.
|
||||
func LoadRulesFromString(rulesString string) ([]Rule, error) {
|
||||
rulesReader := strings.NewReader(rulesString)
|
||||
return LoadRulesFromReader(rulesReader)
|
||||
}
|
||||
|
||||
// LoadRulesFromFile parses rules from the file of the provided name and returns
|
||||
// them.
|
||||
func LoadRulesFromFile(fileName string) ([]Rule, error) {
|
||||
rulesReader, err := os.Open(fileName)
|
||||
if err != nil {
|
||||
|
@ -135,19 +135,25 @@ func LoadRulesFromFile(fileName string) ([]Rule, error) {
|
|||
return LoadRulesFromReader(rulesReader)
|
||||
}
|
||||
|
||||
// LoadExprFromReader parses a single expression from the provided reader and
|
||||
// returns it as an AST node.
|
||||
func LoadExprFromReader(exprReader io.Reader) (ast.Node, error) {
|
||||
expr, err := LoadFromReader(exprReader, true)
|
||||
lexer, err := lexAndParse(exprReader, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return expr.(ast.Node), err
|
||||
return lexer.parsedExpr, err
|
||||
}
|
||||
|
||||
// LoadExprFromString parses a single expression from the provided string and
|
||||
// returns it as an AST node.
|
||||
func LoadExprFromString(exprString string) (ast.Node, error) {
|
||||
exprReader := strings.NewReader(exprString)
|
||||
return LoadExprFromReader(exprReader)
|
||||
}
|
||||
|
||||
// LoadExprFromFile parses a single expression from the file of the provided
|
||||
// name and returns it as an AST node.
|
||||
func LoadExprFromFile(fileName string) (ast.Node, error) {
|
||||
exprReader, err := os.Open(fileName)
|
||||
if err != nil {
|
||||
|
|
|
@ -62,6 +62,8 @@ func init() {
|
|||
prometheus.MustRegister(evalDuration)
|
||||
}
|
||||
|
||||
// A RuleManager manages recording and alerting rules. Create instances with
|
||||
// NewRuleManager.
|
||||
type RuleManager interface {
|
||||
// Load and add rules from rule files specified in the configuration.
|
||||
AddRulesFromConfig(config config.Config) error
|
||||
|
@ -88,9 +90,10 @@ type ruleManager struct {
|
|||
results chan<- *extraction.Result
|
||||
notificationHandler *notification.NotificationHandler
|
||||
|
||||
prometheusUrl string
|
||||
prometheusURL string
|
||||
}
|
||||
|
||||
// RuleManagerOptions bundles options for the RuleManager.
|
||||
type RuleManagerOptions struct {
|
||||
EvaluationInterval time.Duration
|
||||
Storage local.Storage
|
||||
|
@ -98,9 +101,11 @@ type RuleManagerOptions struct {
|
|||
NotificationHandler *notification.NotificationHandler
|
||||
Results chan<- *extraction.Result
|
||||
|
||||
PrometheusUrl string
|
||||
PrometheusURL string
|
||||
}
|
||||
|
||||
// NewRuleManager returns an implementation of RuleManager, ready to be started
|
||||
// by calling the Run method.
|
||||
func NewRuleManager(o *RuleManagerOptions) RuleManager {
|
||||
manager := &ruleManager{
|
||||
rules: []rules.Rule{},
|
||||
|
@ -110,7 +115,7 @@ func NewRuleManager(o *RuleManagerOptions) RuleManager {
|
|||
storage: o.Storage,
|
||||
results: o.Results,
|
||||
notificationHandler: o.NotificationHandler,
|
||||
prometheusUrl: o.PrometheusUrl,
|
||||
prometheusURL: o.PrometheusURL,
|
||||
}
|
||||
return manager
|
||||
}
|
||||
|
@ -120,6 +125,12 @@ func (m *ruleManager) Run() {
|
|||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
// TODO(beorn): This has the same problem as the scraper had
|
||||
// before. If rule evaluation takes longer than the interval,
|
||||
// there is a 50% chance per iteration that - after stopping the
|
||||
// ruleManager - a new evaluation will be started rather than
|
||||
// the ruleManager actually stopped. We need a similar
|
||||
// contraption here as in the scraper.
|
||||
select {
|
||||
case <-ticker.C:
|
||||
start := time.Now()
|
||||
|
@ -145,7 +156,7 @@ func (m *ruleManager) queueAlertNotifications(rule *rules.AlertingRule, timestam
|
|||
|
||||
notifications := make(notification.NotificationReqs, 0, len(activeAlerts))
|
||||
for _, aa := range activeAlerts {
|
||||
if aa.State != rules.FIRING {
|
||||
if aa.State != rules.Firing {
|
||||
// BUG: In the future, make AlertManager support pending alerts?
|
||||
continue
|
||||
}
|
||||
|
@ -185,7 +196,7 @@ func (m *ruleManager) queueAlertNotifications(rule *rules.AlertingRule, timestam
|
|||
Value: aa.Value,
|
||||
ActiveSince: aa.ActiveSince.Time(),
|
||||
RuleString: rule.String(),
|
||||
GeneratorURL: m.prometheusUrl + rules.GraphLinkForExpression(rule.Vector.String()),
|
||||
GeneratorURL: m.prometheusURL + rules.GraphLinkForExpression(rule.Vector.String()),
|
||||
})
|
||||
}
|
||||
m.notificationHandler.SubmitReqs(notifications)
|
||||
|
|
|
@ -16,6 +16,7 @@ package rules
|
|||
import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"reflect"
|
||||
|
||||
clientmodel "github.com/prometheus/client_golang/model"
|
||||
|
||||
|
@ -32,14 +33,16 @@ type RecordingRule struct {
|
|||
permanent bool
|
||||
}
|
||||
|
||||
// Name returns the rule name.
|
||||
func (rule RecordingRule) Name() string { return rule.name }
|
||||
|
||||
// EvalRaw returns the raw value of the rule expression.
|
||||
func (rule RecordingRule) EvalRaw(timestamp clientmodel.Timestamp, storage local.Storage) (ast.Vector, error) {
|
||||
return ast.EvalVectorInstant(rule.vector, timestamp, storage, stats.NewTimerGroup())
|
||||
}
|
||||
|
||||
// Eval evaluates the rule and then overrides the metric names and labels accordingly.
|
||||
func (rule RecordingRule) Eval(timestamp clientmodel.Timestamp, storage local.Storage) (ast.Vector, error) {
|
||||
// Get the raw value of the rule expression.
|
||||
vector, err := rule.EvalRaw(timestamp, storage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -60,12 +63,18 @@ func (rule RecordingRule) Eval(timestamp clientmodel.Timestamp, storage local.St
|
|||
return vector, nil
|
||||
}
|
||||
|
||||
// ToDotGraph returns the text representation of a dot graph.
|
||||
func (rule RecordingRule) ToDotGraph() string {
|
||||
graph := fmt.Sprintf(`digraph "Rules" {
|
||||
graph := fmt.Sprintf(
|
||||
`digraph "Rules" {
|
||||
%#p[shape="box",label="%s = "];
|
||||
%#p -> %#p;
|
||||
%#p -> %x;
|
||||
%s
|
||||
}`, &rule, rule.name, &rule, rule.vector, rule.vector.NodeTreeToDotGraph())
|
||||
}`,
|
||||
&rule, rule.name,
|
||||
&rule, reflect.ValueOf(rule.vector).Pointer(),
|
||||
rule.vector.NodeTreeToDotGraph(),
|
||||
)
|
||||
return graph
|
||||
}
|
||||
|
||||
|
@ -73,6 +82,7 @@ func (rule RecordingRule) String() string {
|
|||
return fmt.Sprintf("%s%s = %s\n", rule.name, rule.labels, rule.vector)
|
||||
}
|
||||
|
||||
// HTMLSnippet returns an HTML snippet representing this rule.
|
||||
func (rule RecordingRule) HTMLSnippet() template.HTML {
|
||||
ruleExpr := rule.vector.String()
|
||||
return template.HTML(fmt.Sprintf(
|
||||
|
@ -83,13 +93,3 @@ func (rule RecordingRule) HTMLSnippet() template.HTML {
|
|||
GraphLinkForExpression(ruleExpr),
|
||||
ruleExpr))
|
||||
}
|
||||
|
||||
// Construct a new RecordingRule.
|
||||
func NewRecordingRule(name string, labels clientmodel.LabelSet, vector ast.VectorNode, permanent bool) *RecordingRule {
|
||||
return &RecordingRule{
|
||||
name: name,
|
||||
labels: labels,
|
||||
vector: vector,
|
||||
permanent: permanent,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ type QueryTiming int
|
|||
const (
|
||||
TotalEvalTime QueryTiming = iota
|
||||
ResultSortTime
|
||||
JsonEncodeTime
|
||||
JSONEncodeTime
|
||||
PreloadTime
|
||||
TotalQueryPreparationTime
|
||||
InnerViewBuildingTime
|
||||
|
@ -44,7 +44,7 @@ func (s QueryTiming) String() string {
|
|||
return "Total eval time"
|
||||
case ResultSortTime:
|
||||
return "Result sorting time"
|
||||
case JsonEncodeTime:
|
||||
case JSONEncodeTime:
|
||||
return "JSON encoding time"
|
||||
case PreloadTime:
|
||||
return "Query preloading time"
|
||||
|
|
|
@ -20,7 +20,7 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
// A timer that can be started and stopped and accumulates the total time it
|
||||
// A Timer that can be started and stopped and accumulates the total time it
|
||||
// was running (the time between Start() and Stop()).
|
||||
type Timer struct {
|
||||
name fmt.Stringer
|
||||
|
@ -51,12 +51,12 @@ type TimerGroup struct {
|
|||
child *TimerGroup
|
||||
}
|
||||
|
||||
// Construct a new TimerGroup.
|
||||
// NewTimerGroup constructs a new TimerGroup.
|
||||
func NewTimerGroup() *TimerGroup {
|
||||
return &TimerGroup{timers: map[fmt.Stringer]*Timer{}}
|
||||
}
|
||||
|
||||
// Get (and create, if necessary) the Timer for a given code section.
|
||||
// GetTimer gets (and creates, if necessary) the Timer for a given code section.
|
||||
func (t *TimerGroup) GetTimer(name fmt.Stringer) *Timer {
|
||||
if timer, exists := t.timers[name]; exists {
|
||||
return timer
|
||||
|
@ -69,14 +69,18 @@ func (t *TimerGroup) GetTimer(name fmt.Stringer) *Timer {
|
|||
return timer
|
||||
}
|
||||
|
||||
// Timers is a slice of Timer pointers that implements Len and Swap from
|
||||
// sort.Interface.
|
||||
type Timers []*Timer
|
||||
|
||||
type byCreationTimeSorter struct{ Timers }
|
||||
|
||||
// Len implements sort.Interface.
|
||||
func (t Timers) Len() int {
|
||||
return len(t)
|
||||
}
|
||||
|
||||
// Swap implements sort.Interface.
|
||||
func (t Timers) Swap(i, j int) {
|
||||
t[i], t[j] = t[j], t[i]
|
||||
}
|
||||
|
|
|
@ -91,6 +91,7 @@ type templateExpander struct {
|
|||
funcMap text_template.FuncMap
|
||||
}
|
||||
|
||||
// NewTemplateExpander returns a template expander ready to use.
|
||||
func NewTemplateExpander(text string, name string, data interface{}, timestamp clientmodel.Timestamp, storage local.Storage) *templateExpander {
|
||||
return &templateExpander{
|
||||
text: text,
|
||||
|
@ -152,17 +153,16 @@ func NewTemplateExpander(text string, name string, data interface{}, timestamp c
|
|||
v /= 1000
|
||||
}
|
||||
return fmt.Sprintf("%.4g%s", v, prefix)
|
||||
} else {
|
||||
prefix := ""
|
||||
for _, p := range []string{"m", "u", "n", "p", "f", "a", "z", "y"} {
|
||||
if math.Abs(v) >= 1 {
|
||||
break
|
||||
}
|
||||
prefix = p
|
||||
v *= 1000
|
||||
}
|
||||
return fmt.Sprintf("%.4g%s", v, prefix)
|
||||
}
|
||||
prefix := ""
|
||||
for _, p := range []string{"m", "u", "n", "p", "f", "a", "z", "y"} {
|
||||
if math.Abs(v) >= 1 {
|
||||
break
|
||||
}
|
||||
prefix = p
|
||||
v *= 1000
|
||||
}
|
||||
return fmt.Sprintf("%.4g%s", v, prefix)
|
||||
},
|
||||
"humanize1024": func(v float64) string {
|
||||
if math.Abs(v) <= 1 {
|
||||
|
@ -204,17 +204,16 @@ func NewTemplateExpander(text string, name string, data interface{}, timestamp c
|
|||
}
|
||||
// For seconds, we display 4 significant digts.
|
||||
return fmt.Sprintf("%s%.4gs", sign, math.Floor(seconds*1000+.5)/1000)
|
||||
} else {
|
||||
prefix := ""
|
||||
for _, p := range []string{"m", "u", "n", "p", "f", "a", "z", "y"} {
|
||||
if math.Abs(v) >= 1 {
|
||||
break
|
||||
}
|
||||
prefix = p
|
||||
v *= 1000
|
||||
}
|
||||
return fmt.Sprintf("%.4g%ss", v, prefix)
|
||||
}
|
||||
prefix := ""
|
||||
for _, p := range []string{"m", "u", "n", "p", "f", "a", "z", "y"} {
|
||||
if math.Abs(v) >= 1 {
|
||||
break
|
||||
}
|
||||
prefix = p
|
||||
v *= 1000
|
||||
}
|
||||
return fmt.Sprintf("%.4g%ss", v, prefix)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -229,7 +228,7 @@ func (te templateExpander) Expand() (result string, resultErr error) {
|
|||
var ok bool
|
||||
resultErr, ok = r.(error)
|
||||
if !ok {
|
||||
resultErr = fmt.Errorf("Panic expanding template %v: %v", te.name, r)
|
||||
resultErr = fmt.Errorf("panic expanding template %v: %v", te.name, r)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
@ -237,11 +236,11 @@ func (te templateExpander) Expand() (result string, resultErr error) {
|
|||
var buffer bytes.Buffer
|
||||
tmpl, err := text_template.New(te.name).Funcs(te.funcMap).Parse(te.text)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Error parsing template %v: %v", te.name, err)
|
||||
return "", fmt.Errorf("error parsing template %v: %v", te.name, err)
|
||||
}
|
||||
err = tmpl.Execute(&buffer, te.data)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Error executing template %v: %v", te.name, err)
|
||||
return "", fmt.Errorf("error executing template %v: %v", te.name, err)
|
||||
}
|
||||
return buffer.String(), nil
|
||||
}
|
||||
|
@ -253,7 +252,7 @@ func (te templateExpander) ExpandHTML(templateFiles []string) (result string, re
|
|||
var ok bool
|
||||
resultErr, ok = r.(error)
|
||||
if !ok {
|
||||
resultErr = fmt.Errorf("Panic expanding template %v: %v", te.name, r)
|
||||
resultErr = fmt.Errorf("panic expanding template %v: %v", te.name, r)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
@ -269,17 +268,17 @@ func (te templateExpander) ExpandHTML(templateFiles []string) (result string, re
|
|||
})
|
||||
tmpl, err := tmpl.Parse(te.text)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Error parsing template %v: %v", te.name, err)
|
||||
return "", fmt.Errorf("error parsing template %v: %v", te.name, err)
|
||||
}
|
||||
if len(templateFiles) > 0 {
|
||||
_, err = tmpl.ParseFiles(templateFiles...)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Error parsing template files for %v: %v", te.name, err)
|
||||
return "", fmt.Errorf("error parsing template files for %v: %v", te.name, err)
|
||||
}
|
||||
}
|
||||
err = tmpl.Execute(&buffer, te.data)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Error executing template %v: %v", te.name, err)
|
||||
return "", fmt.Errorf("error executing template %v: %v", te.name, err)
|
||||
}
|
||||
return buffer.String(), nil
|
||||
}
|
||||
|
|
|
@ -1,55 +0,0 @@
|
|||
// Copyright 2013 Prometheus Team
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package utility
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// The canonical example: http://golang.org/doc/progs/eff_bytesize.go.
|
||||
type ByteSize float64
|
||||
|
||||
const (
|
||||
_ = iota // ignore first value by assigning to blank identifier
|
||||
KB ByteSize = 1 << (10 * iota)
|
||||
MB
|
||||
GB
|
||||
TB
|
||||
PB
|
||||
EB
|
||||
ZB
|
||||
YB
|
||||
)
|
||||
|
||||
func (b ByteSize) String() string {
|
||||
switch {
|
||||
case b >= YB:
|
||||
return fmt.Sprintf("%.2fYB", b/YB)
|
||||
case b >= ZB:
|
||||
return fmt.Sprintf("%.2fZB", b/ZB)
|
||||
case b >= EB:
|
||||
return fmt.Sprintf("%.2fEB", b/EB)
|
||||
case b >= PB:
|
||||
return fmt.Sprintf("%.2fPB", b/PB)
|
||||
case b >= TB:
|
||||
return fmt.Sprintf("%.2fTB", b/TB)
|
||||
case b >= GB:
|
||||
return fmt.Sprintf("%.2fGB", b/GB)
|
||||
case b >= MB:
|
||||
return fmt.Sprintf("%.2fMB", b/MB)
|
||||
case b >= KB:
|
||||
return fmt.Sprintf("%.2fKB", b/KB)
|
||||
}
|
||||
return fmt.Sprintf("%.2fB", b)
|
||||
}
|
176
utility/cache.go
176
utility/cache.go
|
@ -1,176 +0,0 @@
|
|||
// Copyright 2013 Prometheus Team
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package utility
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type Cache interface {
|
||||
Put(k, v interface{}) (replaced bool, err error)
|
||||
PutIfAbsent(k, v interface{}) (put bool, err error)
|
||||
Get(k interface{}) (v interface{}, ok bool, err error)
|
||||
Has(k interface{}) (ok bool, err error)
|
||||
Delete(k interface{}) (deleted bool, err error)
|
||||
Clear() (cleared bool, err error)
|
||||
}
|
||||
|
||||
type LRUCache struct {
|
||||
list *list.List
|
||||
table map[interface{}]*list.Element
|
||||
|
||||
limit uint
|
||||
size uint
|
||||
}
|
||||
|
||||
func NewLRUCache(limit uint) *LRUCache {
|
||||
return &LRUCache{
|
||||
list: list.New(),
|
||||
table: map[interface{}]*list.Element{},
|
||||
|
||||
limit: limit,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *LRUCache) Has(k interface{}) (has bool, err error) {
|
||||
_, ok := c.table[k]
|
||||
return ok, nil
|
||||
}
|
||||
|
||||
func (c *LRUCache) Get(k interface{}) (v interface{}, ok bool, err error) {
|
||||
element, ok := c.table[k]
|
||||
if !ok {
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
c.moveToFront(element)
|
||||
|
||||
return element.Value, true, nil
|
||||
}
|
||||
|
||||
func (c *LRUCache) Put(k, v interface{}) (replaced bool, err error) {
|
||||
element, ok := c.table[k]
|
||||
if ok {
|
||||
c.updateInplace(element, v)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
c.addNew(k, v)
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (c *LRUCache) PutIfAbsent(k, v interface{}) (put bool, err error) {
|
||||
if _, ok := c.table[k]; ok {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
c.addNew(k, v)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (c *LRUCache) Delete(k interface{}) (deleted bool, err error) {
|
||||
element, ok := c.table[k]
|
||||
if !ok {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
c.list.Remove(element)
|
||||
delete(c.table, k)
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (c *LRUCache) Clear() (cleared bool, err error) {
|
||||
c.list.Init()
|
||||
c.table = map[interface{}]*list.Element{}
|
||||
c.size = 0
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (c *LRUCache) updateInplace(e *list.Element, v interface{}) {
|
||||
e.Value = v
|
||||
c.moveToFront(e)
|
||||
c.checkCapacity()
|
||||
}
|
||||
|
||||
func (c *LRUCache) moveToFront(e *list.Element) {
|
||||
c.list.MoveToFront(e)
|
||||
}
|
||||
|
||||
func (c *LRUCache) addNew(k, v interface{}) {
|
||||
c.table[k] = c.list.PushFront(v)
|
||||
c.checkCapacity()
|
||||
}
|
||||
|
||||
func (c *LRUCache) checkCapacity() {
|
||||
for c.size > c.limit {
|
||||
delElem := c.list.Back()
|
||||
v := delElem.Value
|
||||
c.list.Remove(delElem)
|
||||
delete(c.table, v)
|
||||
}
|
||||
}
|
||||
|
||||
type SynchronizedCache struct {
|
||||
mu sync.Mutex
|
||||
c Cache
|
||||
}
|
||||
|
||||
func (c *SynchronizedCache) Put(k, v interface{}) (replaced bool, err error) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
return c.c.Put(k, v)
|
||||
}
|
||||
func (c *SynchronizedCache) PutIfAbsent(k, v interface{}) (put bool, err error) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
return c.PutIfAbsent(k, v)
|
||||
}
|
||||
|
||||
func (c *SynchronizedCache) Get(k interface{}) (v interface{}, ok bool, err error) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
return c.c.Get(k)
|
||||
}
|
||||
func (c *SynchronizedCache) Has(k interface{}) (ok bool, err error) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
return c.c.Has(k)
|
||||
}
|
||||
|
||||
func (c *SynchronizedCache) Delete(k interface{}) (deleted bool, err error) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
return c.c.Delete(k)
|
||||
}
|
||||
|
||||
func (c *SynchronizedCache) Clear() (cleared bool, err error) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
return c.c.Clear()
|
||||
}
|
||||
|
||||
func NewSynchronizedCache(c Cache) *SynchronizedCache {
|
||||
return &SynchronizedCache{
|
||||
c: c,
|
||||
}
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
// Copyright 2013 Prometheus Team
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Useful file/filesystem related functions.
|
||||
|
||||
package utility
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
// Returns true iff dirPath is a valid directory path.
|
||||
func IsDir(dirPath string) (bool, error) {
|
||||
finfo, err := os.Stat(dirPath)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if !finfo.IsDir() {
|
||||
return false, fmt.Errorf("%s not a directory", dirPath)
|
||||
}
|
||||
return true, nil
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
// Copyright 2013 Prometheus Team
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package utility
|
||||
|
||||
type FreeList chan interface{}
|
||||
|
||||
func NewFreeList(cap int) FreeList {
|
||||
return make(FreeList, cap)
|
||||
}
|
||||
|
||||
func (l FreeList) Get() (interface{}, bool) {
|
||||
select {
|
||||
case v := <-l:
|
||||
return v, true
|
||||
default:
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
|
||||
func (l FreeList) Give(v interface{}) bool {
|
||||
select {
|
||||
case l <- v:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (l FreeList) Close() {
|
||||
close(l)
|
||||
|
||||
for _ = range l {
|
||||
}
|
||||
}
|
|
@ -22,6 +22,11 @@ import (
|
|||
|
||||
var durationRE = regexp.MustCompile("^([0-9]+)([ywdhms]+)$")
|
||||
|
||||
// DurationToString formats a time.Duration as a string with the assumption that
|
||||
// a year always has 365 days and a day always has 24h. (The former doesn't work
|
||||
// in leap years, the latter is broken by DST switches, not to speak about leap
|
||||
// seconds, but those are not even treated properly by the duration strings in
|
||||
// the standard library.)
|
||||
func DurationToString(duration time.Duration) string {
|
||||
seconds := int64(duration / time.Second)
|
||||
factors := map[string]int64{
|
||||
|
@ -45,10 +50,13 @@ func DurationToString(duration time.Duration) string {
|
|||
return fmt.Sprintf("%v%v", seconds/factors[unit], unit)
|
||||
}
|
||||
|
||||
// StringToDuration parses a string into a time.Duration, assuming that a year
|
||||
// always has 365d, a week 7d, a day 24h. See DurationToString for problems with
|
||||
// that.
|
||||
func StringToDuration(durationStr string) (duration time.Duration, err error) {
|
||||
matches := durationRE.FindStringSubmatch(durationStr)
|
||||
if len(matches) != 3 {
|
||||
err = fmt.Errorf("Not a valid duration string: '%v'", durationStr)
|
||||
err = fmt.Errorf("not a valid duration string: %q", durationStr)
|
||||
return
|
||||
}
|
||||
durationSeconds, _ := strconv.Atoi(matches[1])
|
||||
|
|
|
@ -24,11 +24,12 @@ const (
|
|||
// environment variable.
|
||||
defaultDirectory = ""
|
||||
|
||||
// A NO-OP Closer.
|
||||
// NilCloser is a no-op Closer.
|
||||
NilCloser = nilCloser(true)
|
||||
)
|
||||
|
||||
type (
|
||||
// Closer is the interface that wraps the Close method.
|
||||
Closer interface {
|
||||
// Close reaps the underlying directory and its children. The directory
|
||||
// could be deleted by its users already.
|
||||
|
@ -74,6 +75,8 @@ func (c callbackCloser) Close() {
|
|||
c.fn()
|
||||
}
|
||||
|
||||
// NewCallbackCloser returns a Closer that calls the provided function upon
|
||||
// closing.
|
||||
func NewCallbackCloser(fn func()) *callbackCloser {
|
||||
return &callbackCloser{
|
||||
fn: fn,
|
||||
|
|
|
@ -17,8 +17,8 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
// A basic interface only useful in testing contexts for dispensing the time
|
||||
// in a controlled manner.
|
||||
// InstantProvider is a basic interface only useful in testing contexts for
|
||||
// dispensing the time in a controlled manner.
|
||||
type InstantProvider interface {
|
||||
// The current instant.
|
||||
Now() time.Time
|
||||
|
@ -35,7 +35,7 @@ type Time struct {
|
|||
Provider InstantProvider
|
||||
}
|
||||
|
||||
// Emit the current instant.
|
||||
// Now emits the current instant.
|
||||
func (t Time) Now() time.Time {
|
||||
if t.Provider == nil {
|
||||
return time.Now()
|
||||
|
|
|
@ -1,162 +0,0 @@
|
|||
// Copyright 2013 Prometheus Team
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package utility
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type state int
|
||||
|
||||
func (s state) String() string {
|
||||
switch s {
|
||||
case unstarted:
|
||||
return "unstarted"
|
||||
case started:
|
||||
return "started"
|
||||
case finished:
|
||||
return "finished"
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
const (
|
||||
unstarted state = iota
|
||||
started
|
||||
finished
|
||||
)
|
||||
|
||||
// An UncertaintyGroup models a group of operations whose result disposition is
|
||||
// tenuous and needs to be validated en masse in order to make a future
|
||||
// decision.
|
||||
type UncertaintyGroup interface {
|
||||
// Succeed makes a remark that a given action succeeded, in part.
|
||||
Succeed()
|
||||
// Fail makes a remark that a given action failed, in part. Nil values are
|
||||
// illegal.
|
||||
Fail(error)
|
||||
// MayFail makes a remark that a given action either succeeded or failed. The
|
||||
// determination is made by whether the error is nil.
|
||||
MayFail(error)
|
||||
// Wait waits for the group to have finished and emits the result of what
|
||||
// occurred for the group.
|
||||
Wait() (succeeded bool)
|
||||
// Errors emits any errors that could have occurred.
|
||||
Errors() []error
|
||||
}
|
||||
|
||||
type uncertaintyGroup struct {
|
||||
state state
|
||||
remaining uint
|
||||
successes uint
|
||||
results chan error
|
||||
anomalies []error
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
func (g *uncertaintyGroup) Succeed() {
|
||||
if g.isFinished() {
|
||||
panic("cannot remark when done")
|
||||
}
|
||||
|
||||
g.results <- nil
|
||||
}
|
||||
|
||||
func (g *uncertaintyGroup) Fail(err error) {
|
||||
if g.isFinished() {
|
||||
panic("cannot remark when done")
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
panic("expected a failure")
|
||||
}
|
||||
|
||||
g.results <- err
|
||||
}
|
||||
|
||||
func (g *uncertaintyGroup) MayFail(err error) {
|
||||
if g.isFinished() {
|
||||
panic("cannot remark when done")
|
||||
}
|
||||
|
||||
g.results <- err
|
||||
}
|
||||
|
||||
func (g *uncertaintyGroup) isFinished() bool {
|
||||
g.Lock()
|
||||
defer g.Unlock()
|
||||
|
||||
return g.state == finished
|
||||
}
|
||||
|
||||
func (g *uncertaintyGroup) finish() {
|
||||
g.Lock()
|
||||
defer g.Unlock()
|
||||
|
||||
g.state = finished
|
||||
}
|
||||
|
||||
func (g *uncertaintyGroup) start() {
|
||||
g.Lock()
|
||||
defer g.Unlock()
|
||||
|
||||
if g.state != unstarted {
|
||||
panic("cannot restart")
|
||||
}
|
||||
|
||||
g.state = started
|
||||
}
|
||||
|
||||
func (g *uncertaintyGroup) Wait() bool {
|
||||
defer close(g.results)
|
||||
g.start()
|
||||
|
||||
for g.remaining > 0 {
|
||||
result := <-g.results
|
||||
switch result {
|
||||
case nil:
|
||||
g.successes++
|
||||
default:
|
||||
g.anomalies = append(g.anomalies, result)
|
||||
}
|
||||
|
||||
g.remaining--
|
||||
}
|
||||
|
||||
g.finish()
|
||||
|
||||
return len(g.anomalies) == 0
|
||||
}
|
||||
|
||||
func (g *uncertaintyGroup) Errors() []error {
|
||||
if g.state != finished {
|
||||
panic("cannot provide errors until finished")
|
||||
}
|
||||
|
||||
return g.anomalies
|
||||
}
|
||||
|
||||
func (g *uncertaintyGroup) String() string {
|
||||
return fmt.Sprintf("UncertaintyGroup %s with %s failures", g.state, g.anomalies)
|
||||
}
|
||||
|
||||
// NewUncertaintyGroup furnishes an UncertaintyGroup for a given set of actions
|
||||
// where their quantity is known a priori.
|
||||
func NewUncertaintyGroup(count uint) UncertaintyGroup {
|
||||
return &uncertaintyGroup{
|
||||
remaining: count,
|
||||
results: make(chan error),
|
||||
}
|
||||
}
|
|
@ -1,77 +0,0 @@
|
|||
package utility
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestGroupSuccess(t *testing.T) {
|
||||
uncertaintyGroup := NewUncertaintyGroup(10)
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
go uncertaintyGroup.Succeed()
|
||||
}
|
||||
|
||||
result := make(chan bool)
|
||||
go func() {
|
||||
result <- uncertaintyGroup.Wait()
|
||||
}()
|
||||
select {
|
||||
case v := <-result:
|
||||
if !v {
|
||||
t.Fatal("expected success")
|
||||
}
|
||||
case <-time.After(time.Second):
|
||||
t.Fatal("deadline exceeded")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGroupFail(t *testing.T) {
|
||||
uncertaintyGroup := NewUncertaintyGroup(10)
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
go uncertaintyGroup.Fail(fmt.Errorf(""))
|
||||
}
|
||||
|
||||
result := make(chan bool)
|
||||
go func() {
|
||||
result <- uncertaintyGroup.Wait()
|
||||
}()
|
||||
select {
|
||||
case v := <-result:
|
||||
if v {
|
||||
t.Fatal("expected failure")
|
||||
}
|
||||
case <-time.After(time.Second):
|
||||
t.Fatal("deadline exceeded")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGroupFailMix(t *testing.T) {
|
||||
uncertaintyGroup := NewUncertaintyGroup(10)
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
go func(i int) {
|
||||
switch {
|
||||
case i%2 == 0:
|
||||
uncertaintyGroup.Fail(fmt.Errorf(""))
|
||||
default:
|
||||
uncertaintyGroup.Succeed()
|
||||
}
|
||||
}(i)
|
||||
}
|
||||
|
||||
result := make(chan bool)
|
||||
go func() {
|
||||
result <- uncertaintyGroup.Wait()
|
||||
}()
|
||||
select {
|
||||
case v := <-result:
|
||||
if v {
|
||||
t.Fatal("expected failure")
|
||||
}
|
||||
case <-time.After(time.Second):
|
||||
t.Fatal("deadline exceeded")
|
||||
}
|
||||
}
|
|
@ -22,17 +22,13 @@ import (
|
|||
"github.com/prometheus/prometheus/rules/manager"
|
||||
)
|
||||
|
||||
// AlertStatus bundles alerting rules and the mapping of alert states to row
|
||||
// classes.
|
||||
type AlertStatus struct {
|
||||
AlertingRules []*rules.AlertingRule
|
||||
AlertStateToRowClass map[rules.AlertState]string
|
||||
}
|
||||
|
||||
type AlertsHandler struct {
|
||||
RuleManager manager.RuleManager
|
||||
|
||||
mutex sync.Mutex
|
||||
}
|
||||
|
||||
type byAlertStateSorter struct {
|
||||
alerts []*rules.AlertingRule
|
||||
}
|
||||
|
@ -49,6 +45,13 @@ func (s byAlertStateSorter) Swap(i, j int) {
|
|||
s.alerts[i], s.alerts[j] = s.alerts[j], s.alerts[i]
|
||||
}
|
||||
|
||||
// AlertsHandler implements http.Handler.
|
||||
type AlertsHandler struct {
|
||||
RuleManager manager.RuleManager
|
||||
|
||||
mutex sync.Mutex
|
||||
}
|
||||
|
||||
func (h *AlertsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
h.mutex.Lock()
|
||||
defer h.mutex.Unlock()
|
||||
|
@ -60,9 +63,9 @@ func (h *AlertsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||
alertStatus := AlertStatus{
|
||||
AlertingRules: alertsSorter.alerts,
|
||||
AlertStateToRowClass: map[rules.AlertState]string{
|
||||
rules.INACTIVE: "success",
|
||||
rules.PENDING: "warning",
|
||||
rules.FIRING: "error",
|
||||
rules.Inactive: "success",
|
||||
rules.Pending: "warning",
|
||||
rules.Firing: "error",
|
||||
},
|
||||
}
|
||||
executeTemplate(w, "alerts", alertStatus)
|
||||
|
|
|
@ -22,9 +22,10 @@ import (
|
|||
"github.com/prometheus/prometheus/retrieval"
|
||||
"github.com/prometheus/prometheus/storage/local"
|
||||
"github.com/prometheus/prometheus/utility"
|
||||
"github.com/prometheus/prometheus/web/http_utils"
|
||||
"github.com/prometheus/prometheus/web/httputils"
|
||||
)
|
||||
|
||||
// MetricsService manages the /api HTTP endpoint.
|
||||
type MetricsService struct {
|
||||
time utility.Time
|
||||
Config *config.Config
|
||||
|
@ -32,9 +33,10 @@ type MetricsService struct {
|
|||
Storage local.Storage
|
||||
}
|
||||
|
||||
// RegisterHandler registers the handler for the various endpoints below /api.
|
||||
func (msrv *MetricsService) RegisterHandler() {
|
||||
handler := func(h func(http.ResponseWriter, *http.Request)) http.Handler {
|
||||
return http_utils.CompressionHandler{
|
||||
return httputils.CompressionHandler{
|
||||
Handler: http.HandlerFunc(h),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ import (
|
|||
"github.com/prometheus/prometheus/rules"
|
||||
"github.com/prometheus/prometheus/rules/ast"
|
||||
"github.com/prometheus/prometheus/stats"
|
||||
"github.com/prometheus/prometheus/web/http_utils"
|
||||
"github.com/prometheus/prometheus/web/httputils"
|
||||
)
|
||||
|
||||
// Enables cross-site script calls.
|
||||
|
@ -40,10 +40,11 @@ func setAccessControlHeaders(w http.ResponseWriter) {
|
|||
w.Header().Set("Access-Control-Expose-Headers", "Date")
|
||||
}
|
||||
|
||||
// Query handles the /api/query endpoint.
|
||||
func (serv MetricsService) Query(w http.ResponseWriter, r *http.Request) {
|
||||
setAccessControlHeaders(w)
|
||||
|
||||
params := http_utils.GetQueryParams(r)
|
||||
params := httputils.GetQueryParams(r)
|
||||
expr := params.Get("expr")
|
||||
asText := params.Get("asText")
|
||||
|
||||
|
@ -71,11 +72,12 @@ func (serv MetricsService) Query(w http.ResponseWriter, r *http.Request) {
|
|||
fmt.Fprint(w, result)
|
||||
}
|
||||
|
||||
// QueryRange handles the /api/query_range endpoint.
|
||||
func (serv MetricsService) QueryRange(w http.ResponseWriter, r *http.Request) {
|
||||
setAccessControlHeaders(w)
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
params := http_utils.GetQueryParams(r)
|
||||
params := httputils.GetQueryParams(r)
|
||||
expr := params.Get("expr")
|
||||
|
||||
// Input times and durations are in seconds and get converted to nanoseconds.
|
||||
|
@ -93,7 +95,7 @@ func (serv MetricsService) QueryRange(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
if exprNode.Type() != ast.VECTOR {
|
||||
fmt.Fprint(w, ast.ErrorToJSON(errors.New("Expression does not evaluate to vector type")))
|
||||
fmt.Fprint(w, ast.ErrorToJSON(errors.New("expression does not evaluate to vector type")))
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -112,7 +114,7 @@ func (serv MetricsService) QueryRange(w http.ResponseWriter, r *http.Request) {
|
|||
// For safety, limit the number of returned points per timeseries.
|
||||
// This is sufficient for 60s resolution for a week or 1h resolution for a year.
|
||||
if duration/step > 11000 {
|
||||
fmt.Fprint(w, ast.ErrorToJSON(errors.New("Exceeded maximum resolution of 11,000 points per timeseries. Try decreasing the query resolution (?step=XX).")))
|
||||
fmt.Fprint(w, ast.ErrorToJSON(errors.New("exceeded maximum resolution of 11,000 points per timeseries. Try decreasing the query resolution (?step=XX)")))
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -139,7 +141,7 @@ func (serv MetricsService) QueryRange(w http.ResponseWriter, r *http.Request) {
|
|||
sort.Sort(matrix)
|
||||
sortTimer.Stop()
|
||||
|
||||
jsonTimer := queryStats.GetTimer(stats.JsonEncodeTime).Start()
|
||||
jsonTimer := queryStats.GetTimer(stats.JSONEncodeTime).Start()
|
||||
result := ast.TypedValueToJSON(matrix, "matrix")
|
||||
jsonTimer.Stop()
|
||||
|
||||
|
@ -147,6 +149,7 @@ func (serv MetricsService) QueryRange(w http.ResponseWriter, r *http.Request) {
|
|||
fmt.Fprint(w, result)
|
||||
}
|
||||
|
||||
// Metrics handles the /api/metrics endpoint.
|
||||
func (serv MetricsService) Metrics(w http.ResponseWriter, r *http.Request) {
|
||||
setAccessControlHeaders(w)
|
||||
|
||||
|
|
|
@ -20,16 +20,19 @@ import (
|
|||
clientmodel "github.com/prometheus/client_golang/model"
|
||||
|
||||
"github.com/prometheus/prometheus/retrieval"
|
||||
"github.com/prometheus/prometheus/web/http_utils"
|
||||
"github.com/prometheus/prometheus/web/httputils"
|
||||
)
|
||||
|
||||
// TargetGroup bundles endpoints and base labels with appropriate JSON
|
||||
// annotations.
|
||||
type TargetGroup struct {
|
||||
Endpoints []string `json:"endpoints"`
|
||||
BaseLabels map[string]string `json:"baseLabels"`
|
||||
}
|
||||
|
||||
// SetTargets handles the /api/targets endpoint.
|
||||
func (serv MetricsService) SetTargets(w http.ResponseWriter, r *http.Request) {
|
||||
params := http_utils.GetQueryParams(r)
|
||||
params := httputils.GetQueryParams(r)
|
||||
jobName := params.Get("job")
|
||||
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
// Sub-directories for templates and static content.
|
||||
const (
|
||||
TemplateFiles = "templates"
|
||||
StaticFiles = "static"
|
||||
|
@ -22,10 +23,11 @@ var mimeMap = map[string]string{
|
|||
"descriptor": "application/vnd.google.protobuf;proto=google.protobuf.FileDescriptorSet",
|
||||
}
|
||||
|
||||
// GetFile retrieves the content of an embedded file.
|
||||
func GetFile(bucket string, name string) ([]byte, error) {
|
||||
blob, ok := files[bucket][name]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("Could not find %s/%s. Missing/updated files.go?", bucket, name)
|
||||
return nil, fmt.Errorf("could not find %s/%s (missing or updated files.go?)", bucket, name)
|
||||
}
|
||||
reader := bytes.NewReader(blob)
|
||||
gz, err := gzip.NewReader(reader)
|
||||
|
@ -40,6 +42,7 @@ func GetFile(bucket string, name string) ([]byte, error) {
|
|||
return b.Bytes(), nil
|
||||
}
|
||||
|
||||
// Handler implements http.Handler.
|
||||
type Handler struct{}
|
||||
|
||||
func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
|
|
|
@ -31,6 +31,7 @@ var (
|
|||
consoleLibrariesPath = flag.String("web.console.libraries", "console_libraries", "Path to the console library directory.")
|
||||
)
|
||||
|
||||
// ConsolesHandler implements http.Handler.
|
||||
type ConsolesHandler struct {
|
||||
Storage local.Storage
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package http_utils
|
||||
package httputils
|
||||
|
||||
import (
|
||||
"compress/gzip"
|
||||
|
@ -78,13 +78,13 @@ func newCompressedResponseWriter(writer http.ResponseWriter, req *http.Request)
|
|||
}
|
||||
}
|
||||
|
||||
// Wrapper around http.Handler which adds suitable response compression based
|
||||
// on the client's Accept-Encoding headers.
|
||||
// CompressionHandler is a wrapper around http.Handler which adds suitable
|
||||
// response compression based on the client's Accept-Encoding headers.
|
||||
type CompressionHandler struct {
|
||||
Handler http.Handler
|
||||
}
|
||||
|
||||
// Adds compression to the original http.Handler's ServeHTTP() method.
|
||||
// ServeHTTP adds compression to the original http.Handler's ServeHTTP() method.
|
||||
func (c CompressionHandler) ServeHTTP(writer http.ResponseWriter, req *http.Request) {
|
||||
compWriter := newCompressedResponseWriter(writer, req)
|
||||
c.Handler.ServeHTTP(compWriter, req)
|
|
@ -11,13 +11,14 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package http_utils
|
||||
package httputils
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// GetQueryParams calls r.ParseForm and returns r.Form.
|
||||
func GetQueryParams(r *http.Request) url.Values {
|
||||
r.ParseForm()
|
||||
return r.Form
|
|
@ -22,6 +22,7 @@ import (
|
|||
"github.com/prometheus/prometheus/rules/manager"
|
||||
)
|
||||
|
||||
// PrometheusStatusHandler implements http.Handler.
|
||||
type PrometheusStatusHandler struct {
|
||||
mu sync.RWMutex
|
||||
|
||||
|
|
35
web/web.go
35
web/web.go
|
@ -20,7 +20,6 @@ import (
|
|||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
_ "net/http/pprof"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
|
@ -41,6 +40,7 @@ var (
|
|||
enableQuit = flag.Bool("web.enable-remote-shutdown", false, "Enable remote service shutdown.")
|
||||
)
|
||||
|
||||
// WebService handles the HTTP endpoints with the exception of /api.
|
||||
type WebService struct {
|
||||
StatusHandler *PrometheusStatusHandler
|
||||
MetricsHandler *api.MetricsService
|
||||
|
@ -50,19 +50,20 @@ type WebService struct {
|
|||
QuitDelegate func()
|
||||
}
|
||||
|
||||
func (w WebService) ServeForever() error {
|
||||
// ServeForever serves the HTTP endpoints and only returns upon errors.
|
||||
func (ws WebService) ServeForever() error {
|
||||
http.Handle("/favicon.ico", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
http.Error(w, "", 404)
|
||||
}))
|
||||
|
||||
http.Handle("/", prometheus.InstrumentHandler(
|
||||
"/", w.StatusHandler,
|
||||
"/", ws.StatusHandler,
|
||||
))
|
||||
http.Handle("/alerts", prometheus.InstrumentHandler(
|
||||
"/alerts", w.AlertsHandler,
|
||||
"/alerts", ws.AlertsHandler,
|
||||
))
|
||||
http.Handle("/consoles/", prometheus.InstrumentHandler(
|
||||
"/consoles/", http.StripPrefix("/consoles/", w.ConsolesHandler),
|
||||
"/consoles/", http.StripPrefix("/consoles/", ws.ConsolesHandler),
|
||||
))
|
||||
http.Handle("/graph", prometheus.InstrumentHandler(
|
||||
"/graph", http.HandlerFunc(graphHandler),
|
||||
|
@ -71,7 +72,7 @@ func (w WebService) ServeForever() error {
|
|||
"/heap", http.HandlerFunc(dumpHeap),
|
||||
))
|
||||
|
||||
w.MetricsHandler.RegisterHandler()
|
||||
ws.MetricsHandler.RegisterHandler()
|
||||
http.Handle("/metrics", prometheus.Handler())
|
||||
if *useLocalAssets {
|
||||
http.Handle("/static/", prometheus.InstrumentHandler(
|
||||
|
@ -90,7 +91,7 @@ func (w WebService) ServeForever() error {
|
|||
}
|
||||
|
||||
if *enableQuit {
|
||||
http.Handle("/-/quit", http.HandlerFunc(w.quitHandler))
|
||||
http.Handle("/-/quit", http.HandlerFunc(ws.quitHandler))
|
||||
}
|
||||
|
||||
glog.Info("listening on ", *listenAddress)
|
||||
|
@ -98,7 +99,7 @@ func (w WebService) ServeForever() error {
|
|||
return http.ListenAndServe(*listenAddress, nil)
|
||||
}
|
||||
|
||||
func (s WebService) quitHandler(w http.ResponseWriter, r *http.Request) {
|
||||
func (ws WebService) quitHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != "POST" {
|
||||
w.Header().Add("Allow", "POST")
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
|
@ -107,7 +108,7 @@ func (s WebService) quitHandler(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
fmt.Fprintf(w, "Requesting termination... Goodbye!")
|
||||
|
||||
s.QuitDelegate()
|
||||
ws.QuitDelegate()
|
||||
}
|
||||
|
||||
func getTemplateFile(name string) (string, error) {
|
||||
|
@ -118,14 +119,13 @@ func getTemplateFile(name string) (string, error) {
|
|||
return "", err
|
||||
}
|
||||
return string(file), nil
|
||||
} else {
|
||||
file, err := blob.GetFile(blob.TemplateFiles, name+".html")
|
||||
if err != nil {
|
||||
glog.Errorf("Could not read %s template: %s", name, err)
|
||||
return "", err
|
||||
}
|
||||
return string(file), nil
|
||||
}
|
||||
file, err := blob.GetFile(blob.TemplateFiles, name+".html")
|
||||
if err != nil {
|
||||
glog.Errorf("Could not read %s template: %s", name, err)
|
||||
return "", err
|
||||
}
|
||||
return string(file), nil
|
||||
}
|
||||
|
||||
func getConsoles() string {
|
||||
|
@ -186,7 +186,8 @@ func dumpHeap(w http.ResponseWriter, r *http.Request) {
|
|||
fmt.Fprintf(w, "Done")
|
||||
}
|
||||
|
||||
func MustBuildServerUrl() string {
|
||||
// MustBuildServerURL returns the server URL and panics in case an error occurs.
|
||||
func MustBuildServerURL() string {
|
||||
_, port, err := net.SplitHostPort(*listenAddress)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
|
Loading…
Reference in a new issue