started work on new arch

This commit is contained in:
Torkel Ödegaard 2014-10-04 13:33:20 +02:00
parent 5d120de70e
commit a4204880e8
12 changed files with 638 additions and 49 deletions

14
.bra.toml Normal file
View File

@ -0,0 +1,14 @@
[run]
init_cmds = [["./grafana-pro", "web"]]
watch_all = true
watch_dirs = [
"$WORKDIR/pkg",
"$WORKDIR/views",
]
watch_exts = [".go", ".ini"]
build_delay = 1500
cmds = [
["go", "install"],
["go", "build"],
["./grafana-pro", "web"]
]

41
conf/grafana.ini Normal file
View File

@ -0,0 +1,41 @@
app_name = Grafana Pro Server
app_mode = dev
[server]
protocol = http
domain = localhost
root_url = %(protocol)s://%(domain)s:%(http_port)s/
http_addr =
http_port = 3000
ssh_port = 22
route_log = true
[log]
root_path =
; Either "console", "file", "conn", "smtp" or "database", default is "console"
; Use comma to separate multiple modes, e.g. "console, file"
mode = console
; Buffer length of channel, keep it as it is if you don't know what it is.
buffer_len = 10000
; Either "Trace", "Debug", "Info", "Warn", "Error", "Critical", default is "Trace"
level = Trace
; For "console" mode only
[log.console]
level =
; For "file" mode only
[log.file]
level =
; This enables automated log rotate(switch of following options), default is true
log_rotate = true
; Max line number of single file, default is 1000000
max_lines = 1000000
; Max size shift of single file, default is 28 means 1 << 28, 256MB
max_lines_shift = 28
; Segment log daily, default is true
daily_rotate = true
; Expired days of log file(delete after max days), default is 7
max_days = 7

Binary file not shown.

View File

@ -2,32 +2,26 @@ package main
import (
"os"
"time"
"runtime"
log "github.com/alecthomas/log4go"
"github.com/torkelo/grafana-pro/pkg/configuration"
"github.com/torkelo/grafana-pro/pkg/server"
"github.com/codegangsta/cli"
"github.com/torkelo/grafana-pro/pkg/cmd"
)
func main() {
port := os.Getenv("PORT")
if port == "" {
port = "3838"
}
const APP_VER = "0.1.0 Alpha"
log.Info("Starting Grafana-Pro v.1-alpha")
cfg := configuration.NewCfg(port)
server, err := server.NewServer(cfg)
if err != nil {
time.Sleep(time.Second)
panic(err)
}
err = server.ListenAndServe()
if err != nil {
log.Error("ListenAndServe failed: ", err)
}
time.Sleep(time.Millisecond * 2000)
func init() {
runtime.GOMAXPROCS(runtime.NumCPU())
}
func main() {
app := cli.NewApp()
app.Name = "Grafana Pro"
app.Usage = "Grafana Pro Service"
app.Version = APP_VER
app.Commands = []cli.Command{
cmd.CmdWeb,
}
app.Flags = append(app.Flags, []cli.Flag{}...)
app.Run(os.Args)
}

View File

@ -1,14 +1,17 @@
package api
import (
"fmt"
"html/template"
log "github.com/alecthomas/log4go"
"github.com/gin-gonic/gin"
"github.com/gorilla/sessions"
"github.com/torkelo/grafana-pro/pkg/components"
"github.com/torkelo/grafana-pro/pkg/configuration"
"github.com/torkelo/grafana-pro/pkg/log"
"github.com/torkelo/grafana-pro/pkg/models"
"github.com/torkelo/grafana-pro/pkg/setting"
"github.com/torkelo/grafana-pro/pkg/stores"
)
@ -34,9 +37,9 @@ func NewHttpServer(cfg *configuration.Cfg, store stores.Store) *HttpServer {
}
func (self *HttpServer) ListenAndServe() {
log.Info("Starting Http Listener on port %v", self.port)
defer func() { self.shutdown <- true }()
gin.SetMode(gin.ReleaseMode)
self.router = gin.New()
self.router.Use(gin.Recovery(), apiLogger(), CacheHeadersMiddleware())
@ -60,7 +63,9 @@ func (self *HttpServer) ListenAndServe() {
self.router.GET("/admin/*_", self.auth(), self.index)
self.router.GET("/account/*_", self.auth(), self.index)
self.router.Run(":" + self.port)
listenAddr := fmt.Sprintf("%s:%s", setting.HttpAddr, setting.HttpPort)
log.Info("Listen: %v://%s%s", setting.Protocol, listenAddr, setting.AppSubUrl)
self.router.Run(listenAddr)
}
func (self *HttpServer) index(c *gin.Context) {

View File

@ -1,12 +1,12 @@
package api
import (
"log"
"os"
"strings"
"time"
"github.com/gin-gonic/gin"
"github.com/torkelo/grafana-pro/pkg/log"
)
var (
@ -25,8 +25,6 @@ func ignoreLoggingRequest(code int, contentType string) bool {
}
func apiLogger() gin.HandlerFunc {
stdlogger := log.New(os.Stdout, "", 0)
return func(c *gin.Context) {
// Start timer
start := time.Now()
@ -53,26 +51,13 @@ func apiLogger() gin.HandlerFunc {
requester = c.Request.RemoteAddr
}
var color string
switch {
case code >= 200 && code <= 299:
color = green
case code >= 300 && code <= 399:
color = white
case code >= 400 && code <= 499:
color = yellow
default:
color = red
}
end := time.Now()
latency := end.Sub(start)
stdlogger.Printf("[GIN] %v |%s %3d %s| %12v | %s %4s %s\n%s",
end.Format("2006/01/02 - 15:04:05"),
color, code, reset,
log.Info("[http] %s %s %3d %12v %s %s",
c.Request.Method, c.Request.URL.Path,
code,
latency,
requester,
c.Request.Method, c.Request.URL.Path,
c.Errors.String(),
)
}

45
pkg/cmd/web.go Normal file
View File

@ -0,0 +1,45 @@
package cmd
import (
"os"
"time"
"github.com/codegangsta/cli"
"github.com/siddontang/go-log/log"
"github.com/torkelo/grafana-pro/pkg/configuration"
"github.com/torkelo/grafana-pro/pkg/routes"
"github.com/torkelo/grafana-pro/pkg/server"
)
var CmdWeb = cli.Command{
Name: "web",
Usage: "Start Grafana Pro web server",
Description: `Start Grafana Pro server`,
Action: runWeb,
Flags: []cli.Flag{},
}
func runWeb(*cli.Context) {
routes.GlobalInit()
port := os.Getenv("PORT")
if port == "" {
port = "3838"
}
log.Info("Starting Grafana-Pro v.1-alpha")
cfg := configuration.NewCfg(port)
server, err := server.NewServer(cfg)
if err != nil {
time.Sleep(time.Second)
panic(err)
}
err = server.ListenAndServe()
if err != nil {
log.Error("ListenAndServe failed: ", err)
}
time.Sleep(time.Millisecond * 2000)
}

73
pkg/log/console.go Normal file
View File

@ -0,0 +1,73 @@
// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package log
import (
"encoding/json"
"log"
"os"
"runtime"
)
type Brush func(string) string
func NewBrush(color string) Brush {
pre := "\033["
reset := "\033[0m"
return func(text string) string {
return pre + color + "m" + text + reset
}
}
var colors = []Brush{
NewBrush("1;36"), // Trace cyan
NewBrush("1;34"), // Debug blue
NewBrush("1;32"), // Info green
NewBrush("1;33"), // Warn yellow
NewBrush("1;31"), // Error red
NewBrush("1;35"), // Critical purple
NewBrush("1;31"), // Fatal red
}
// ConsoleWriter implements LoggerInterface and writes messages to terminal.
type ConsoleWriter struct {
lg *log.Logger
Level int `json:"level"`
}
// create ConsoleWriter returning as LoggerInterface.
func NewConsole() LoggerInterface {
return &ConsoleWriter{
lg: log.New(os.Stdout, "", log.Ldate|log.Ltime),
Level: TRACE,
}
}
func (cw *ConsoleWriter) Init(config string) error {
return json.Unmarshal([]byte(config), cw)
}
func (cw *ConsoleWriter) WriteMsg(msg string, skip, level int) error {
if cw.Level > level {
return nil
}
if runtime.GOOS == "windows" {
cw.lg.Println(msg)
} else {
cw.lg.Println(colors[level](msg))
}
return nil
}
func (_ *ConsoleWriter) Flush() {
}
func (_ *ConsoleWriter) Destroy() {
}
func init() {
Register("console", NewConsole)
}

299
pkg/log/log.go Normal file
View File

@ -0,0 +1,299 @@
// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package log
import (
"fmt"
"os"
"path/filepath"
"runtime"
"strings"
"sync"
)
var (
loggers []*Logger
)
func NewLogger(bufLen int64, mode, config string) {
logger := newLogger(bufLen)
isExist := false
for _, l := range loggers {
if l.adapter == mode {
isExist = true
l = logger
}
}
if !isExist {
loggers = append(loggers, logger)
}
if err := logger.SetLogger(mode, config); err != nil {
Fatal(1, "Fail to set logger(%s): %v", mode, err)
}
}
func Trace(format string, v ...interface{}) {
for _, logger := range loggers {
logger.Trace(format, v...)
}
}
func Debug(format string, v ...interface{}) {
for _, logger := range loggers {
logger.Debug(format, v...)
}
}
func Info(format string, v ...interface{}) {
for _, logger := range loggers {
logger.Info(format, v...)
}
}
func Warn(format string, v ...interface{}) {
for _, logger := range loggers {
logger.Warn(format, v...)
}
}
func Error(skip int, format string, v ...interface{}) {
for _, logger := range loggers {
logger.Error(skip, format, v...)
}
}
func Critical(skip int, format string, v ...interface{}) {
for _, logger := range loggers {
logger.Critical(skip, format, v...)
}
}
func Fatal(skip int, format string, v ...interface{}) {
Error(skip, format, v...)
for _, l := range loggers {
l.Close()
}
os.Exit(1)
}
func Close() {
for _, l := range loggers {
l.Close()
}
}
// .___ __ _____
// | | _____/ |_ ____________/ ____\____ ____ ____
// | |/ \ __\/ __ \_ __ \ __\\__ \ _/ ___\/ __ \
// | | | \ | \ ___/| | \/| | / __ \\ \__\ ___/
// |___|___| /__| \___ >__| |__| (____ /\___ >___ >
// \/ \/ \/ \/ \/
type LogLevel int
const (
TRACE = iota
DEBUG
INFO
WARN
ERROR
CRITICAL
FATAL
)
// LoggerInterface represents behaviors of a logger provider.
type LoggerInterface interface {
Init(config string) error
WriteMsg(msg string, skip, level int) error
Destroy()
Flush()
}
type loggerType func() LoggerInterface
var adapters = make(map[string]loggerType)
// Register registers given logger provider to adapters.
func Register(name string, log loggerType) {
if log == nil {
panic("log: register provider is nil")
}
if _, dup := adapters[name]; dup {
panic("log: register called twice for provider \"" + name + "\"")
}
adapters[name] = log
}
type logMsg struct {
skip, level int
msg string
}
// Logger is default logger in beego application.
// it can contain several providers and log message into all providers.
type Logger struct {
adapter string
lock sync.Mutex
level int
msg chan *logMsg
outputs map[string]LoggerInterface
quit chan bool
}
// newLogger initializes and returns a new logger.
func newLogger(buffer int64) *Logger {
l := &Logger{
msg: make(chan *logMsg, buffer),
outputs: make(map[string]LoggerInterface),
quit: make(chan bool),
}
go l.StartLogger()
return l
}
// SetLogger sets new logger instanse with given logger adapter and config.
func (l *Logger) SetLogger(adapter string, config string) error {
l.lock.Lock()
defer l.lock.Unlock()
if log, ok := adapters[adapter]; ok {
lg := log()
if err := lg.Init(config); err != nil {
return err
}
l.outputs[adapter] = lg
l.adapter = adapter
} else {
panic("log: unknown adapter \"" + adapter + "\" (forgotten register?)")
}
return nil
}
// DelLogger removes a logger adapter instance.
func (l *Logger) DelLogger(adapter string) error {
l.lock.Lock()
defer l.lock.Unlock()
if lg, ok := l.outputs[adapter]; ok {
lg.Destroy()
delete(l.outputs, adapter)
} else {
panic("log: unknown adapter \"" + adapter + "\" (forgotten register?)")
}
return nil
}
func (l *Logger) writerMsg(skip, level int, msg string) error {
if l.level > level {
return nil
}
lm := &logMsg{
skip: skip,
level: level,
}
// Only error information needs locate position for debugging.
if lm.level >= ERROR {
pc, file, line, ok := runtime.Caller(skip)
if ok {
// Get caller function name.
fn := runtime.FuncForPC(pc)
var fnName string
if fn == nil {
fnName = "?()"
} else {
fnName = strings.TrimLeft(filepath.Ext(fn.Name()), ".") + "()"
}
lm.msg = fmt.Sprintf("[%s:%d %s] %s", filepath.Base(file), line, fnName, msg)
} else {
lm.msg = msg
}
} else {
lm.msg = msg
}
l.msg <- lm
return nil
}
// StartLogger starts logger chan reading.
func (l *Logger) StartLogger() {
for {
select {
case bm := <-l.msg:
for _, l := range l.outputs {
if err := l.WriteMsg(bm.msg, bm.skip, bm.level); err != nil {
fmt.Println("ERROR, unable to WriteMsg:", err)
}
}
case <-l.quit:
return
}
}
}
// Flush flushs all chan data.
func (l *Logger) Flush() {
for _, l := range l.outputs {
l.Flush()
}
}
// Close closes logger, flush all chan data and destroy all adapter instances.
func (l *Logger) Close() {
l.quit <- true
for {
if len(l.msg) > 0 {
bm := <-l.msg
for _, l := range l.outputs {
if err := l.WriteMsg(bm.msg, bm.skip, bm.level); err != nil {
fmt.Println("ERROR, unable to WriteMsg:", err)
}
}
} else {
break
}
}
for _, l := range l.outputs {
l.Flush()
l.Destroy()
}
}
func (l *Logger) Trace(format string, v ...interface{}) {
msg := fmt.Sprintf("[T] "+format, v...)
l.writerMsg(0, TRACE, msg)
}
func (l *Logger) Debug(format string, v ...interface{}) {
msg := fmt.Sprintf("[D] "+format, v...)
l.writerMsg(0, DEBUG, msg)
}
func (l *Logger) Info(format string, v ...interface{}) {
msg := fmt.Sprintf("[I] "+format, v...)
l.writerMsg(0, INFO, msg)
}
func (l *Logger) Warn(format string, v ...interface{}) {
msg := fmt.Sprintf("[W] "+format, v...)
l.writerMsg(0, WARN, msg)
}
func (l *Logger) Error(skip int, format string, v ...interface{}) {
msg := fmt.Sprintf("[E] "+format, v...)
l.writerMsg(skip, ERROR, msg)
}
func (l *Logger) Critical(skip int, format string, v ...interface{}) {
msg := fmt.Sprintf("[C] "+format, v...)
l.writerMsg(skip, CRITICAL, msg)
}
func (l *Logger) Fatal(skip int, format string, v ...interface{}) {
msg := fmt.Sprintf("[F] "+format, v...)
l.writerMsg(skip, FATAL, msg)
l.Close()
os.Exit(1)
}

7
pkg/routes/routes.go Normal file
View File

@ -0,0 +1,7 @@
package routes
import "github.com/torkelo/grafana-pro/pkg/setting"
func GlobalInit() {
setting.NewConfigContext()
}

125
pkg/setting/setting.go Normal file
View File

@ -0,0 +1,125 @@
package setting
import (
"net/url"
"os"
"os/exec"
"path"
"path/filepath"
"runtime"
"strings"
"github.com/Unknwon/com"
"github.com/Unknwon/goconfig"
"github.com/torkelo/grafana-pro/pkg/log"
)
type Scheme string
const (
HTTP Scheme = "http"
HTTPS Scheme = "https"
)
var (
// App settings.
AppVer string
AppName string
AppUrl string
AppSubUrl string
// Log settings.
LogRootPath string
LogModes []string
LogConfigs []string
// Http server options
Protocol Scheme
Domain string
HttpAddr, HttpPort string
SshPort int
CertFile, KeyFile string
DisableRouterLog bool
// Global setting objects.
Cfg *goconfig.ConfigFile
ConfRootPath string
CustomPath string // Custom directory path.
ProdMode bool
RunUser string
IsWindows bool
)
func init() {
IsWindows = runtime.GOOS == "windows"
log.NewLogger(0, "console", `{"level": 0}`)
}
// WorkDir returns absolute path of work directory.
func WorkDir() (string, error) {
execPath, err := ExecPath()
return path.Dir(strings.Replace(execPath, "\\", "/", -1)), err
}
func ExecPath() (string, error) {
file, err := exec.LookPath(os.Args[0])
if err != nil {
return "", err
}
p, err := filepath.Abs(file)
if err != nil {
return "", err
}
return p, nil
}
func NewConfigContext() {
workDir, err := WorkDir()
if err != nil {
log.Fatal(4, "Fail to get work directory: %v", err)
}
ConfRootPath = path.Join(workDir, "conf")
Cfg, err = goconfig.LoadConfigFile(path.Join(workDir, "conf/grafana.ini"))
if err != nil {
log.Fatal(4, "Fail to parse 'conf/grafana.ini': %v", err)
}
CustomPath = os.Getenv("GRAFANA_CONF")
if len(CustomPath) == 0 {
CustomPath = path.Join(workDir, "custom")
}
cfgPath := path.Join(CustomPath, "conf/grafana.ini")
if com.IsFile(cfgPath) {
if err = Cfg.AppendFiles(cfgPath); err != nil {
log.Fatal(4, "Fail to load custom 'conf/grafana.ini': %v", err)
}
} else {
log.Warn("No custom 'conf/grafana.ini'")
}
AppName = Cfg.MustValue("", "app_name", "Grafana Pro")
AppUrl = Cfg.MustValue("server", "root_url", "http://localhost:3000/")
if AppUrl[len(AppUrl)-1] != '/' {
AppUrl += "/"
}
// Check if has app suburl.
url, err := url.Parse(AppUrl)
if err != nil {
log.Fatal(4, "Invalid root_url(%s): %s", AppUrl, err)
}
AppSubUrl = strings.TrimSuffix(url.Path, "/")
Protocol = HTTP
if Cfg.MustValue("server", "protocol") == "https" {
Protocol = HTTPS
CertFile = Cfg.MustValue("server", "cert_file")
KeyFile = Cfg.MustValue("server", "key_file")
}
Domain = Cfg.MustValue("server", "domain", "localhost")
HttpAddr = Cfg.MustValue("server", "http_addr", "0.0.0.0")
HttpPort = Cfg.MustValue("server", "http_port", "3000")
}

View File

@ -3,8 +3,9 @@ package stores
import (
"time"
log "github.com/alecthomas/log4go"
r "github.com/dancannon/gorethink"
"github.com/torkelo/grafana-pro/pkg/log"
)
type rethinkStore struct {
@ -31,7 +32,7 @@ func NewRethinkStore(config *RethinkCfg) *rethinkStore {
})
if err != nil {
log.Crash("Failed to connect to rethink database %v", err)
log.Error(3, "Failed to connect to rethink database %v", err)
}
createRethinkDBTablesAndIndices(config, session)