Merge pull request #292 from influxdata/feature/tracking-simple

Add server reporting
pull/10616/head
Gunnar 2016-10-28 14:59:34 -07:00 committed by GitHub
commit 7ee9a10d30
9 changed files with 130 additions and 31 deletions

1
Godeps
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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