Merge pull request #292 from influxdata/feature/tracking-simple
Add server reportingpull/10616/head
commit
7ee9a10d30
1
Godeps
1
Godeps
|
@ -6,6 +6,7 @@ github.com/elazarl/go-bindata-assetfs 9a6736ed45b44bf3835afeebb3034b57ed329f3e
|
||||||
github.com/gogo/protobuf 6abcf94fd4c97dcb423fdafd42fe9f96ca7e421b
|
github.com/gogo/protobuf 6abcf94fd4c97dcb423fdafd42fe9f96ca7e421b
|
||||||
github.com/google/go-github 1bc362c7737e51014af7299e016444b654095ad9
|
github.com/google/go-github 1bc362c7737e51014af7299e016444b654095ad9
|
||||||
github.com/google/go-querystring 9235644dd9e52eeae6fa48efd539fdc351a0af53
|
github.com/google/go-querystring 9235644dd9e52eeae6fa48efd539fdc351a0af53
|
||||||
|
github.com/influxdata/usage-client 6d3895376368aa52a3a81d2a16e90f0f52371967
|
||||||
github.com/jessevdk/go-flags 4cc2832a6e6d1d3b815e2b9d544b2a4dfb3ce8fa
|
github.com/jessevdk/go-flags 4cc2832a6e6d1d3b815e2b9d544b2a4dfb3ce8fa
|
||||||
github.com/satori/go.uuid b061729afc07e77a8aa4fad0a2fd840958f1942a
|
github.com/satori/go.uuid b061729afc07e77a8aa4fad0a2fd840958f1942a
|
||||||
github.com/tylerb/graceful 50a48b6e73fcc75b45e22c05b79629a67c79e938
|
github.com/tylerb/graceful 50a48b6e73fcc75b45e22c05b79629a67c79e938
|
||||||
|
|
4
Makefile
4
Makefile
|
@ -1,11 +1,9 @@
|
||||||
VERSION ?= $$(git describe --always --tags)
|
VERSION ?= $$(git describe --always --tags)
|
||||||
COMMIT ?= $$(git rev-parse --short=8 HEAD)
|
COMMIT ?= $$(git rev-parse --short=8 HEAD)
|
||||||
BRANCH ?= $$(git rev-parse --abbrev-ref HEAD | tr / _)
|
|
||||||
BUILD_TIME ?= $$(date +%FT%T%z)
|
|
||||||
|
|
||||||
SOURCES := $(shell find . -name '*.go')
|
SOURCES := $(shell find . -name '*.go')
|
||||||
|
|
||||||
LDFLAGS=-ldflags "-s -X main.Version=${VERSION} -X main.Commit=${COMMIT} -X main.BuildTime=${BUILD_TIME} -X main.Branch=${BRANCH}"
|
LDFLAGS=-ldflags "-s -X main.Version=${VERSION} -X main.Commit=${COMMIT}"
|
||||||
BINARY=chronograf
|
BINARY=chronograf
|
||||||
|
|
||||||
default: dep build
|
default: dep build
|
||||||
|
|
|
@ -17,7 +17,7 @@ import (
|
||||||
|
|
||||||
const AppExt = ".json"
|
const AppExt = ".json"
|
||||||
|
|
||||||
var logger = clog.New()
|
var logger = clog.New(clog.DebugLevel)
|
||||||
|
|
||||||
// Apps are canned JSON layouts. Implements LayoutStore.
|
// Apps are canned JSON layouts. Implements LayoutStore.
|
||||||
type Apps struct {
|
type Apps struct {
|
||||||
|
|
|
@ -27,11 +27,12 @@ func (e Error) Error() string {
|
||||||
// provides methods to trigger log messages at various alert levels and a
|
// provides methods to trigger log messages at various alert levels and a
|
||||||
// WithField method to set keys for a structured log message.
|
// WithField method to set keys for a structured log message.
|
||||||
type Logger interface {
|
type Logger interface {
|
||||||
|
Debug(...interface{})
|
||||||
Info(...interface{})
|
Info(...interface{})
|
||||||
Warn(...interface{})
|
Warn(...interface{})
|
||||||
Debug(...interface{})
|
|
||||||
Panic(...interface{})
|
|
||||||
Error(...interface{})
|
Error(...interface{})
|
||||||
|
Fatal(...interface{})
|
||||||
|
Panic(...interface{})
|
||||||
|
|
||||||
WithField(string, interface{}) Logger
|
WithField(string, interface{}) Logger
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,12 +12,15 @@ import (
|
||||||
var (
|
var (
|
||||||
Version = ""
|
Version = ""
|
||||||
Commit = ""
|
Commit = ""
|
||||||
BuildTime = ""
|
|
||||||
Branch = ""
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
srv := server.Server{}
|
srv := server.Server{
|
||||||
|
BuildInfo: server.BuildInfo{
|
||||||
|
Version: Version,
|
||||||
|
Commit: Commit,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
parser := flags.NewParser(&srv, flags.Default)
|
parser := flags.NewParser(&srv, flags.Default)
|
||||||
parser.ShortDescription = `Chronograf`
|
parser.ShortDescription = `Chronograf`
|
||||||
|
|
|
@ -26,7 +26,7 @@ func Test_Influx_MakesRequestsToQueryEndpoint(t *testing.T) {
|
||||||
defer ts.Close()
|
defer ts.Close()
|
||||||
|
|
||||||
var series chronograf.TimeSeries
|
var series chronograf.TimeSeries
|
||||||
series, err := influx.NewClient(ts.URL, log.New())
|
series, err := influx.NewClient(ts.URL, log.New(log.DebugLevel))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("Unexpected error initializing client: err:", err)
|
t.Fatal("Unexpected error initializing client: err:", err)
|
||||||
}
|
}
|
||||||
|
@ -59,7 +59,7 @@ func Test_Influx_CancelsInFlightRequests(t *testing.T) {
|
||||||
ts.Close()
|
ts.Close()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
series, _ := influx.NewClient(ts.URL, log.New())
|
series, _ := influx.NewClient(ts.URL, log.New(log.DebugLevel))
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
errs := make(chan (error))
|
errs := make(chan (error))
|
||||||
|
@ -102,7 +102,7 @@ func Test_Influx_CancelsInFlightRequests(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_Influx_RejectsInvalidHosts(t *testing.T) {
|
func Test_Influx_RejectsInvalidHosts(t *testing.T) {
|
||||||
_, err := influx.NewClient(":", log.New())
|
_, err := influx.NewClient(":", log.New(log.DebugLevel))
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("Expected err but was nil")
|
t.Fatal("Expected err but was nil")
|
||||||
}
|
}
|
||||||
|
@ -114,7 +114,7 @@ func Test_Influx_ReportsInfluxErrs(t *testing.T) {
|
||||||
}))
|
}))
|
||||||
defer ts.Close()
|
defer ts.Close()
|
||||||
|
|
||||||
cl, err := influx.NewClient(ts.URL, log.New())
|
cl, err := influx.NewClient(ts.URL, log.New(log.DebugLevel))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("Encountered unexpected error while initializing influx client: err:", err)
|
t.Fatal("Encountered unexpected error while initializing influx client: err:", err)
|
||||||
}
|
}
|
||||||
|
|
61
log/log.go
61
log/log.go
|
@ -7,11 +7,56 @@ import (
|
||||||
"github.com/influxdata/chronograf"
|
"github.com/influxdata/chronograf"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Level type
|
||||||
|
type Level uint8
|
||||||
|
|
||||||
|
// These are the different logging levels.
|
||||||
|
const (
|
||||||
|
// PanicLevel level, highest level of severity. Logs and then calls panic with the
|
||||||
|
// message passed to Debug, Info, ...
|
||||||
|
PanicLevel Level = iota
|
||||||
|
// FatalLevel level. Logs and then calls `os.Exit(1)`. It will exit even if the
|
||||||
|
// logging level is set to Panic.
|
||||||
|
FatalLevel
|
||||||
|
// ErrorLevel level. Logs. Used for errors that should definitely be noted.
|
||||||
|
// Commonly used for hooks to send errors to an error tracking service.
|
||||||
|
ErrorLevel
|
||||||
|
// WarnLevel level. Non-critical entries that deserve eyes.
|
||||||
|
WarnLevel
|
||||||
|
// InfoLevel level. General operational entries about what's going on inside the
|
||||||
|
// application.
|
||||||
|
InfoLevel
|
||||||
|
// DebugLevel level. Usually only enabled when debugging. Very verbose logging.
|
||||||
|
DebugLevel
|
||||||
|
)
|
||||||
|
|
||||||
|
// ParseLevel takes a string level and returns the Logrus log level constant.
|
||||||
|
func ParseLevel(lvl string) Level {
|
||||||
|
switch lvl {
|
||||||
|
case "panic":
|
||||||
|
return PanicLevel
|
||||||
|
case "fatal":
|
||||||
|
return FatalLevel
|
||||||
|
case "error":
|
||||||
|
return ErrorLevel
|
||||||
|
case "warn":
|
||||||
|
return WarnLevel
|
||||||
|
case "info":
|
||||||
|
return InfoLevel
|
||||||
|
default:
|
||||||
|
return DebugLevel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// LogrusLogger is a chronograf.Logger that uses logrus to process logs
|
// LogrusLogger is a chronograf.Logger that uses logrus to process logs
|
||||||
type logrusLogger struct {
|
type logrusLogger struct {
|
||||||
l *logrus.Entry
|
l *logrus.Entry
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ll *logrusLogger) Debug(items ...interface{}) {
|
||||||
|
ll.l.Debug(items...)
|
||||||
|
}
|
||||||
|
|
||||||
func (ll *logrusLogger) Info(items ...interface{}) {
|
func (ll *logrusLogger) Info(items ...interface{}) {
|
||||||
ll.l.Info(items...)
|
ll.l.Info(items...)
|
||||||
}
|
}
|
||||||
|
@ -20,28 +65,28 @@ func (ll *logrusLogger) Warn(items ...interface{}) {
|
||||||
ll.l.Warn(items...)
|
ll.l.Warn(items...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ll *logrusLogger) Debug(items ...interface{}) {
|
func (ll *logrusLogger) Error(items ...interface{}) {
|
||||||
ll.l.Debug(items...)
|
ll.l.Error(items...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ll *logrusLogger) Fatal(items ...interface{}) {
|
||||||
|
ll.l.Fatal(items...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ll *logrusLogger) Panic(items ...interface{}) {
|
func (ll *logrusLogger) Panic(items ...interface{}) {
|
||||||
ll.l.Panic(items...)
|
ll.l.Panic(items...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ll *logrusLogger) Error(items ...interface{}) {
|
|
||||||
ll.l.Error(items...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ll *logrusLogger) WithField(key string, value interface{}) chronograf.Logger {
|
func (ll *logrusLogger) WithField(key string, value interface{}) chronograf.Logger {
|
||||||
return &logrusLogger{ll.l.WithField(key, value)}
|
return &logrusLogger{ll.l.WithField(key, value)}
|
||||||
}
|
}
|
||||||
|
|
||||||
func New() chronograf.Logger {
|
func New(l Level) chronograf.Logger {
|
||||||
logger := &logrus.Logger{
|
logger := &logrus.Logger{
|
||||||
Out: os.Stderr,
|
Out: os.Stderr,
|
||||||
Formatter: new(logrus.TextFormatter),
|
Formatter: new(logrus.TextFormatter),
|
||||||
Hooks: make(logrus.LevelHooks),
|
Hooks: make(logrus.LevelHooks),
|
||||||
Level: logrus.DebugLevel,
|
Level: logrus.Level(l),
|
||||||
}
|
}
|
||||||
|
|
||||||
return &logrusLogger{
|
return &logrusLogger{
|
||||||
|
|
|
@ -9,8 +9,8 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/influxdata/chronograf"
|
"github.com/influxdata/chronograf"
|
||||||
"github.com/influxdata/chronograf/server"
|
|
||||||
clog "github.com/influxdata/chronograf/log"
|
clog "github.com/influxdata/chronograf/log"
|
||||||
|
"github.com/influxdata/chronograf/server"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCookieExtractor(t *testing.T) {
|
func TestCookieExtractor(t *testing.T) {
|
||||||
|
@ -180,7 +180,7 @@ func TestAuthorizedToken(t *testing.T) {
|
||||||
Principal: test.Principal,
|
Principal: test.Principal,
|
||||||
}
|
}
|
||||||
|
|
||||||
logger := clog.New()
|
logger := clog.New(clog.DebugLevel)
|
||||||
handler := server.AuthorizedToken(a, e, logger, next)
|
handler := server.AuthorizedToken(a, e, logger, next)
|
||||||
handler.ServeHTTP(w, req)
|
handler.ServeHTTP(w, req)
|
||||||
if w.Code != test.Code {
|
if w.Code != test.Code {
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -13,12 +15,11 @@ import (
|
||||||
"github.com/influxdata/chronograf/layouts"
|
"github.com/influxdata/chronograf/layouts"
|
||||||
clog "github.com/influxdata/chronograf/log"
|
clog "github.com/influxdata/chronograf/log"
|
||||||
"github.com/influxdata/chronograf/uuid"
|
"github.com/influxdata/chronograf/uuid"
|
||||||
|
client "github.com/influxdata/usage-client/v1"
|
||||||
flags "github.com/jessevdk/go-flags"
|
flags "github.com/jessevdk/go-flags"
|
||||||
"github.com/tylerb/graceful"
|
"github.com/tylerb/graceful"
|
||||||
)
|
)
|
||||||
|
|
||||||
var logger = clog.New()
|
|
||||||
|
|
||||||
// Server for the chronograf API
|
// Server for the chronograf API
|
||||||
type Server struct {
|
type Server struct {
|
||||||
Host string `long:"host" description:"the IP to listen on" default:"localhost" env:"HOST"`
|
Host string `long:"host" description:"the IP to listen on" default:"localhost" env:"HOST"`
|
||||||
|
@ -35,18 +36,28 @@ type Server struct {
|
||||||
TokenSecret string `short:"t" long:"token-secret" description:"Secret to sign tokens" env:"TOKEN_SECRET"`
|
TokenSecret string `short:"t" long:"token-secret" description:"Secret to sign tokens" env:"TOKEN_SECRET"`
|
||||||
GithubClientID string `short:"i" long:"github-client-id" description:"Github Client ID for OAuth 2 support" env:"GH_CLIENT_ID"`
|
GithubClientID string `short:"i" long:"github-client-id" description:"Github Client ID for OAuth 2 support" env:"GH_CLIENT_ID"`
|
||||||
GithubClientSecret string `short:"s" long:"github-client-secret" description:"Github Client Secret for OAuth 2 support" env:"GH_CLIENT_SECRET"`
|
GithubClientSecret string `short:"s" long:"github-client-secret" description:"Github Client Secret for OAuth 2 support" env:"GH_CLIENT_SECRET"`
|
||||||
|
ReportingDisabled bool `short:"r" long:"reporting-disabled" description:"Disable reporting of usage stats (os,arch,version,cluster_id) once every 24hr" env:"REPORTING_DISABLED"`
|
||||||
|
LogLevel string `short:"l" long:"log-level" value-name:"choice" choice:"debug" choice:"info" choice:"warn" choice:"error" choice:"fatal" choice:"panic" default:"info" description:"Set the logging level" env:"LOG_LEVEL"`
|
||||||
|
|
||||||
|
BuildInfo BuildInfo
|
||||||
|
|
||||||
Listener net.Listener
|
Listener net.Listener
|
||||||
handler http.Handler
|
handler http.Handler
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type BuildInfo struct {
|
||||||
|
Version string
|
||||||
|
Commit string
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Server) useAuth() bool {
|
func (s *Server) useAuth() bool {
|
||||||
return s.TokenSecret != "" && s.GithubClientID != "" && s.GithubClientSecret != ""
|
return s.TokenSecret != "" && s.GithubClientID != "" && s.GithubClientSecret != ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serve starts and runs the chronograf server
|
// Serve starts and runs the chronograf server
|
||||||
func (s *Server) Serve() error {
|
func (s *Server) Serve() error {
|
||||||
service := openService(s.BoltPath, s.CannedPath)
|
logger := clog.New(clog.ParseLevel(s.LogLevel))
|
||||||
|
service := openService(s.BoltPath, s.CannedPath, logger)
|
||||||
s.handler = NewMux(MuxOpts{
|
s.handler = NewMux(MuxOpts{
|
||||||
Develop: s.Develop,
|
Develop: s.Develop,
|
||||||
TokenSecret: s.TokenSecret,
|
TokenSecret: s.TokenSecret,
|
||||||
|
@ -70,9 +81,13 @@ func (s *Server) Serve() error {
|
||||||
httpServer.TCPKeepAlive = 1 * time.Minute
|
httpServer.TCPKeepAlive = 1 * time.Minute
|
||||||
httpServer.Handler = s.handler
|
httpServer.Handler = s.handler
|
||||||
|
|
||||||
|
if !s.ReportingDisabled {
|
||||||
|
go reportUsageStats(s.BuildInfo, logger)
|
||||||
|
}
|
||||||
|
|
||||||
logger.
|
logger.
|
||||||
WithField("component", "server").
|
WithField("component", "server").
|
||||||
Info("Serving chronograf at http://%s", s.Listener.Addr())
|
Info("Serving chronograf at http://", s.Listener.Addr())
|
||||||
|
|
||||||
if err := httpServer.Serve(s.Listener); err != nil {
|
if err := httpServer.Serve(s.Listener); err != nil {
|
||||||
logger.
|
logger.
|
||||||
|
@ -83,12 +98,12 @@ func (s *Server) Serve() error {
|
||||||
|
|
||||||
logger.
|
logger.
|
||||||
WithField("component", "server").
|
WithField("component", "server").
|
||||||
Info("Stopped serving chronograf at http://%s", s.Listener.Addr())
|
Info("Stopped serving chronograf at http://", s.Listener.Addr())
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func openService(boltPath, cannedPath string) Service {
|
func openService(boltPath, cannedPath string, logger chronograf.Logger) Service {
|
||||||
db := bolt.NewClient()
|
db := bolt.NewClient()
|
||||||
db.Path = boltPath
|
db.Path = boltPath
|
||||||
if err := db.Open(); err != nil {
|
if err := db.Open(); err != nil {
|
||||||
|
@ -115,3 +130,39 @@ func openService(boltPath, cannedPath string) Service {
|
||||||
LayoutStore: layouts,
|
LayoutStore: layouts,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// reportUsageStats starts periodic server reporting.
|
||||||
|
func reportUsageStats(bi BuildInfo, logger chronograf.Logger) {
|
||||||
|
rand.Seed(time.Now().UTC().UnixNano())
|
||||||
|
serverID := strconv.FormatUint(uint64(rand.Int63()), 10)
|
||||||
|
reporter := client.New("")
|
||||||
|
u := &client.Usage{
|
||||||
|
Product: "chronograf",
|
||||||
|
Data: []client.UsageData{
|
||||||
|
{
|
||||||
|
Values: client.Values{
|
||||||
|
"os": runtime.GOOS,
|
||||||
|
"arch": runtime.GOARCH,
|
||||||
|
"version": bi.Version,
|
||||||
|
"cluster_id": serverID,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
l := logger.WithField("component", "usage").
|
||||||
|
WithField("reporting_addr", reporter.URL).
|
||||||
|
WithField("freq", "24h").
|
||||||
|
WithField("stats", "os,arch,version,cluster_id")
|
||||||
|
l.Info("Reporting usage stats")
|
||||||
|
reporter.Save(u)
|
||||||
|
|
||||||
|
ticker := time.NewTicker(24 * time.Hour)
|
||||||
|
defer ticker.Stop()
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ticker.C:
|
||||||
|
l.Debug("Reporting usage stats")
|
||||||
|
go reporter.Save(u)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue