Updated vendoring for inclusion of http auth
authorconorbroderick <cjayjayb@gmail.com>
Fri, 23 Jun 2017 14:12:28 +0000 (15:12 +0100)
committerBrian Brazil <brian.brazil@robustperception.io>
Tue, 27 Jun 2017 12:40:10 +0000 (13:40 +0100)
21 files changed:
http.go
tcp.go
vendor/github.com/prometheus/common/config/config.go
vendor/github.com/prometheus/common/config/http_config.go [new file with mode: 0644]
vendor/github.com/prometheus/common/config/tls_config.go [deleted file]
vendor/github.com/prometheus/common/expfmt/decode.go
vendor/github.com/prometheus/common/expfmt/expfmt.go
vendor/github.com/prometheus/common/expfmt/fuzz.go
vendor/github.com/prometheus/common/expfmt/json_decode.go [deleted file]
vendor/github.com/prometheus/common/expfmt/text_create.go
vendor/github.com/prometheus/common/expfmt/text_parse.go
vendor/github.com/prometheus/common/log/eventlog_formatter.go [new file with mode: 0644]
vendor/github.com/prometheus/common/log/log.go
vendor/github.com/prometheus/common/log/syslog_formatter.go
vendor/github.com/prometheus/common/model/labels.go
vendor/github.com/prometheus/common/model/labelset.go
vendor/github.com/prometheus/common/model/metric.go
vendor/github.com/prometheus/common/model/model.go
vendor/github.com/prometheus/common/model/time.go
vendor/github.com/prometheus/common/model/value.go
vendor/vendor.json

diff --git a/http.go b/http.go
index d543d0f8e3af6c03d03a5a7dc71c1dfce95ed4a3..d21be60aa53f4afe0d0d6151c2a72a3bd2014e25 100644 (file)
--- a/http.go
+++ b/http.go
@@ -24,16 +24,17 @@ import (
        "strings"
 
        "github.com/prometheus/client_golang/prometheus"
+       "github.com/prometheus/common/config"
        "github.com/prometheus/common/log"
 )
 
-func matchRegularExpressions(reader io.Reader, config HTTPProbe) bool {
+func matchRegularExpressions(reader io.Reader, httpConfig HTTPProbe) bool {
        body, err := ioutil.ReadAll(reader)
        if err != nil {
                log.Errorf("Error reading HTTP body: %s", err)
                return false
        }
-       for _, expression := range config.FailIfMatchesRegexp {
+       for _, expression := range httpConfig.FailIfMatchesRegexp {
                re, err := regexp.Compile(expression)
                if err != nil {
                        log.Errorf("Could not compile expression %q as regular expression: %s", expression, err)
@@ -43,7 +44,7 @@ func matchRegularExpressions(reader io.Reader, config HTTPProbe) bool {
                        return false
                }
        }
-       for _, expression := range config.FailIfNotMatchesRegexp {
+       for _, expression := range httpConfig.FailIfNotMatchesRegexp {
                re, err := regexp.Compile(expression)
                if err != nil {
                        log.Errorf("Could not compile expression %q as regular expression: %s", expression, err)
@@ -92,7 +93,7 @@ func probeHTTP(target string, module Module, registry *prometheus.Registry) (suc
        registry.MustRegister(isSSLGauge)
        registry.MustRegister(statusCodeGauge)
 
-       config := module.HTTP
+       httpConfig := module.HTTP
 
        if !strings.HasPrefix(target, "http://") && !strings.HasPrefix(target, "https://") {
                target = "http://" + target
@@ -123,7 +124,7 @@ func probeHTTP(target string, module Module, registry *prometheus.Registry) (suc
                Timeout: module.Timeout,
        }
 
-       tlsconfig, err := module.HTTP.TLSConfig.GenerateConfig()
+       tlsconfig, err := config.NewTLSConfig(&module.HTTP.TLSConfig)
        if err != nil {
                log.Errorf("Error generating TLS config: %s", err)
                return false
@@ -140,17 +141,17 @@ func probeHTTP(target string, module Module, registry *prometheus.Registry) (suc
 
        client.CheckRedirect = func(_ *http.Request, via []*http.Request) error {
                redirects = len(via)
-               if redirects > 10 || config.NoFollowRedirects {
+               if redirects > 10 || httpConfig.NoFollowRedirects {
                        return errors.New("Don't follow redirects")
                }
                return nil
        }
 
-       if config.Method == "" {
-               config.Method = "GET"
+       if httpConfig.Method == "" {
+               httpConfig.Method = "GET"
        }
 
-       request, err := http.NewRequest(config.Method, target, nil)
+       request, err := http.NewRequest(httpConfig.Method, target, nil)
        request.Host = targetURL.Host
        if targetPort == "" {
                targetURL.Host = ip.String()
@@ -163,7 +164,7 @@ func probeHTTP(target string, module Module, registry *prometheus.Registry) (suc
                return
        }
 
-       for key, value := range config.Headers {
+       for key, value := range httpConfig.Headers {
                if strings.Title(key) == "Host" {
                        request.Host = value
                        continue
@@ -172,8 +173,8 @@ func probeHTTP(target string, module Module, registry *prometheus.Registry) (suc
        }
 
        // If a body is configured, add it to the request
-       if config.Body != "" {
-               request.Body = ioutil.NopCloser(strings.NewReader(config.Body))
+       if httpConfig.Body != "" {
+               request.Body = ioutil.NopCloser(strings.NewReader(httpConfig.Body))
        }
        resp, err := client.Do(request)
 
@@ -182,8 +183,8 @@ func probeHTTP(target string, module Module, registry *prometheus.Registry) (suc
                log.Warnf("Error for HTTP request to %s: %s", target, err)
        } else {
                defer resp.Body.Close()
-               if len(config.ValidStatusCodes) != 0 {
-                       for _, code := range config.ValidStatusCodes {
+               if len(httpConfig.ValidStatusCodes) != 0 {
+                       for _, code := range httpConfig.ValidStatusCodes {
                                if resp.StatusCode == code {
                                        success = true
                                        break
@@ -193,8 +194,8 @@ func probeHTTP(target string, module Module, registry *prometheus.Registry) (suc
                        success = true
                }
 
-               if success && (len(config.FailIfMatchesRegexp) > 0 || len(config.FailIfNotMatchesRegexp) > 0) {
-                       success = matchRegularExpressions(resp.Body, config)
+               if success && (len(httpConfig.FailIfMatchesRegexp) > 0 || len(httpConfig.FailIfNotMatchesRegexp) > 0) {
+                       success = matchRegularExpressions(resp.Body, httpConfig)
                }
        }
 
@@ -206,10 +207,10 @@ func probeHTTP(target string, module Module, registry *prometheus.Registry) (suc
                isSSLGauge.Set(float64(1))
                registry.MustRegister(probeSSLEarliestCertExpiryGauge)
                probeSSLEarliestCertExpiryGauge.Set(float64(getEarliestCertExpiry(resp.TLS).UnixNano() / 1e9))
-               if config.FailIfSSL {
+               if httpConfig.FailIfSSL {
                        success = false
                }
-       } else if config.FailIfNotSSL {
+       } else if httpConfig.FailIfNotSSL {
                success = false
        }
 
diff --git a/tcp.go b/tcp.go
index ba6cde3f8ec723ee4bc41c3764f674ba01d2d9b7..3a1ac2fdcaa65a42622e86a59c7ca83fd2c67252 100644 (file)
--- a/tcp.go
+++ b/tcp.go
@@ -22,6 +22,7 @@ import (
        "time"
 
        "github.com/prometheus/client_golang/prometheus"
+       "github.com/prometheus/common/config"
        "github.com/prometheus/common/log"
 )
 
@@ -49,11 +50,11 @@ func dialTCP(target string, module Module, registry *prometheus.Registry) (net.C
        if !module.TCP.TLS {
                return dialer.Dial(dialProtocol, dialTarget)
        }
-       config, err := module.TCP.TLSConfig.GenerateConfig()
+       tlsConfig, err := config.NewTLSConfig(&module.TCP.TLSConfig)
        if err != nil {
                return nil, err
        }
-       return tls.DialWithDialer(dialer, dialProtocol, dialTarget, config)
+       return tls.DialWithDialer(dialer, dialProtocol, dialTarget, tlsConfig)
 }
 
 func probeTCP(target string, module Module, registry *prometheus.Registry) bool {
index 33eb922ce82b41d460d7f8e382265df2a21b6b85..9195c34bfd591e486bcb34ce4adc83abc0073093 100644 (file)
@@ -28,3 +28,20 @@ func checkOverflow(m map[string]interface{}, ctx string) error {
        }
        return nil
 }
+
+// Secret special type for storing secrets.
+type Secret string
+
+// MarshalYAML implements the yaml.Marshaler interface for Secrets.
+func (s Secret) MarshalYAML() (interface{}, error) {
+       if s != "" {
+               return "<secret>", nil
+       }
+       return nil, nil
+}
+
+//UnmarshalYAML implements the yaml.Unmarshaler interface for Secrets.
+func (s *Secret) UnmarshalYAML(unmarshal func(interface{}) error) error {
+       type plain Secret
+       return unmarshal((*plain)(s))
+}
diff --git a/vendor/github.com/prometheus/common/config/http_config.go b/vendor/github.com/prometheus/common/config/http_config.go
new file mode 100644 (file)
index 0000000..ff5837f
--- /dev/null
@@ -0,0 +1,279 @@
+// Copyright 2016 The Prometheus Authors
+// 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 config
+
+import (
+       "crypto/tls"
+       "crypto/x509"
+       "fmt"
+       "io/ioutil"
+       "net/http"
+       "net/url"
+       "strings"
+
+       yaml "gopkg.in/yaml.v2"
+)
+
+// BasicAuth contains basic HTTP authentication credentials.
+type BasicAuth struct {
+       Username string `yaml:"username"`
+       Password Secret `yaml:"password"`
+
+       // Catches all undefined fields and must be empty after parsing.
+       XXX map[string]interface{} `yaml:",inline"`
+}
+
+// URL is a custom URL type that allows validation at configuration load time.
+type URL struct {
+       *url.URL
+}
+
+// UnmarshalYAML implements the yaml.Unmarshaler interface for URLs.
+func (u *URL) UnmarshalYAML(unmarshal func(interface{}) error) error {
+       var s string
+       if err := unmarshal(&s); err != nil {
+               return err
+       }
+
+       urlp, err := url.Parse(s)
+       if err != nil {
+               return err
+       }
+       u.URL = urlp
+       return nil
+}
+
+// MarshalYAML implements the yaml.Marshaler interface for URLs.
+func (u URL) MarshalYAML() (interface{}, error) {
+       if u.URL != nil {
+               return u.String(), nil
+       }
+       return nil, nil
+}
+
+// HTTPClientConfig configures an HTTP client.
+type HTTPClientConfig struct {
+       // The HTTP basic authentication credentials for the targets.
+       BasicAuth *BasicAuth `yaml:"basic_auth,omitempty"`
+       // The bearer token for the targets.
+       BearerToken Secret `yaml:"bearer_token,omitempty"`
+       // The bearer token file for the targets.
+       BearerTokenFile string `yaml:"bearer_token_file,omitempty"`
+       // HTTP proxy server to use to connect to the targets.
+       ProxyURL URL `yaml:"proxy_url,omitempty"`
+       // TLSConfig to use to connect to the targets.
+       TLSConfig TLSConfig `yaml:"tls_config,omitempty"`
+
+       // Catches all undefined fields and must be empty after parsing.
+       XXX map[string]interface{} `yaml:",inline"`
+}
+
+func (c *HTTPClientConfig) validate() error {
+       if len(c.BearerToken) > 0 && len(c.BearerTokenFile) > 0 {
+               return fmt.Errorf("at most one of bearer_token & bearer_token_file must be configured")
+       }
+       if c.BasicAuth != nil && (len(c.BearerToken) > 0 || len(c.BearerTokenFile) > 0) {
+               return fmt.Errorf("at most one of basic_auth, bearer_token & bearer_token_file must be configured")
+       }
+       return nil
+}
+
+// UnmarshalYAML implements the yaml.Unmarshaler interface
+func (c *HTTPClientConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
+       type plain HTTPClientConfig
+       err := unmarshal((*plain)(c))
+       if err != nil {
+               return err
+       }
+       err = c.validate()
+       if err != nil {
+               return c.validate()
+       }
+       return checkOverflow(c.XXX, "http_client_config")
+}
+
+// UnmarshalYAML implements the yaml.Unmarshaler interface.
+func (a *BasicAuth) UnmarshalYAML(unmarshal func(interface{}) error) error {
+       type plain BasicAuth
+       err := unmarshal((*plain)(a))
+       if err != nil {
+               return err
+       }
+       return checkOverflow(a.XXX, "basic_auth")
+}
+
+// NewHTTPClientFromConfig returns a new HTTP client configured for the
+// given config.HTTPClientConfig.
+func NewHTTPClientFromConfig(cfg *HTTPClientConfig) (*http.Client, error) {
+       tlsConfig, err := NewTLSConfig(&cfg.TLSConfig)
+       if err != nil {
+               return nil, err
+       }
+
+       // It's the caller's job to handle timeouts
+       var rt http.RoundTripper = &http.Transport{
+               Proxy:             http.ProxyURL(cfg.ProxyURL.URL),
+               DisableKeepAlives: true,
+               TLSClientConfig:   tlsConfig,
+       }
+
+       // If a bearer token is provided, create a round tripper that will set the
+       // Authorization header correctly on each request.
+       bearerToken := cfg.BearerToken
+       if len(bearerToken) == 0 && len(cfg.BearerTokenFile) > 0 {
+               b, err := ioutil.ReadFile(cfg.BearerTokenFile)
+               if err != nil {
+                       return nil, fmt.Errorf("unable to read bearer token file %s: %s", cfg.BearerTokenFile, err)
+               }
+               bearerToken = Secret(strings.TrimSpace(string(b)))
+       }
+
+       if len(bearerToken) > 0 {
+               rt = NewBearerAuthRoundTripper(bearerToken, rt)
+       }
+
+       if cfg.BasicAuth != nil {
+               rt = NewBasicAuthRoundTripper(cfg.BasicAuth.Username, Secret(cfg.BasicAuth.Password), rt)
+       }
+
+       // Return a new client with the configured round tripper.
+       return &http.Client{Transport: rt}, nil
+}
+
+type bearerAuthRoundTripper struct {
+       bearerToken Secret
+       rt          http.RoundTripper
+}
+
+type basicAuthRoundTripper struct {
+       username string
+       password Secret
+       rt       http.RoundTripper
+}
+
+// NewBasicAuthRoundTripper will apply a BASIC auth authorization header to a request unless it has
+// already been set.
+func NewBasicAuthRoundTripper(username string, password Secret, rt http.RoundTripper) http.RoundTripper {
+       return &basicAuthRoundTripper{username, password, rt}
+}
+
+func (rt *bearerAuthRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
+       if len(req.Header.Get("Authorization")) == 0 {
+               req = cloneRequest(req)
+               req.Header.Set("Authorization", "Bearer "+string(rt.bearerToken))
+       }
+
+       return rt.rt.RoundTrip(req)
+}
+
+// NewBearerAuthRoundTripper adds the provided bearer token to a request unless the authorization
+// header has already been set.
+func NewBearerAuthRoundTripper(bearer Secret, rt http.RoundTripper) http.RoundTripper {
+       return &bearerAuthRoundTripper{bearer, rt}
+}
+
+func (rt *basicAuthRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
+       if len(req.Header.Get("Authorization")) != 0 {
+               return rt.RoundTrip(req)
+       }
+       req = cloneRequest(req)
+       req.SetBasicAuth(rt.username, string(rt.password))
+       return rt.rt.RoundTrip(req)
+}
+
+// cloneRequest returns a clone of the provided *http.Request.
+// The clone is a shallow copy of the struct and its Header map.
+func cloneRequest(r *http.Request) *http.Request {
+       // Shallow copy of the struct.
+       r2 := new(http.Request)
+       *r2 = *r
+       // Deep copy of the Header.
+       r2.Header = make(http.Header)
+       for k, s := range r.Header {
+               r2.Header[k] = s
+       }
+       return r2
+}
+
+// NewTLSConfig creates a new tls.Config from the given config.TLSConfig.
+func NewTLSConfig(cfg *TLSConfig) (*tls.Config, error) {
+       tlsConfig := &tls.Config{InsecureSkipVerify: cfg.InsecureSkipVerify}
+
+       // If a CA cert is provided then let's read it in so we can validate the
+       // scrape target's certificate properly.
+       if len(cfg.CAFile) > 0 {
+               caCertPool := x509.NewCertPool()
+               // Load CA cert.
+               caCert, err := ioutil.ReadFile(cfg.CAFile)
+               if err != nil {
+                       return nil, fmt.Errorf("unable to use specified CA cert %s: %s", cfg.CAFile, err)
+               }
+               caCertPool.AppendCertsFromPEM(caCert)
+               tlsConfig.RootCAs = caCertPool
+       }
+
+       if len(cfg.ServerName) > 0 {
+               tlsConfig.ServerName = cfg.ServerName
+       }
+
+       // If a client cert & key is provided then configure TLS config accordingly.
+       if len(cfg.CertFile) > 0 && len(cfg.KeyFile) == 0 {
+               return nil, fmt.Errorf("client cert file %q specified without client key file", cfg.CertFile)
+       } else if len(cfg.KeyFile) > 0 && len(cfg.CertFile) == 0 {
+               return nil, fmt.Errorf("client key file %q specified without client cert file", cfg.KeyFile)
+       } else if len(cfg.CertFile) > 0 && len(cfg.KeyFile) > 0 {
+               cert, err := tls.LoadX509KeyPair(cfg.CertFile, cfg.KeyFile)
+               if err != nil {
+                       return nil, fmt.Errorf("unable to use specified client cert (%s) & key (%s): %s", cfg.CertFile, cfg.KeyFile, err)
+               }
+               tlsConfig.Certificates = []tls.Certificate{cert}
+       }
+       tlsConfig.BuildNameToCertificate()
+
+       return tlsConfig, nil
+}
+
+// TLSConfig configures the options for TLS connections.
+type TLSConfig struct {
+       // The CA cert to use for the targets.
+       CAFile string `yaml:"ca_file,omitempty"`
+       // The client cert file for the targets.
+       CertFile string `yaml:"cert_file,omitempty"`
+       // The client key file for the targets.
+       KeyFile string `yaml:"key_file,omitempty"`
+       // Used to verify the hostname for the targets.
+       ServerName string `yaml:"server_name,omitempty"`
+       // Disable target certificate validation.
+       InsecureSkipVerify bool `yaml:"insecure_skip_verify"`
+
+       // Catches all undefined fields and must be empty after parsing.
+       XXX map[string]interface{} `yaml:",inline"`
+}
+
+// UnmarshalYAML implements the yaml.Unmarshaler interface.
+func (c *TLSConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
+       type plain TLSConfig
+       if err := unmarshal((*plain)(c)); err != nil {
+               return err
+       }
+       return checkOverflow(c.XXX, "TLS config")
+}
+
+func (c HTTPClientConfig) String() string {
+       b, err := yaml.Marshal(c)
+       if err != nil {
+               return fmt.Sprintf("<error creating http client config string: %s>", err)
+       }
+       return string(b)
+}
diff --git a/vendor/github.com/prometheus/common/config/tls_config.go b/vendor/github.com/prometheus/common/config/tls_config.go
deleted file mode 100644 (file)
index 7c7e7cb..0000000
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright 2016 The Prometheus Authors
-// 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 config
-
-import (
-       "crypto/tls"
-       "crypto/x509"
-       "fmt"
-       "io/ioutil"
-)
-
-// TLSConfig configures the options for TLS connections.
-type TLSConfig struct {
-       // The CA cert to use for the targets.
-       CAFile string `yaml:"ca_file,omitempty"`
-       // The client cert file for the targets.
-       CertFile string `yaml:"cert_file,omitempty"`
-       // The client key file for the targets.
-       KeyFile string `yaml:"key_file,omitempty"`
-       // Disable target certificate validation.
-       InsecureSkipVerify bool `yaml:"insecure_skip_verify"`
-
-       // Catches all undefined fields and must be empty after parsing.
-       XXX map[string]interface{} `yaml:",inline"`
-}
-
-// UnmarshalYAML implements the yaml.Unmarshaler interface.
-func (c *TLSConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
-       type plain TLSConfig
-       if err := unmarshal((*plain)(c)); err != nil {
-               return err
-       }
-       return checkOverflow(c.XXX, "TLS config")
-}
-
-// GenerateConfig produces a tls.Config based on TLS connection options.
-// It loads certificate files from disk if they are defined.
-func (c *TLSConfig) GenerateConfig() (*tls.Config, error) {
-       tlsConfig := &tls.Config{InsecureSkipVerify: c.InsecureSkipVerify}
-
-       // If a CA cert is provided then let's read it in so we can validate the
-       // scrape target's certificate properly.
-       if len(c.CAFile) > 0 {
-               caCertPool := x509.NewCertPool()
-               // Load CA cert.
-               caCert, err := ioutil.ReadFile(c.CAFile)
-               if err != nil {
-                       return nil, fmt.Errorf("unable to use specified CA cert %s: %s", c.CAFile, err)
-               }
-               caCertPool.AppendCertsFromPEM(caCert)
-               tlsConfig.RootCAs = caCertPool
-       }
-
-       if len(c.CertFile) > 0 && len(c.KeyFile) == 0 {
-               return nil, fmt.Errorf("client cert file %q specified without client key file", c.CertFile)
-       } else if len(c.KeyFile) > 0 && len(c.CertFile) == 0 {
-               return nil, fmt.Errorf("client key file %q specified without client cert file", c.KeyFile)
-       } else if len(c.CertFile) > 0 && len(c.KeyFile) > 0 {
-               cert, err := tls.LoadX509KeyPair(c.CertFile, c.KeyFile)
-               if err != nil {
-                       return nil, fmt.Errorf("unable to use specified client cert (%s) & key (%s): %s", c.CertFile, c.KeyFile, err)
-               }
-               tlsConfig.Certificates = []tls.Certificate{cert}
-       }
-       tlsConfig.BuildNameToCertificate()
-
-       return tlsConfig, nil
-}
index a98696dfc304fac2b5673d59a323e83a43001f73..a7a42d5ef4130ae516dadea69e633942b148a3e6 100644 (file)
@@ -31,6 +31,7 @@ type Decoder interface {
        Decode(*dto.MetricFamily) error
 }
 
+// DecodeOptions contains options used by the Decoder and in sample extraction.
 type DecodeOptions struct {
        // Timestamp is added to each value from the stream that has no explicit timestamp set.
        Timestamp model.Time
@@ -46,10 +47,7 @@ func ResponseFormat(h http.Header) Format {
                return FmtUnknown
        }
 
-       const (
-               textType = "text/plain"
-               jsonType = "application/json"
-       )
+       const textType = "text/plain"
 
        switch mediatype {
        case ProtoType:
@@ -66,22 +64,6 @@ func ResponseFormat(h http.Header) Format {
                        return FmtUnknown
                }
                return FmtText
-
-       case jsonType:
-               var prometheusAPIVersion string
-
-               if params["schema"] == "prometheus/telemetry" && params["version"] != "" {
-                       prometheusAPIVersion = params["version"]
-               } else {
-                       prometheusAPIVersion = h.Get("X-Prometheus-API-Version")
-               }
-
-               switch prometheusAPIVersion {
-               case "0.0.2", "":
-                       return fmtJSON2
-               default:
-                       return FmtUnknown
-               }
        }
 
        return FmtUnknown
@@ -93,8 +75,6 @@ func NewDecoder(r io.Reader, format Format) Decoder {
        switch format {
        case FmtProtoDelim:
                return &protoDecoder{r: r}
-       case fmtJSON2:
-               return newJSON2Decoder(r)
        }
        return &textDecoder{r: r}
 }
@@ -132,7 +112,7 @@ func (d *protoDecoder) Decode(v *dto.MetricFamily) error {
        return nil
 }
 
-// textDecoder implements the Decoder interface for the text protcol.
+// textDecoder implements the Decoder interface for the text protocol.
 type textDecoder struct {
        r    io.Reader
        p    TextParser
@@ -163,6 +143,8 @@ func (d *textDecoder) Decode(v *dto.MetricFamily) error {
        return nil
 }
 
+// SampleDecoder wraps a Decoder to extract samples from the metric families
+// decoded by the wrapped Decoder.
 type SampleDecoder struct {
        Dec  Decoder
        Opts *DecodeOptions
@@ -170,37 +152,51 @@ type SampleDecoder struct {
        f dto.MetricFamily
 }
 
+// Decode calls the Decode method of the wrapped Decoder and then extracts the
+// samples from the decoded MetricFamily into the provided model.Vector.
 func (sd *SampleDecoder) Decode(s *model.Vector) error {
-       if err := sd.Dec.Decode(&sd.f); err != nil {
+       err := sd.Dec.Decode(&sd.f)
+       if err != nil {
                return err
        }
-       *s = extractSamples(&sd.f, sd.Opts)
-       return nil
+       *s, err = extractSamples(&sd.f, sd.Opts)
+       return err
 }
 
-// Extract samples builds a slice of samples from the provided metric families.
-func ExtractSamples(o *DecodeOptions, fams ...*dto.MetricFamily) model.Vector {
-       var all model.Vector
+// ExtractSamples builds a slice of samples from the provided metric
+// families. If an error occurs during sample extraction, it continues to
+// extract from the remaining metric families. The returned error is the last
+// error that has occured.
+func ExtractSamples(o *DecodeOptions, fams ...*dto.MetricFamily) (model.Vector, error) {
+       var (
+               all     model.Vector
+               lastErr error
+       )
        for _, f := range fams {
-               all = append(all, extractSamples(f, o)...)
+               some, err := extractSamples(f, o)
+               if err != nil {
+                       lastErr = err
+                       continue
+               }
+               all = append(all, some...)
        }
-       return all
+       return all, lastErr
 }
 
-func extractSamples(f *dto.MetricFamily, o *DecodeOptions) model.Vector {
+func extractSamples(f *dto.MetricFamily, o *DecodeOptions) (model.Vector, error) {
        switch f.GetType() {
        case dto.MetricType_COUNTER:
-               return extractCounter(o, f)
+               return extractCounter(o, f), nil
        case dto.MetricType_GAUGE:
-               return extractGauge(o, f)
+               return extractGauge(o, f), nil
        case dto.MetricType_SUMMARY:
-               return extractSummary(o, f)
+               return extractSummary(o, f), nil
        case dto.MetricType_UNTYPED:
-               return extractUntyped(o, f)
+               return extractUntyped(o, f), nil
        case dto.MetricType_HISTOGRAM:
-               return extractHistogram(o, f)
+               return extractHistogram(o, f), nil
        }
-       panic("expfmt.extractSamples: unknown metric family type")
+       return nil, fmt.Errorf("expfmt.extractSamples: unknown metric family type %v", f.GetType())
 }
 
 func extractCounter(o *DecodeOptions, f *dto.MetricFamily) model.Vector {
index 366fbde98a154461cdf958950e35153c3cf6d27d..371ac75037bccbe64b25a88ae4eedc9c78a08bc8 100644 (file)
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// A package for reading and writing Prometheus metrics.
+// Package expfmt contains tools for reading and writing Prometheus metrics.
 package expfmt
 
+// Format specifies the HTTP content type of the different wire protocols.
 type Format string
 
+// Constants to assemble the Content-Type values for the different wire protocols.
 const (
-       TextVersion = "0.0.4"
-
+       TextVersion   = "0.0.4"
        ProtoType     = `application/vnd.google.protobuf`
        ProtoProtocol = `io.prometheus.client.MetricFamily`
        ProtoFmt      = ProtoType + "; proto=" + ProtoProtocol + ";"
@@ -29,9 +30,6 @@ const (
        FmtProtoDelim   Format = ProtoFmt + ` encoding=delimited`
        FmtProtoText    Format = ProtoFmt + ` encoding=text`
        FmtProtoCompact Format = ProtoFmt + ` encoding=compact-text`
-
-       // fmtJSON2 is hidden as it is deprecated.
-       fmtJSON2 Format = `application/json; version=0.0.2`
 )
 
 const (
index 14f920146921a8113ae210c5fe927281342a8605..dc2eedeefcac4ce1e383ec529c1043e6f0c6bad2 100644 (file)
@@ -20,8 +20,8 @@ import "bytes"
 
 // Fuzz text metric parser with with github.com/dvyukov/go-fuzz:
 //
-//     go-fuzz-build github.com/prometheus/client_golang/text
-//     go-fuzz -bin text-fuzz.zip -workdir fuzz
+//     go-fuzz-build github.com/prometheus/common/expfmt
+//     go-fuzz -bin expfmt-fuzz.zip -workdir fuzz
 //
 // Further input samples should go in the folder fuzz/corpus.
 func Fuzz(in []byte) int {
diff --git a/vendor/github.com/prometheus/common/expfmt/json_decode.go b/vendor/github.com/prometheus/common/expfmt/json_decode.go
deleted file mode 100644 (file)
index cf86545..0000000
+++ /dev/null
@@ -1,174 +0,0 @@
-// Copyright 2015 The Prometheus Authors
-// 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 expfmt
-
-import (
-       "encoding/json"
-       "fmt"
-       "io"
-       "sort"
-
-       "github.com/golang/protobuf/proto"
-       dto "github.com/prometheus/client_model/go"
-
-       "github.com/prometheus/common/model"
-)
-
-type json2Decoder struct {
-       dec  *json.Decoder
-       fams []*dto.MetricFamily
-}
-
-func newJSON2Decoder(r io.Reader) Decoder {
-       return &json2Decoder{
-               dec: json.NewDecoder(r),
-       }
-}
-
-type histogram002 struct {
-       Labels model.LabelSet     `json:"labels"`
-       Values map[string]float64 `json:"value"`
-}
-
-type counter002 struct {
-       Labels model.LabelSet `json:"labels"`
-       Value  float64        `json:"value"`
-}
-
-func protoLabelSet(base, ext model.LabelSet) ([]*dto.LabelPair, error) {
-       labels := base.Clone().Merge(ext)
-       delete(labels, model.MetricNameLabel)
-
-       names := make([]string, 0, len(labels))
-       for ln := range labels {
-               names = append(names, string(ln))
-       }
-       sort.Strings(names)
-
-       pairs := make([]*dto.LabelPair, 0, len(labels))
-
-       for _, ln := range names {
-               if !model.LabelNameRE.MatchString(ln) {
-                       return nil, fmt.Errorf("invalid label name %q", ln)
-               }
-               lv := labels[model.LabelName(ln)]
-
-               pairs = append(pairs, &dto.LabelPair{
-                       Name:  proto.String(ln),
-                       Value: proto.String(string(lv)),
-               })
-       }
-
-       return pairs, nil
-}
-
-func (d *json2Decoder) more() error {
-       var entities []struct {
-               BaseLabels model.LabelSet `json:"baseLabels"`
-               Docstring  string         `json:"docstring"`
-               Metric     struct {
-                       Type   string          `json:"type"`
-                       Values json.RawMessage `json:"value"`
-               } `json:"metric"`
-       }
-
-       if err := d.dec.Decode(&entities); err != nil {
-               return err
-       }
-       for _, e := range entities {
-               f := &dto.MetricFamily{
-                       Name:   proto.String(string(e.BaseLabels[model.MetricNameLabel])),
-                       Help:   proto.String(e.Docstring),
-                       Type:   dto.MetricType_UNTYPED.Enum(),
-                       Metric: []*dto.Metric{},
-               }
-
-               d.fams = append(d.fams, f)
-
-               switch e.Metric.Type {
-               case "counter", "gauge":
-                       var values []counter002
-
-                       if err := json.Unmarshal(e.Metric.Values, &values); err != nil {
-                               return fmt.Errorf("could not extract %s value: %s", e.Metric.Type, err)
-                       }
-
-                       for _, ctr := range values {
-                               labels, err := protoLabelSet(e.BaseLabels, ctr.Labels)
-                               if err != nil {
-                                       return err
-                               }
-                               f.Metric = append(f.Metric, &dto.Metric{
-                                       Label: labels,
-                                       Untyped: &dto.Untyped{
-                                               Value: proto.Float64(ctr.Value),
-                                       },
-                               })
-                       }
-
-               case "histogram":
-                       var values []histogram002
-
-                       if err := json.Unmarshal(e.Metric.Values, &values); err != nil {
-                               return fmt.Errorf("could not extract %s value: %s", e.Metric.Type, err)
-                       }
-
-                       for _, hist := range values {
-                               quants := make([]string, 0, len(values))
-                               for q := range hist.Values {
-                                       quants = append(quants, q)
-                               }
-
-                               sort.Strings(quants)
-
-                               for _, q := range quants {
-                                       value := hist.Values[q]
-                                       // The correct label is "quantile" but to not break old expressions
-                                       // this remains "percentile"
-                                       hist.Labels["percentile"] = model.LabelValue(q)
-
-                                       labels, err := protoLabelSet(e.BaseLabels, hist.Labels)
-                                       if err != nil {
-                                               return err
-                                       }
-
-                                       f.Metric = append(f.Metric, &dto.Metric{
-                                               Label: labels,
-                                               Untyped: &dto.Untyped{
-                                                       Value: proto.Float64(value),
-                                               },
-                                       })
-                               }
-                       }
-
-               default:
-                       return fmt.Errorf("unknown metric type %q", e.Metric.Type)
-               }
-       }
-       return nil
-}
-
-// Decode implements the Decoder interface.
-func (d *json2Decoder) Decode(v *dto.MetricFamily) error {
-       if len(d.fams) == 0 {
-               if err := d.more(); err != nil {
-                       return err
-               }
-       }
-
-       *v = *d.fams[0]
-       d.fams = d.fams[1:]
-
-       return nil
-}
index 0bb9c14cc2b672b88f7f53de7c0653f8f68091f6..f11321cd0c726939ac1297d445bec4535572c0b8 100644 (file)
@@ -14,7 +14,6 @@
 package expfmt
 
 import (
-       "bytes"
        "fmt"
        "io"
        "math"
@@ -26,9 +25,12 @@ import (
 
 // MetricFamilyToText converts a MetricFamily proto message into text format and
 // writes the resulting lines to 'out'. It returns the number of bytes written
-// and any error encountered.  This function does not perform checks on the
-// content of the metric and label names, i.e. invalid metric or label names
+// and any error encountered. The output will have the same order as the input,
+// no further sorting is performed. Furthermore, this function assumes the input
+// is already sanitized and does not perform any sanity checks. If the input
+// contains duplicate metrics or invalid metric or label names, the conversion
 // will result in invalid text format output.
+//
 // This method fulfills the type 'prometheus.encoder'.
 func MetricFamilyToText(out io.Writer, in *dto.MetricFamily) (int, error) {
        var written int
@@ -285,21 +287,17 @@ func labelPairsToText(
        return written, nil
 }
 
+var (
+       escape                = strings.NewReplacer("\\", `\\`, "\n", `\n`)
+       escapeWithDoubleQuote = strings.NewReplacer("\\", `\\`, "\n", `\n`, "\"", `\"`)
+)
+
 // escapeString replaces '\' by '\\', new line character by '\n', and - if
 // includeDoubleQuote is true - '"' by '\"'.
 func escapeString(v string, includeDoubleQuote bool) string {
-       result := bytes.NewBuffer(make([]byte, 0, len(v)))
-       for _, c := range v {
-               switch {
-               case c == '\\':
-                       result.WriteString(`\\`)
-               case includeDoubleQuote && c == '"':
-                       result.WriteString(`\"`)
-               case c == '\n':
-                       result.WriteString(`\n`)
-               default:
-                       result.WriteRune(c)
-               }
+       if includeDoubleQuote {
+               return escapeWithDoubleQuote.Replace(v)
        }
-       return result.String()
+
+       return escape.Replace(v)
 }
index bd170b1675f897770af7aa622df29f94f90c8634..ef9a1507710f8f2d2644fb6b0836f374399ec2d3 100644 (file)
@@ -47,7 +47,7 @@ func (e ParseError) Error() string {
 }
 
 // TextParser is used to parse the simple and flat text-based exchange format. Its
-// nil value is ready to use.
+// zero value is ready to use.
 type TextParser struct {
        metricFamiliesByName map[string]*dto.MetricFamily
        buf                  *bufio.Reader // Where the parsed input is read through.
diff --git a/vendor/github.com/prometheus/common/log/eventlog_formatter.go b/vendor/github.com/prometheus/common/log/eventlog_formatter.go
new file mode 100644 (file)
index 0000000..bcf68e6
--- /dev/null
@@ -0,0 +1,89 @@
+// Copyright 2015 The Prometheus Authors
+// 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.
+
+// +build windows
+
+package log
+
+import (
+       "fmt"
+       "os"
+
+       "golang.org/x/sys/windows/svc/eventlog"
+
+       "github.com/sirupsen/logrus"
+)
+
+func init() {
+       setEventlogFormatter = func(l logger, name string, debugAsInfo bool) error {
+               if name == "" {
+                       return fmt.Errorf("missing name parameter")
+               }
+
+               fmter, err := newEventlogger(name, debugAsInfo, l.entry.Logger.Formatter)
+               if err != nil {
+                       fmt.Fprintf(os.Stderr, "error creating eventlog formatter: %v\n", err)
+                       l.Errorf("can't connect logger to eventlog: %v", err)
+                       return err
+               }
+               l.entry.Logger.Formatter = fmter
+               return nil
+       }
+}
+
+type eventlogger struct {
+       log         *eventlog.Log
+       debugAsInfo bool
+       wrap        logrus.Formatter
+}
+
+func newEventlogger(name string, debugAsInfo bool, fmter logrus.Formatter) (*eventlogger, error) {
+       logHandle, err := eventlog.Open(name)
+       if err != nil {
+               return nil, err
+       }
+       return &eventlogger{log: logHandle, debugAsInfo: debugAsInfo, wrap: fmter}, nil
+}
+
+func (s *eventlogger) Format(e *logrus.Entry) ([]byte, error) {
+       data, err := s.wrap.Format(e)
+       if err != nil {
+               fmt.Fprintf(os.Stderr, "eventlogger: can't format entry: %v\n", err)
+               return data, err
+       }
+
+       switch e.Level {
+       case logrus.PanicLevel:
+               fallthrough
+       case logrus.FatalLevel:
+               fallthrough
+       case logrus.ErrorLevel:
+               err = s.log.Error(102, e.Message)
+       case logrus.WarnLevel:
+               err = s.log.Warning(101, e.Message)
+       case logrus.InfoLevel:
+               err = s.log.Info(100, e.Message)
+       case logrus.DebugLevel:
+               if s.debugAsInfo {
+                       err = s.log.Info(100, e.Message)
+               }
+       default:
+               err = s.log.Info(100, e.Message)
+       }
+
+       if err != nil {
+               fmt.Fprintf(os.Stderr, "eventlogger: can't send log to eventlog: %v\n", err)
+       }
+
+       return data, err
+}
index 07fbfef61545ad65b9ebf996609b413a1a8153de..1321741ad3e0fa462780637a6884d4c5acd27dfe 100644 (file)
@@ -16,19 +16,23 @@ package log
 import (
        "flag"
        "fmt"
+       "io"
+       "io/ioutil"
+       "log"
        "net/url"
        "os"
        "runtime"
+       "strconv"
        "strings"
 
-       "github.com/Sirupsen/logrus"
+       "github.com/sirupsen/logrus"
 )
 
-type levelFlag struct{}
+type levelFlag string
 
 // String implements flag.Value.
 func (f levelFlag) String() string {
-       return origLogger.Level.String()
+       return fmt.Sprintf("%q", origLogger.Level.String())
 }
 
 // Set implements flag.Value.
@@ -42,60 +46,50 @@ func (f levelFlag) Set(level string) error {
 }
 
 // setSyslogFormatter is nil if the target architecture does not support syslog.
-var setSyslogFormatter func(string, string) error
+var setSyslogFormatter func(logger, string, string) error
+
+// setEventlogFormatter is nil if the target OS does not support Eventlog (i.e., is not Windows).
+var setEventlogFormatter func(logger, string, bool) error
 
 func setJSONFormatter() {
        origLogger.Formatter = &logrus.JSONFormatter{}
 }
 
-type logFormatFlag struct{ uri string }
+type logFormatFlag url.URL
 
 // String implements flag.Value.
 func (f logFormatFlag) String() string {
-       return f.uri
+       u := url.URL(f)
+       return fmt.Sprintf("%q", u.String())
 }
 
 // Set implements flag.Value.
 func (f logFormatFlag) Set(format string) error {
-       f.uri = format
-       u, err := url.Parse(format)
-       if err != nil {
-               return err
-       }
-       if u.Scheme != "logger" {
-               return fmt.Errorf("invalid scheme %s", u.Scheme)
-       }
-       jsonq := u.Query().Get("json")
-       if jsonq == "true" {
-               setJSONFormatter()
-       }
-
-       switch u.Opaque {
-       case "syslog":
-               if setSyslogFormatter == nil {
-                       return fmt.Errorf("system does not support syslog")
-               }
-               appname := u.Query().Get("appname")
-               facility := u.Query().Get("local")
-               return setSyslogFormatter(appname, facility)
-       case "stdout":
-               origLogger.Out = os.Stdout
-       case "stderr":
-               origLogger.Out = os.Stderr
-
-       default:
-               return fmt.Errorf("unsupported logger %s", u.Opaque)
-       }
-       return nil
+       return baseLogger.SetFormat(format)
 }
 
 func init() {
-       // In order for these flags to take effect, the user of the package must call
-       // flag.Parse() before logging anything.
-       flag.Var(levelFlag{}, "log.level", "Only log messages with the given severity or above. Valid levels: [debug, info, warn, error, fatal].")
-       flag.Var(logFormatFlag{}, "log.format", "If set use a syslog logger or JSON logging. Example: logger:syslog?appname=bob&local=7 or logger:stdout?json=true. Defaults to stderr.")
-}
-
+       AddFlags(flag.CommandLine)
+}
+
+// AddFlags adds the flags used by this package to the given FlagSet. That's
+// useful if working with a custom FlagSet. The init function of this package
+// adds the flags to flag.CommandLine anyway. Thus, it's usually enough to call
+// flag.Parse() to make the logging flags take effect.
+func AddFlags(fs *flag.FlagSet) {
+       fs.Var(
+               levelFlag(origLogger.Level.String()),
+               "log.level",
+               "Only log messages with the given severity or above. Valid levels: [debug, info, warn, error, fatal]",
+       )
+       fs.Var(
+               logFormatFlag(url.URL{Scheme: "logger", Opaque: "stderr"}),
+               "log.format",
+               `Set the log target and format. Example: "logger:syslog?appname=bob&local=7" or "logger:stdout?json=true"`,
+       )
+}
+
+// Logger is the interface for loggers used in the Prometheus components.
 type Logger interface {
        Debug(...interface{})
        Debugln(...interface{})
@@ -118,6 +112,9 @@ type Logger interface {
        Fatalf(string, ...interface{})
 
        With(key string, value interface{}) Logger
+
+       SetFormat(string) error
+       SetLevel(string) error
 }
 
 type logger struct {
@@ -203,6 +200,58 @@ func (l logger) Fatalf(format string, args ...interface{}) {
        l.sourced().Fatalf(format, args...)
 }
 
+func (l logger) SetLevel(level string) error {
+       lvl, err := logrus.ParseLevel(level)
+       if err != nil {
+               return err
+       }
+
+       l.entry.Logger.Level = lvl
+       return nil
+}
+
+func (l logger) SetFormat(format string) error {
+       u, err := url.Parse(format)
+       if err != nil {
+               return err
+       }
+       if u.Scheme != "logger" {
+               return fmt.Errorf("invalid scheme %s", u.Scheme)
+       }
+       jsonq := u.Query().Get("json")
+       if jsonq == "true" {
+               setJSONFormatter()
+       }
+
+       switch u.Opaque {
+       case "syslog":
+               if setSyslogFormatter == nil {
+                       return fmt.Errorf("system does not support syslog")
+               }
+               appname := u.Query().Get("appname")
+               facility := u.Query().Get("local")
+               return setSyslogFormatter(l, appname, facility)
+       case "eventlog":
+               if setEventlogFormatter == nil {
+                       return fmt.Errorf("system does not support eventlog")
+               }
+               name := u.Query().Get("name")
+               debugAsInfo := false
+               debugAsInfoRaw := u.Query().Get("debugAsInfo")
+               if parsedDebugAsInfo, err := strconv.ParseBool(debugAsInfoRaw); err == nil {
+                       debugAsInfo = parsedDebugAsInfo
+               }
+               return setEventlogFormatter(l, name, debugAsInfo)
+       case "stdout":
+               l.entry.Logger.Out = os.Stdout
+       case "stderr":
+               l.entry.Logger.Out = os.Stderr
+       default:
+               return fmt.Errorf("unsupported logger %q", u.Opaque)
+       }
+       return nil
+}
+
 // sourced adds a source field to the logger that contains
 // the file name and line where the logging happened.
 func (l logger) sourced() *logrus.Entry {
@@ -220,10 +269,26 @@ func (l logger) sourced() *logrus.Entry {
 var origLogger = logrus.New()
 var baseLogger = logger{entry: logrus.NewEntry(origLogger)}
 
+// Base returns the default Logger logging to
 func Base() Logger {
        return baseLogger
 }
 
+// NewLogger returns a new Logger logging to out.
+func NewLogger(w io.Writer) Logger {
+       l := logrus.New()
+       l.Out = w
+       return logger{entry: logrus.NewEntry(l)}
+}
+
+// NewNopLogger returns a logger that discards all log messages.
+func NewNopLogger() Logger {
+       l := logrus.New()
+       l.Out = ioutil.Discard
+       return logger{entry: logrus.NewEntry(l)}
+}
+
+// With adds a field to the logger.
 func With(key string, value interface{}) Logger {
        return baseLogger.With(key, value)
 }
@@ -233,7 +298,7 @@ func Debug(args ...interface{}) {
        baseLogger.sourced().Debug(args...)
 }
 
-// Debug logs a message at level Debug on the standard logger.
+// Debugln logs a message at level Debug on the standard logger.
 func Debugln(args ...interface{}) {
        baseLogger.sourced().Debugln(args...)
 }
@@ -248,7 +313,7 @@ func Info(args ...interface{}) {
        baseLogger.sourced().Info(args...)
 }
 
-// Info logs a message at level Info on the standard logger.
+// Infoln logs a message at level Info on the standard logger.
 func Infoln(args ...interface{}) {
        baseLogger.sourced().Infoln(args...)
 }
@@ -263,7 +328,7 @@ func Warn(args ...interface{}) {
        baseLogger.sourced().Warn(args...)
 }
 
-// Warn logs a message at level Warn on the standard logger.
+// Warnln logs a message at level Warn on the standard logger.
 func Warnln(args ...interface{}) {
        baseLogger.sourced().Warnln(args...)
 }
@@ -278,7 +343,7 @@ func Error(args ...interface{}) {
        baseLogger.sourced().Error(args...)
 }
 
-// Error logs a message at level Error on the standard logger.
+// Errorln logs a message at level Error on the standard logger.
 func Errorln(args ...interface{}) {
        baseLogger.sourced().Errorln(args...)
 }
@@ -293,7 +358,7 @@ func Fatal(args ...interface{}) {
        baseLogger.sourced().Fatal(args...)
 }
 
-// Fatal logs a message at level Fatal on the standard logger.
+// Fatalln logs a message at level Fatal on the standard logger.
 func Fatalln(args ...interface{}) {
        baseLogger.sourced().Fatalln(args...)
 }
@@ -302,3 +367,16 @@ func Fatalln(args ...interface{}) {
 func Fatalf(format string, args ...interface{}) {
        baseLogger.sourced().Fatalf(format, args...)
 }
+
+type errorLogWriter struct{}
+
+func (errorLogWriter) Write(b []byte) (int, error) {
+       baseLogger.sourced().Error(string(b))
+       return len(b), nil
+}
+
+// NewErrorLogger returns a log.Logger that is meant to be used
+// in the ErrorLog field of an http.Server to log HTTP server errors.
+func NewErrorLogger() *log.Logger {
+       return log.New(&errorLogWriter{}, "", 0)
+}
index fd8c6fbeeb3b90e833e7e6d8c12cdbf1194f583a..f882f2f8485e929f21065a48a3958ab8adabbc74 100644 (file)
@@ -20,11 +20,13 @@ import (
        "log/syslog"
        "os"
 
-       "github.com/Sirupsen/logrus"
+       "github.com/sirupsen/logrus"
 )
 
+var _ logrus.Formatter = (*syslogger)(nil)
+
 func init() {
-       setSyslogFormatter = func(appname, local string) error {
+       setSyslogFormatter = func(l logger, appname, local string) error {
                if appname == "" {
                        return fmt.Errorf("missing appname parameter")
                }
@@ -32,18 +34,18 @@ func init() {
                        return fmt.Errorf("missing local parameter")
                }
 
-               fmter, err := newSyslogger(appname, local, origLogger.Formatter)
+               fmter, err := newSyslogger(appname, local, l.entry.Logger.Formatter)
                if err != nil {
                        fmt.Fprintf(os.Stderr, "error creating syslog formatter: %v\n", err)
-                       origLogger.Errorf("can't connect logger to syslog: %v", err)
+                       l.entry.Errorf("can't connect logger to syslog: %v", err)
                        return err
                }
-               origLogger.Formatter = fmter
+               l.entry.Logger.Formatter = fmter
                return nil
        }
 }
 
-var ceeTag = []byte("@cee:")
+var prefixTag []byte
 
 type syslogger struct {
        wrap logrus.Formatter
@@ -56,6 +58,11 @@ func newSyslogger(appname string, facility string, fmter logrus.Formatter) (*sys
                return nil, err
        }
        out, err := syslog.New(priority, appname)
+       _, isJSON := fmter.(*logrus.JSONFormatter)
+       if isJSON {
+               // add cee tag to json formatted syslogs
+               prefixTag = []byte("@cee:")
+       }
        return &syslogger{
                out:  out,
                wrap: fmter,
@@ -92,7 +99,7 @@ func (s *syslogger) Format(e *logrus.Entry) ([]byte, error) {
        }
        // only append tag to data sent to syslog (line), not to what
        // is returned
-       line := string(append(ceeTag, data...))
+       line := string(append(prefixTag, data...))
 
        switch e.Level {
        case logrus.PanicLevel:
index 3b72e7ff8f6cf751e84beec863e4f47dd86d9c91..41051a01a36d49c5b85be61dcdaadb6030f6bf98 100644 (file)
@@ -80,14 +80,18 @@ const (
        QuantileLabel = "quantile"
 )
 
-// LabelNameRE is a regular expression matching valid label names.
+// LabelNameRE is a regular expression matching valid label names. Note that the
+// IsValid method of LabelName performs the same check but faster than a match
+// with this regular expression.
 var LabelNameRE = regexp.MustCompile("^[a-zA-Z_][a-zA-Z0-9_]*$")
 
 // A LabelName is a key for a LabelSet or Metric.  It has a value associated
 // therewith.
 type LabelName string
 
-// IsValid is true iff the label name matches the pattern of LabelNameRE.
+// IsValid is true iff the label name matches the pattern of LabelNameRE. This
+// method, however, does not use LabelNameRE for the check but a much faster
+// hardcoded implementation.
 func (ln LabelName) IsValid() bool {
        if len(ln) == 0 {
                return false
@@ -106,7 +110,7 @@ func (ln *LabelName) UnmarshalYAML(unmarshal func(interface{}) error) error {
        if err := unmarshal(&s); err != nil {
                return err
        }
-       if !LabelNameRE.MatchString(s) {
+       if !LabelName(s).IsValid() {
                return fmt.Errorf("%q is not a valid label name", s)
        }
        *ln = LabelName(s)
@@ -119,7 +123,7 @@ func (ln *LabelName) UnmarshalJSON(b []byte) error {
        if err := json.Unmarshal(b, &s); err != nil {
                return err
        }
-       if !LabelNameRE.MatchString(s) {
+       if !LabelName(s).IsValid() {
                return fmt.Errorf("%q is not a valid label name", s)
        }
        *ln = LabelName(s)
index 5f931cdb9b3ab63415a7b7ed60bb7765f48d46ac..6eda08a7395a1b96c9d762f816343ee069feae56 100644 (file)
@@ -160,7 +160,7 @@ func (l *LabelSet) UnmarshalJSON(b []byte) error {
        // LabelName as a string and does not call its UnmarshalJSON method.
        // Thus, we have to replicate the behavior here.
        for ln := range m {
-               if !LabelNameRE.MatchString(string(ln)) {
+               if !ln.IsValid() {
                        return fmt.Errorf("%q is not a valid label name", ln)
                }
        }
index a5da59a5055e519d3c588edebe4e3b502af5aab2..f7250909b9fd3e2dd78740ad9180a0c4980ab6a1 100644 (file)
@@ -21,8 +21,11 @@ import (
 )
 
 var (
-       separator    = []byte{0}
-       MetricNameRE = regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_:]*$`)
+       separator = []byte{0}
+       // MetricNameRE is a regular expression matching valid metric
+       // names. Note that the IsValidMetricName function performs the same
+       // check but faster than a match with this regular expression.
+       MetricNameRE = regexp.MustCompile(`^[a-zA-Z_:][a-zA-Z0-9_:]*$`)
 )
 
 // A Metric is similar to a LabelSet, but the key difference is that a Metric is
@@ -41,7 +44,7 @@ func (m Metric) Before(o Metric) bool {
 
 // Clone returns a copy of the Metric.
 func (m Metric) Clone() Metric {
-       clone := Metric{}
+       clone := make(Metric, len(m))
        for k, v := range m {
                clone[k] = v
        }
@@ -85,6 +88,8 @@ func (m Metric) FastFingerprint() Fingerprint {
 }
 
 // IsValidMetricName returns true iff name matches the pattern of MetricNameRE.
+// This function, however, does not use MetricNameRE for the check but a much
+// faster hardcoded implementation.
 func IsValidMetricName(n LabelValue) bool {
        if len(n) == 0 {
                return false
index 88f013a47a41b6a3abb23a894d3670630a778116..a7b9691707e83b9302f16aa56dbbb6ff3c5f7a25 100644 (file)
@@ -12,5 +12,5 @@
 // limitations under the License.
 
 // Package model contains common data structures that are shared across
-// Prometheus componenets and libraries.
+// Prometheus components and libraries.
 package model
index 548968aebe6d48ad1a523da64ce776399121d829..7e87f1ac63439820f9f76d34e54baec286e3583e 100644 (file)
@@ -163,9 +163,21 @@ func (t *Time) UnmarshalJSON(b []byte) error {
 // This type should not propagate beyond the scope of input/output processing.
 type Duration time.Duration
 
+// Set implements pflag/flag.Value
+func (d *Duration) Set(s string) error {
+       var err error
+       *d, err = ParseDuration(s)
+       return err
+}
+
+// Type implements pflag.Value
+func (d *Duration) Type() string {
+       return "duration"
+}
+
 var durationRE = regexp.MustCompile("^([0-9]+)(y|w|d|h|m|s|ms)$")
 
-// StringToDuration parses a string into a time.Duration, assuming that a year
+// ParseDuration parses a string into a time.Duration, assuming that a year
 // always has 365d, a week always has 7d, and a day always has 24h.
 func ParseDuration(durationStr string) (Duration, error) {
        matches := durationRE.FindStringSubmatch(durationStr)
index 10ffb0bd61147b56a79403973245fb3cf1d686b9..c9ed3ffd82aeafb5a37fc6f3321278dc789fc858 100644 (file)
@@ -16,11 +16,28 @@ package model
 import (
        "encoding/json"
        "fmt"
+       "math"
        "sort"
        "strconv"
        "strings"
 )
 
+var (
+       // ZeroSamplePair is the pseudo zero-value of SamplePair used to signal a
+       // non-existing sample pair. It is a SamplePair with timestamp Earliest and
+       // value 0.0. Note that the natural zero value of SamplePair has a timestamp
+       // of 0, which is possible to appear in a real SamplePair and thus not
+       // suitable to signal a non-existing SamplePair.
+       ZeroSamplePair = SamplePair{Timestamp: Earliest}
+
+       // ZeroSample is the pseudo zero-value of Sample used to signal a
+       // non-existing sample. It is a Sample with timestamp Earliest, value 0.0,
+       // and metric nil. Note that the natural zero value of Sample has a timestamp
+       // of 0, which is possible to appear in a real Sample and thus not suitable
+       // to signal a non-existing Sample.
+       ZeroSample = Sample{Timestamp: Earliest}
+)
+
 // A SampleValue is a representation of a value for a given sample at a given
 // time.
 type SampleValue float64
@@ -43,8 +60,14 @@ func (v *SampleValue) UnmarshalJSON(b []byte) error {
        return nil
 }
 
+// Equal returns true if the value of v and o is equal or if both are NaN. Note
+// that v==o is false if both are NaN. If you want the conventional float
+// behavior, use == to compare two SampleValues.
 func (v SampleValue) Equal(o SampleValue) bool {
-       return v == o
+       if v == o {
+               return true
+       }
+       return math.IsNaN(float64(v)) && math.IsNaN(float64(o))
 }
 
 func (v SampleValue) String() string {
@@ -77,9 +100,9 @@ func (s *SamplePair) UnmarshalJSON(b []byte) error {
 }
 
 // Equal returns true if this SamplePair and o have equal Values and equal
-// Timestamps.
+// Timestamps. The sematics of Value equality is defined by SampleValue.Equal.
 func (s *SamplePair) Equal(o *SamplePair) bool {
-       return s == o || (s.Value == o.Value && s.Timestamp.Equal(o.Timestamp))
+       return s == o || (s.Value.Equal(o.Value) && s.Timestamp.Equal(o.Timestamp))
 }
 
 func (s SamplePair) String() string {
@@ -93,7 +116,8 @@ type Sample struct {
        Timestamp Time        `json:"timestamp"`
 }
 
-// Equal compares first the metrics, then the timestamp, then the value.
+// Equal compares first the metrics, then the timestamp, then the value. The
+// sematics of value equality is defined by SampleValue.Equal.
 func (s *Sample) Equal(o *Sample) bool {
        if s == o {
                return true
@@ -105,11 +129,8 @@ func (s *Sample) Equal(o *Sample) bool {
        if !s.Timestamp.Equal(o.Timestamp) {
                return false
        }
-       if s.Value != o.Value {
-               return false
-       }
 
-       return true
+       return s.Value.Equal(o.Value)
 }
 
 func (s Sample) String() string {
index 2b5b51a6ec180faf57e8f6d329656700bf56df3f..b88d4de95fe08dc73d667f158271ca96adff8fb4 100644 (file)
                        "revisionTime": "2015-02-12T10:17:44Z"
                },
                {
-                       "checksumSHA1": "lxe3NRr1SukdE8Mf+xNGcTUR0NI=",
+                       "checksumSHA1": "4TLgSCgJZuS5gtytxNvcVk4h8/g=",
                        "path": "github.com/prometheus/common/config",
-                       "revision": "167b27da48d058a9b46d84b834d67f68f0243f67",
-                       "revisionTime": "2016-03-18T12:23:18Z"
+                       "revision": "0866df4b85a18d652b6965be022d007cdf076822",
+                       "revisionTime": "2017-06-21T12:10:01Z"
                },
                {
-                       "checksumSHA1": "Zsc9IQzQDkOzqiAITqxEm0JXdws=",
+                       "checksumSHA1": "Wtpzndm/+bdwwNU5PCTfb4oUhc8=",
                        "path": "github.com/prometheus/common/expfmt",
-                       "revision": "167b27da48d058a9b46d84b834d67f68f0243f67",
-                       "revisionTime": "2016-03-18T12:23:18Z"
+                       "revision": "0866df4b85a18d652b6965be022d007cdf076822",
+                       "revisionTime": "2017-06-21T12:10:01Z"
                },
                {
                        "checksumSHA1": "GWlM3d2vPYyNATtTFgftS10/A9w=",
                        "path": "github.com/prometheus/common/internal/bitbucket.org/ww/goautoneg",
-                       "revision": "167b27da48d058a9b46d84b834d67f68f0243f67",
-                       "revisionTime": "2016-03-18T12:23:18Z"
+                       "revision": "0866df4b85a18d652b6965be022d007cdf076822",
+                       "revisionTime": "2017-06-21T12:10:01Z"
                },
                {
-                       "checksumSHA1": "koBNYQryxAG8hyHBlpn8pcnSVdM=",
+                       "checksumSHA1": "esljoY35OOHttvpjBctA6WGZc70=",
                        "path": "github.com/prometheus/common/log",
-                       "revision": "dd586c1c5abb0be59e60f942c22af711a2008cb4",
-                       "revisionTime": "2016-05-03T22:05:32Z"
+                       "revision": "0866df4b85a18d652b6965be022d007cdf076822",
+                       "revisionTime": "2017-06-21T12:10:01Z"
                },
                {
-                       "checksumSHA1": "IwxsYOL9A/K9rvHvGxfyY37dusk=",
+                       "checksumSHA1": "3VoqH7TFfzA6Ds0zFzIbKCUvBmw=",
                        "path": "github.com/prometheus/common/model",
-                       "revision": "167b27da48d058a9b46d84b834d67f68f0243f67",
-                       "revisionTime": "2016-03-18T12:23:18Z"
+                       "revision": "0866df4b85a18d652b6965be022d007cdf076822",
+                       "revisionTime": "2017-06-21T12:10:01Z"
                },
                {
                        "checksumSHA1": "91KYK0SpvkaMJJA2+BcxbVnyRO0=",
                        "path": "github.com/prometheus/common/version",
-                       "revision": "dd586c1c5abb0be59e60f942c22af711a2008cb4",
-                       "revisionTime": "2016-05-03T22:05:32Z"
+                       "revision": "0866df4b85a18d652b6965be022d007cdf076822",
+                       "revisionTime": "2017-06-21T12:10:01Z"
                },
                {
                        "checksumSHA1": "kO2MAzPuortudABaRd7fY+7EaoM=",