Finally rid of capital Sirupsen??

This commit is contained in:
Travis Reeder
2017-09-18 23:33:47 -07:00
parent f335d34636
commit 3aecebdf48
1127 changed files with 41199 additions and 41383 deletions

View File

@@ -2,6 +2,5 @@ sudo: false
language: go
go:
- 1.5.4
- 1.6.2
- 1.7.5
- tip

View File

@@ -6,7 +6,7 @@ components and libraries.
* **config**: Common configuration structures
* **expfmt**: Decoding and encoding for the exposition format
* **log**: A logging wrapper around [logrus](https://github.com/Sirupsen/logrus)
* **log**: A logging wrapper around [logrus](https://github.com/sirupsen/logrus)
* **model**: Shared data structures
* **route**: A routing wrapper around [httprouter](https://github.com/julienschmidt/httprouter) using `context.Context`
* **version**: Version informations and metric

View 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))
}

View File

@@ -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)
}

View File

@@ -0,0 +1,157 @@
// 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 config
import (
"io/ioutil"
"net/http"
"net/url"
"strings"
"testing"
yaml "gopkg.in/yaml.v2"
)
var invalidHTTPClientConfigs = []struct {
httpClientConfigFile string
errMsg string
}{
{
httpClientConfigFile: "testdata/http.conf.bearer-token-and-file-set.bad.yml",
errMsg: "at most one of bearer_token & bearer_token_file must be configured",
},
{
httpClientConfigFile: "testdata/http.conf.empty.bad.yml",
errMsg: "at most one of basic_auth, bearer_token & bearer_token_file must be configured",
},
}
func TestAuthRoundTrippers(t *testing.T) {
cfg, _, err := LoadHTTPConfigFile("testdata/http.conf.good.yml")
if err != nil {
t.Errorf("Error loading HTTP client config: %v", err)
}
tlsConfig, err := NewTLSConfig(&cfg.TLSConfig)
if err != nil {
t.Errorf("Error creating new TLS config: %v", err)
}
rt := &http.Transport{
Proxy: http.ProxyURL(cfg.ProxyURL.URL),
DisableKeepAlives: true,
TLSClientConfig: tlsConfig,
}
req := new(http.Request)
bearerAuthRoundTripper := NewBearerAuthRoundTripper("mysecret", rt)
bearerAuthRoundTripper.RoundTrip(req)
basicAuthRoundTripper := NewBasicAuthRoundTripper("username", "password", rt)
basicAuthRoundTripper.RoundTrip(req)
}
func TestHideHTTPClientConfigSecrets(t *testing.T) {
c, _, err := LoadHTTPConfigFile("testdata/http.conf.good.yml")
if err != nil {
t.Errorf("Error parsing %s: %s", "testdata/http.conf.good.yml", err)
}
// String method must not reveal authentication credentials.
s := c.String()
if strings.Contains(s, "mysecret") {
t.Fatal("http client config's String method reveals authentication credentials.")
}
}
func mustParseURL(u string) *URL {
parsed, err := url.Parse(u)
if err != nil {
panic(err)
}
return &URL{URL: parsed}
}
func TestNewClientFromConfig(t *testing.T) {
cfg, _, err := LoadHTTPConfigFile("testdata/http.conf.good.yml")
if err != nil {
t.Errorf("Error loading HTTP client config: %v", err)
}
_, err = NewHTTPClientFromConfig(cfg)
if err != nil {
t.Errorf("Error creating new client from config: %v", err)
}
}
func TestNewClientFromInvalidConfig(t *testing.T) {
cfg, _, err := LoadHTTPConfigFile("testdata/http.conf.invalid-bearer-token-file.bad.yml")
if err != nil {
t.Errorf("Error loading HTTP client config: %v", err)
}
_, err = NewHTTPClientFromConfig(cfg)
if err == nil {
t.Error("Expected error creating new client from invalid config but got none")
}
if !strings.Contains(err.Error(), "unable to read bearer token file file: open file: no such file or directory") {
t.Errorf("Expected error with config but got: %s", err.Error())
}
}
func TestValidateHTTPConfig(t *testing.T) {
cfg, _, err := LoadHTTPConfigFile("testdata/http.conf.good.yml")
if err != nil {
t.Errorf("Error loading HTTP client config: %v", err)
}
err = cfg.validate()
if err != nil {
t.Fatalf("Error validating %s: %s", "testdata/http.conf.good.yml", err)
}
}
func TestInvalidHTTPConfigs(t *testing.T) {
for _, ee := range invalidHTTPClientConfigs {
_, _, err := LoadHTTPConfigFile(ee.httpClientConfigFile)
if err == nil {
t.Error("Expected error with config but got none")
continue
}
if !strings.Contains(err.Error(), ee.errMsg) {
t.Errorf("Expected error for invalid HTTP client configuration to contain %q but got: %s", ee.errMsg, err)
}
}
}
// LoadHTTPConfig parses the YAML input s into a HTTPClientConfig.
func LoadHTTPConfig(s string) (*HTTPClientConfig, error) {
cfg := &HTTPClientConfig{}
err := yaml.Unmarshal([]byte(s), cfg)
if err != nil {
return nil, err
}
return cfg, nil
}
// LoadHTTPConfigFile parses the given YAML file into a HTTPClientConfig.
func LoadHTTPConfigFile(filename string) (*HTTPClientConfig, []byte, error) {
content, err := ioutil.ReadFile(filename)
if err != nil {
return nil, nil, err
}
cfg, err := LoadHTTPConfig(string(content))
if err != nil {
return nil, nil, err
}
return cfg, content, nil
}

View File

@@ -0,0 +1,5 @@
basic_auth:
username: username
password: "mysecret"
bearer_token: mysecret
bearer_token_file: file

View File

@@ -0,0 +1,4 @@
basic_auth:
username: username
password: mysecret
bearer_token_file: file

View File

@@ -0,0 +1,4 @@
basic_auth:
username: username
password: "mysecret"
proxy_url: "http://remote.host"

View File

@@ -0,0 +1 @@
bearer_token_file: file

View File

@@ -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
}

View File

@@ -33,7 +33,7 @@ func LoadTLSConfig(filename string) (*tls.Config, error) {
if err = yaml.Unmarshal(content, cfg); err != nil {
return nil, err
}
return cfg.GenerateConfig()
return NewTLSConfig(cfg)
}
var expectedTLSConfigs = []struct {
@@ -57,7 +57,7 @@ func TestValidTLSConfig(t *testing.T) {
t.Errorf("Error parsing %s: %s", cfg.filename, err)
}
if !reflect.DeepEqual(*got, *cfg.config) {
t.Fatalf("%s: unexpected config result: \n\n%s\n expected\n\n%s", cfg.filename, got, cfg.config)
t.Fatalf("%v: unexpected config result: \n\n%v\n expected\n\n%v", cfg.filename, got, cfg.config)
}
}
}

View File

