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/google/go-github 1bc362c7737e51014af7299e016444b654095ad9
|
||||
github.com/google/go-querystring 9235644dd9e52eeae6fa48efd539fdc351a0af53
|
||||
github.com/influxdata/usage-client 6d3895376368aa52a3a81d2a16e90f0f52371967
|
||||
github.com/jessevdk/go-flags 4cc2832a6e6d1d3b815e2b9d544b2a4dfb3ce8fa
|
||||
github.com/satori/go.uuid b061729afc07e77a8aa4fad0a2fd840958f1942a
|
||||
github.com/tylerb/graceful 50a48b6e73fcc75b45e22c05b79629a67c79e938
|
||||
|
|
4
Makefile
4
Makefile
|
@ -1,11 +1,9 @@
|
|||
VERSION ?= $$(git describe --always --tags)
|
||||
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')
|
||||
|
||||
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
|
||||
|
||||
default: dep build
|
||||
|
|
|
@ -17,7 +17,7 @@ import (
|
|||
|
||||
const AppExt = ".json"
|
||||
|
||||
var logger = clog.New()
|
||||
var logger = clog.New(clog.DebugLevel)
|
||||
|
||||
// Apps are canned JSON layouts. Implements LayoutStore.
|
||||
type Apps struct {
|
||||
|
|
|
@ -27,11 +27,12 @@ func (e Error) Error() string {
|
|||
// provides methods to trigger log messages at various alert levels and a
|
||||
// WithField method to set keys for a structured log message.
|
||||
type Logger interface {
|
||||
Debug(...interface{})
|
||||
Info(...interface{})
|
||||
Warn(...interface{})
|
||||
Debug(...interface{})
|
||||
Panic(...interface{})
|
||||
Error(...interface{})
|
||||
Fatal(...interface{})
|
||||
Panic(...interface{})
|
||||
|
||||
WithField(string, interface{}) Logger
|
||||
}
|
||||
|
|
|
@ -10,14 +10,17 @@ import (
|
|||
|
||||
// Build flags
|
||||
var (
|
||||
Version = ""
|
||||
Commit = ""
|
||||
BuildTime = ""
|
||||
Branch = ""
|
||||
Version = ""
|
||||
Commit = ""
|
||||
)
|
||||
|
||||
func main() {
|
||||
srv := server.Server{}
|
||||
srv := server.Server{
|
||||
BuildInfo: server.BuildInfo{
|
||||
Version: Version,
|
||||
Commit: Commit,
|
||||
},
|
||||
}
|
||||
|
||||
parser := flags.NewParser(&srv, flags.Default)
|
||||
parser.ShortDescription = `Chronograf`
|
||||
|
|
|
@ -26,7 +26,7 @@ func Test_Influx_MakesRequestsToQueryEndpoint(t *testing.T) {
|
|||
defer ts.Close()
|
||||
|
||||
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 {
|
||||
t.Fatal("Unexpected error initializing client: err:", err)
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ func Test_Influx_CancelsInFlightRequests(t *testing.T) {
|
|||
ts.Close()
|
||||
}()
|
||||
|
||||
series, _ := influx.NewClient(ts.URL, log.New())
|
||||
series, _ := influx.NewClient(ts.URL, log.New(log.DebugLevel))
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
errs := make(chan (error))
|
||||
|
@ -102,7 +102,7 @@ func Test_Influx_CancelsInFlightRequests(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 {
|
||||
t.Fatal("Expected err but was nil")
|
||||
}
|
||||
|
@ -114,7 +114,7 @@ func Test_Influx_ReportsInfluxErrs(t *testing.T) {
|
|||
}))
|
||||
defer ts.Close()
|
||||
|
||||
cl, err := influx.NewClient(ts.URL, log.New())
|
||||
cl, err := influx.NewClient(ts.URL, log.New(log.DebugLevel))
|
||||
if err != nil {
|
||||
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"
|
||||
)
|
||||
|
||||
// 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
|
||||
type logrusLogger struct {
|
||||
l *logrus.Entry
|
||||
}
|
||||
|
||||
func (ll *logrusLogger) Debug(items ...interface{}) {
|
||||
ll.l.Debug(items...)
|
||||
}
|
||||
|
||||
func (ll *logrusLogger) Info(items ...interface{}) {
|
||||
ll.l.Info(items...)
|
||||
}
|
||||
|
@ -20,28 +65,28 @@ func (ll *logrusLogger) Warn(items ...interface{}) {
|
|||
ll.l.Warn(items...)
|
||||
}
|
||||
|
||||
func (ll *logrusLogger) Debug(items ...interface{}) {
|
||||
ll.l.Debug(items...)
|
||||
func (ll *logrusLogger) Error(items ...interface{}) {
|
||||
ll.l.Error(items...)
|
||||
}
|
||||
|
||||
func (ll *logrusLogger) Fatal(items ...interface{}) {
|
||||
ll.l.Fatal(items...)
|
||||
}
|
||||
|
||||
func (ll *logrusLogger) Panic(items ...interface{}) {
|
||||
ll.l.Panic(items...)
|
||||
}
|
||||
|
||||
func (ll *logrusLogger) Error(items ...interface{}) {
|
||||
ll.l.Error(items...)
|
||||
}
|
||||
|
||||
func (ll *logrusLogger) WithField(key string, value interface{}) chronograf.Logger {
|
||||
return &logrusLogger{ll.l.WithField(key, value)}
|
||||
}
|
||||
|
||||
func New() chronograf.Logger {
|
||||
func New(l Level) chronograf.Logger {
|
||||
logger := &logrus.Logger{
|
||||
Out: os.Stderr,
|
||||
Formatter: new(logrus.TextFormatter),
|
||||
Hooks: make(logrus.LevelHooks),
|
||||
Level: logrus.DebugLevel,
|
||||
Level: logrus.Level(l),
|
||||
}
|
||||
|
||||
return &logrusLogger{
|
||||
|
|
|
@ -9,8 +9,8 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/influxdata/chronograf"
|
||||
"github.com/influxdata/chronograf/server"
|
||||
clog "github.com/influxdata/chronograf/log"
|
||||
"github.com/influxdata/chronograf/server"
|
||||
)
|
||||
|
||||
func TestCookieExtractor(t *testing.T) {
|
||||
|
@ -180,7 +180,7 @@ func TestAuthorizedToken(t *testing.T) {
|
|||
Principal: test.Principal,
|
||||
}
|
||||
|
||||
logger := clog.New()
|
||||
logger := clog.New(clog.DebugLevel)
|
||||
handler := server.AuthorizedToken(a, e, logger, next)
|
||||
handler.ServeHTTP(w, req)
|
||||
if w.Code != test.Code {
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"net"
|
||||
"net/http"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
|
@ -13,12 +15,11 @@ import (
|
|||
"github.com/influxdata/chronograf/layouts"
|
||||
clog "github.com/influxdata/chronograf/log"
|
||||
"github.com/influxdata/chronograf/uuid"
|
||||
client "github.com/influxdata/usage-client/v1"
|
||||
flags "github.com/jessevdk/go-flags"
|
||||
"github.com/tylerb/graceful"
|
||||
)
|
||||
|
||||
var logger = clog.New()
|
||||
|
||||
// Server for the chronograf API
|
||||
type Server struct {
|
||||
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"`
|
||||
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"`
|
||||
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
|
||||
handler http.Handler
|
||||
}
|
||||
|
||||
type BuildInfo struct {
|
||||
Version string
|
||||
Commit string
|
||||
}
|
||||
|
||||
func (s *Server) useAuth() bool {
|
||||
return s.TokenSecret != "" && s.GithubClientID != "" && s.GithubClientSecret != ""
|
||||
}
|
||||
|
||||
// Serve starts and runs the chronograf server
|
||||
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{
|
||||
Develop: s.Develop,
|
||||
TokenSecret: s.TokenSecret,
|
||||
|
@ -70,9 +81,13 @@ func (s *Server) Serve() error {
|
|||
httpServer.TCPKeepAlive = 1 * time.Minute
|
||||
httpServer.Handler = s.handler
|
||||
|
||||
if !s.ReportingDisabled {
|
||||
go reportUsageStats(s.BuildInfo, logger)
|
||||
}
|
||||
|
||||
logger.
|
||||
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 {
|
||||
logger.
|
||||
|
@ -83,12 +98,12 @@ func (s *Server) Serve() error {
|
|||
|
||||
logger.
|
||||
WithField("component", "server").
|
||||
Info("Stopped serving chronograf at http://%s", s.Listener.Addr())
|
||||
Info("Stopped serving chronograf at http://", s.Listener.Addr())
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func openService(boltPath, cannedPath string) Service {
|
||||
func openService(boltPath, cannedPath string, logger chronograf.Logger) Service {
|
||||
db := bolt.NewClient()
|
||||
db.Path = boltPath
|
||||
if err := db.Open(); err != nil {
|
||||
|
@ -115,3 +130,39 @@ func openService(boltPath, cannedPath string) Service {
|
|||
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