@@ -315,6 +315,10 @@ func (p *TextParser) startLabelValue() stateFn {
if p.readTokenAsLabelValue(); p.err != nil {
return nil
}
if !model.LabelValue(p.currentToken.String()).IsValid() {
p.parseError(fmt.Sprintf("invalid label value %q", p.currentToken.String()))
return nil
}
p.currentLabelPair.Value = proto.String(p.currentToken.String())
// Special treatment of summaries:
// - Quantile labels are special, will result in dto.Quantile later.

View File

@@ -559,6 +559,11 @@ metric_bucket{le="bla"} 3.14
`,
err: "text format parsing error in line 3: expected float as value for 'le' label",
},
// 19: Invalid UTF-8 in label value.
{
in: "metric{l=\"\xbd\"} 3.14\n",
err: "text format parsing error in line 1: invalid label value \"\\xbd\"",
},
}
for i, scenario := range scenarios {

View File

@@ -21,22 +21,22 @@ import (
"golang.org/x/sys/windows/svc/eventlog"
"github.com/Sirupsen/logrus"
"github.com/sirupsen/logrus"
)
func init() {
setEventlogFormatter = func(name string, debugAsInfo bool) error {
setEventlogFormatter = func(l logger, name string, debugAsInfo bool) error {
if name == "" {
return fmt.Errorf("missing name parameter")
}
fmter, err := newEventlogger(name, debugAsInfo, origLogger.Formatter)
fmter, err := newEventlogger(name, debugAsInfo, l.entry.Logger.Formatter)
if err != nil {
fmt.Fprintf(os.Stderr, "error creating eventlog formatter: %v\n", err)
origLogger.Errorf("can't connect logger to eventlog: %v", err)
l.Errorf("can't connect logger to eventlog: %v", err)
return err
}
origLogger.Formatter = fmter
l.entry.Logger.Formatter = fmter
return nil
}
}

View File

@@ -14,7 +14,6 @@
package log
import (
"flag"
"fmt"
"io"
"io/ioutil"
@@ -25,106 +24,46 @@ import (
"strconv"
"strings"
"github.com/Sirupsen/logrus"
"github.com/sirupsen/logrus"
"gopkg.in/alecthomas/kingpin.v2"
)
type levelFlag string
// String implements flag.Value.
func (f levelFlag) String() string {
return fmt.Sprintf("%q", string(f))
}
// Set implements flag.Value.
func (f levelFlag) Set(level string) error {
l, err := logrus.ParseLevel(level)
if err != nil {
return err
}
origLogger.Level = l
return nil
}
// 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(string, bool) error
var setEventlogFormatter func(logger, string, bool) error
func setJSONFormatter() {
origLogger.Formatter = &logrus.JSONFormatter{}
}
type logFormatFlag url.URL
// String implements flag.Value.
func (f logFormatFlag) String() string {
u := url.URL(f)
return fmt.Sprintf("%q", u.String())
type loggerSettings struct {
level string
format string
}
// Set implements flag.Value.
func (f logFormatFlag) Set(format string) error {
u, err := url.Parse(format)
func (s *loggerSettings) apply(ctx *kingpin.ParseContext) error {
err := baseLogger.SetLevel(s.level)
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 "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(name, debugAsInfo)
case "stdout":
origLogger.Out = os.Stdout
case "stderr":
origLogger.Out = os.Stderr
default:
return fmt.Errorf("unsupported logger %q", u.Opaque)
}
return nil
err = baseLogger.SetFormat(s.format)
return err
}
func init() {
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"`,
)
// AddFlags adds the flags used by this package to the Kingpin application.
// To use the default Kingpin application, call AddFlags(kingpin.CommandLine)
func AddFlags(a *kingpin.Application) {
s := loggerSettings{}
kingpin.Flag("log.level", "Only log messages with the given severity or above. Valid levels: [debug, info, warn, error, fatal]").
Default(origLogger.Level.String()).
StringVar(&s.level)
defaultFormat := url.URL{Scheme: "logger", Opaque: "stderr"}
kingpin.Flag("log.format", `Set the log target and format. Example: "logger:syslog?appname=bob&local=7" or "logger:stdout?json=true"`).
Default(defaultFormat.String()).
StringVar(&s.format)
a.Action(s.apply)
}
// Logger is the interface for loggers used in the Prometheus components.
@@ -150,6 +89,9 @@ type Logger interface {
Fatalf(string, ...interface{})
With(key string, value interface{}) Logger
SetFormat(string) error
SetLevel(string) error
}
type logger struct {
@@ -235,6 +177,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 {

View File

@@ -18,7 +18,7 @@ import (
"regexp"
"testing"
"github.com/Sirupsen/logrus"
"github.com/sirupsen/logrus"
)
func TestFileLineLogging(t *testing.T) {
@@ -32,7 +32,7 @@ func TestFileLineLogging(t *testing.T) {
Debug("This debug-level line should not show up in the output.")
Infof("This %s-level line should show up in the output.", "info")
re := `^time=".*" level=info msg="This info-level line should show up in the output." source="log_test.go:33" \n$`
re := `^time=".*" level=info msg="This info-level line should show up in the output." source="log_test.go:33"\n$`
if !regexp.MustCompile(re).Match(buf.Bytes()) {
t.Fatalf("%q did not match expected regex %q", buf.String(), re)
}

View File

@@ -20,13 +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")
}
@@ -34,13 +34,13 @@ 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
}
}

View 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)

View File

@@ -0,0 +1,33 @@
// Copyright 2017 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 flag
import (
"github.com/prometheus/common/promlog"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
// LevelFlagName is the canonical flag name to configure the allowed log level
// within Prometheus projects.
const LevelFlagName = "log.level"
// LevelFlagHelp is the help description for the log.level flag.
const LevelFlagHelp = "Only log messages with the given severity or above. One of: [debug, info, warn, error]"
// AddFlags adds the flags used by this package to the Kingpin application.
// To use the default Kingpin application, call AddFlags(kingpin.CommandLine)
func AddFlags(a *kingpin.Application, logLevel *promlog.AllowedLevel) {
a.Flag(LevelFlagName, LevelFlagHelp).
Default("info").SetValue(logLevel)
}

63
vendor/github.com/prometheus/common/promlog/log.go generated vendored Normal file
View File

@@ -0,0 +1,63 @@
// Copyright 2017 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 promlog defines standardised ways to initialize Go kit loggers
// across Prometheus components.
// It should typically only ever be imported by main packages.
package promlog
import (
"os"
"github.com/go-kit/kit/log"
"github.com/go-kit/kit/log/level"
"github.com/pkg/errors"
)
// AllowedLevel is a settable identifier for the minimum level a log entry
// must be have.
type AllowedLevel struct {
s string
o level.Option
}
func (l *AllowedLevel) String() string {
return l.s
}
// Set updates the value of the allowed level.
func (l *AllowedLevel) Set(s string) error {
switch s {
case "debug":
l.o = level.AllowDebug()
case "info":
l.o = level.AllowInfo()
case "warn":
l.o = level.AllowWarn()
case "error":
l.o = level.AllowError()
default:
return errors.Errorf("unrecognized log level %q", s)
}
l.s = s
return nil
}
// New returns a new leveled oklog logger in the logfmt format. Each logged line will be annotated
// with a timestamp. The output always goes to stderr.
func New(al AllowedLevel) log.Logger {
l := log.NewLogfmtLogger(log.NewSyncWriter(os.Stderr))
l = level.NewFilter(l, al.o)
l = log.With(l, "ts", log.DefaultTimestampUTC, "caller", log.DefaultCaller)
return l
}

View File

@@ -1,26 +1,12 @@
package route
import (
"fmt"
"net/http"
"sync"
"github.com/julienschmidt/httprouter"
"golang.org/x/net/context"
)
var (
mtx = sync.RWMutex{}
ctxts = map[*http.Request]context.Context{}
)
// Context returns the context for the request.
func Context(r *http.Request) context.Context {
mtx.RLock()
defer mtx.RUnlock()
return ctxts[r]
}
type param string
// Param returns param p for the context.
@@ -33,59 +19,35 @@ func WithParam(ctx context.Context, p, v string) context.Context {
return context.WithValue(ctx, param(p), v)
}
// ContextFunc returns a new context for a request.
type ContextFunc func(r *http.Request) (context.Context, error)
// Router wraps httprouter.Router and adds support for prefixed sub-routers
// and per-request context injections.
type Router struct {
rtr *httprouter.Router
prefix string
ctxFn ContextFunc
}
// New returns a new Router.
func New(ctxFn ContextFunc) *Router {
if ctxFn == nil {
ctxFn = func(r *http.Request) (context.Context, error) {
return context.Background(), nil
}
}
func New() *Router {
return &Router{
rtr: httprouter.New(),
ctxFn: ctxFn,
rtr: httprouter.New(),
}
}
// WithPrefix returns a router that prefixes all registered routes with prefix.
func (r *Router) WithPrefix(prefix string) *Router {
return &Router{rtr: r.rtr, prefix: r.prefix + prefix, ctxFn: r.ctxFn}
return &Router{rtr: r.rtr, prefix: r.prefix + prefix}
}
// handle turns a HandlerFunc into an httprouter.Handle.
func (r *Router) handle(h http.HandlerFunc) httprouter.Handle {
return func(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
reqCtx, err := r.ctxFn(req)
if err != nil {
http.Error(w, fmt.Sprintf("Error creating request context: %v", err), http.StatusBadRequest)
return
}
ctx, cancel := context.WithCancel(reqCtx)
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
for _, p := range params {
ctx = context.WithValue(ctx, param(p.Key), p.Value)
}
mtx.Lock()
ctxts[req] = ctx
mtx.Unlock()
h(w, req)
mtx.Lock()
delete(ctxts, req)
mtx.Unlock()
h(w, req.WithContext(ctx))
}
}
@@ -132,7 +94,7 @@ func FileServe(dir string) http.HandlerFunc {
fs := http.FileServer(http.Dir(dir))
return func(w http.ResponseWriter, r *http.Request) {
r.URL.Path = Param(Context(r), "filepath")
r.URL.Path = Param(r.Context(), "filepath")
fs.ServeHTTP(w, r)
}
}

View File

@@ -1,16 +1,13 @@
package route
import (
"fmt"
"net/http"
"net/http/httptest"
"testing"
"golang.org/x/net/context"
)
func TestRedirect(t *testing.T) {
router := New(nil).WithPrefix("/test/prefix")
router := New().WithPrefix("/test/prefix")
w := httptest.NewRecorder()
r, err := http.NewRequest("GET", "http://localhost:9090/foo", nil)
if err != nil {
@@ -29,47 +26,19 @@ func TestRedirect(t *testing.T) {
}
}
func TestContextFunc(t *testing.T) {
router := New(func(r *http.Request) (context.Context, error) {
return context.WithValue(context.Background(), "testkey", "testvalue"), nil
})
router.Get("/test", func(w http.ResponseWriter, r *http.Request) {
want := "testvalue"
got := Context(r).Value("testkey")
func TestContext(t *testing.T) {
router := New()
router.Get("/test/:foo/", func(w http.ResponseWriter, r *http.Request) {
want := "bar"
got := Param(r.Context(), "foo")
if want != got {
t.Fatalf("Unexpected context value: want %q, got %q", want, got)
}
})
r, err := http.NewRequest("GET", "http://localhost:9090/test", nil)
r, err := http.NewRequest("GET", "http://localhost:9090/test/bar/", nil)
if err != nil {
t.Fatalf("Error building test request: %s", err)
}
router.ServeHTTP(nil, r)
}
func TestContextFnError(t *testing.T) {
router := New(func(r *http.Request) (context.Context, error) {
return context.Background(), fmt.Errorf("test error")
})
router.Get("/test", func(w http.ResponseWriter, r *http.Request) {})
r, err := http.NewRequest("GET", "http://localhost:9090/test", nil)
if err != nil {
t.Fatalf("Error building test request: %s", err)
}
w := httptest.NewRecorder()
router.ServeHTTP(w, r)
if w.Code != http.StatusBadRequest {
t.Fatalf("Unexpected response status: got %q, want %q", w.Code, http.StatusBadRequest)
}
want := "Error creating request context: test error\n"
got := w.Body.String()
if want != got {
t.Fatalf("Unexpected response body: got %q, want %q", got, want)
}
}