Added logging
This commit is contained in:
parent
091711f4c5
commit
e93cba7108
24 changed files with 1596 additions and 10 deletions
|
@ -157,7 +157,6 @@ Teams sind global, d.h. Ein Team kann mehrere Namespaces verwalten.
|
|||
* [x] Delete
|
||||
|
||||
* [x] /namespaces soll zumindest auch die namen (+id) der dazugehörigen Listen rausgeben
|
||||
* [ ] Endpoint um nach Usern zu suchen, erstmal nur mit Nutzernamen, später mit setting ob auch mit email gesucht werden darf
|
||||
|
||||
## Feature-Ideen
|
||||
|
||||
|
@ -216,7 +215,7 @@ Teams sind global, d.h. Ein Team kann mehrere Namespaces verwalten.
|
|||
* [x] Nen endpoint um /teams/members /list/users etc die Rechte updazudaten ohne erst zu löschen und dann neu einzufügen
|
||||
* [ ] Search endpoints /users?s=name und /teams?s=name, erstmal nur mit Namen suchen. -> Interface erweitern mit ner Funktion Search?
|
||||
* [ ] namespaces & listen updaten geht nicht, gibt nen 500er zurück
|
||||
* [ ] Logging für alle Fehler irgendwohin, da gibts bestimmt ne coole library für
|
||||
* [x] Logging für alle Fehler irgendwohin, da gibts bestimmt ne coole library für
|
||||
|
||||
### Later/Nice to have
|
||||
|
||||
|
|
9
Gopkg.lock
generated
9
Gopkg.lock
generated
|
@ -161,6 +161,14 @@
|
|||
revision = "fa473d140ef3c6adf42d6b391fe76707f1f243c8"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:5b3b29ce0e569f62935d9541dff2e16cc09df981ebde48e82259076a73a3d0c7"
|
||||
name = "github.com/op/go-logging"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "b2cb9fa56473e98db8caba80237377e83fe44db5"
|
||||
version = "v1"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:95741de3af260a92cc5c7f3f3061e85273f5a81b5db20d4bd68da74bd521675e"
|
||||
name = "github.com/pelletier/go-toml"
|
||||
|
@ -306,6 +314,7 @@
|
|||
"github.com/labstack/echo",
|
||||
"github.com/labstack/echo/middleware",
|
||||
"github.com/mattn/go-sqlite3",
|
||||
"github.com/op/go-logging",
|
||||
"github.com/spf13/viper",
|
||||
"github.com/stretchr/testify/assert",
|
||||
"golang.org/x/crypto/bcrypt",
|
||||
|
|
|
@ -51,3 +51,7 @@
|
|||
[[constraint]]
|
||||
name = "github.com/imdario/mergo"
|
||||
version = "0.3.6"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/op/go-logging"
|
||||
version = "1.0.0"
|
||||
|
|
12
main.go
12
main.go
|
@ -5,7 +5,6 @@ import (
|
|||
"code.vikunja.io/api/routes"
|
||||
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/spf13/viper"
|
||||
"os"
|
||||
"os/signal"
|
||||
|
@ -17,22 +16,25 @@ var Version = "0.1"
|
|||
|
||||
func main() {
|
||||
|
||||
// Init logging
|
||||
models.InitLogger()
|
||||
|
||||
// Init Config
|
||||
err := models.InitConfig()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
models.Log.Error(err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Set Engine
|
||||
err = models.SetEngine()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
models.Log.Error(err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Version notification
|
||||
fmt.Println("Vikunja version", Version)
|
||||
models.Log.Infof("Vikunja version %s", Version)
|
||||
|
||||
// Start the webserver
|
||||
e := routes.NewEcho()
|
||||
|
@ -51,7 +53,7 @@ func main() {
|
|||
<-quit
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
fmt.Println("Shutting down...")
|
||||
models.Log.Infof("Sutting down...")
|
||||
if err := e.Shutdown(ctx); err != nil {
|
||||
e.Logger.Fatal(err)
|
||||
}
|
||||
|
|
19
models/logging.go
Normal file
19
models/logging.go
Normal file
|
@ -0,0 +1,19 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"github.com/op/go-logging"
|
||||
"os"
|
||||
)
|
||||
|
||||
var Log = logging.MustGetLogger("vikunja")
|
||||
|
||||
var format = logging.MustStringFormatter(
|
||||
`%{color}%{time:15:04:05.000} %{shortfunc} ▶ %{level:.4s} %{id:03x}%{color:reset} %{message}`,
|
||||
)
|
||||
|
||||
func InitLogger() {
|
||||
backend := logging.NewLogBackend(os.Stderr, "", 0)
|
||||
backendFormatter := logging.NewBackendFormatter(backend, format)
|
||||
logging.SetBackend(backendFormatter)
|
||||
}
|
||||
|
|
@ -26,12 +26,15 @@ func (c *WebHandler) CreateWeb(ctx echo.Context) error {
|
|||
|
||||
// Check rights
|
||||
if !c.CObject.CanCreate(¤tUser) {
|
||||
models.Log.Noticef("%s [ID: %d] tried to create while not having the rights for it", currentUser.Username, currentUser.ID)
|
||||
return echo.NewHTTPError(http.StatusForbidden)
|
||||
}
|
||||
|
||||
// Create
|
||||
err = c.CObject.Create(¤tUser)
|
||||
if err != nil {
|
||||
models.Log.Error(err.Error())
|
||||
|
||||
if models.IsErrListDoesNotExist(err) {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "The list does not exist.")
|
||||
}
|
||||
|
|
|
@ -19,11 +19,14 @@ func (c *WebHandler) DeleteWeb(ctx echo.Context) error {
|
|||
return echo.NewHTTPError(http.StatusInternalServerError)
|
||||
}
|
||||
if !c.CObject.CanDelete(&user) {
|
||||
models.Log.Noticef("%s [ID: %d] tried to delete while not having the rights for it", user.Username, user.ID)
|
||||
return echo.NewHTTPError(http.StatusForbidden)
|
||||
}
|
||||
|
||||
err = c.CObject.Delete()
|
||||
if err != nil {
|
||||
models.Log.Error(err.Error())
|
||||
|
||||
if models.IsErrNeedToBeListAdmin(err) {
|
||||
return echo.NewHTTPError(http.StatusForbidden, "You need to be the list admin to delete a list.")
|
||||
}
|
||||
|
|
|
@ -20,6 +20,8 @@ func (c *WebHandler) ReadAllWeb(ctx echo.Context) error {
|
|||
|
||||
lists, err := c.CObject.ReadAll(¤tUser)
|
||||
if err != nil {
|
||||
models.Log.Error(err.Error())
|
||||
|
||||
if models.IsErrNeedToHaveListReadAccess(err) {
|
||||
return echo.NewHTTPError(http.StatusForbidden, "You need to have read access to this list.")
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package crud
|
|||
|
||||
import (
|
||||
"code.vikunja.io/api/models"
|
||||
"fmt"
|
||||
"github.com/labstack/echo"
|
||||
"net/http"
|
||||
)
|
||||
|
@ -18,6 +17,8 @@ func (c *WebHandler) ReadOneWeb(ctx echo.Context) error {
|
|||
// Get our object
|
||||
err := c.CObject.ReadOne()
|
||||
if err != nil {
|
||||
models.Log.Error(err.Error())
|
||||
|
||||
if models.IsErrListDoesNotExist(err) {
|
||||
return echo.NewHTTPError(http.StatusNotFound)
|
||||
}
|
||||
|
@ -30,8 +31,6 @@ func (c *WebHandler) ReadOneWeb(ctx echo.Context) error {
|
|||
return echo.NewHTTPError(http.StatusNotFound)
|
||||
}
|
||||
|
||||
fmt.Println(err)
|
||||
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, "An error occured.")
|
||||
}
|
||||
|
||||
|
@ -42,6 +41,7 @@ func (c *WebHandler) ReadOneWeb(ctx echo.Context) error {
|
|||
return echo.NewHTTPError(http.StatusInternalServerError, "Could not determine the current user.")
|
||||
}
|
||||
if !c.CObject.CanRead(¤tUser) {
|
||||
models.Log.Noticef("%s [ID: %d] tried to read while not having the rights for it", currentUser.Username, currentUser.ID)
|
||||
return echo.NewHTTPError(http.StatusForbidden, "You don't have the right to see this")
|
||||
}
|
||||
|
||||
|
|
|
@ -24,12 +24,15 @@ func (c *WebHandler) UpdateWeb(ctx echo.Context) error {
|
|||
return echo.NewHTTPError(http.StatusInternalServerError, "Could not determine the current user.")
|
||||
}
|
||||
if !c.CObject.CanUpdate(¤tUser) {
|
||||
models.Log.Noticef("%s [ID: %d] tried to update while not having the rights for it", currentUser.Username, currentUser.ID)
|
||||
return echo.NewHTTPError(http.StatusForbidden)
|
||||
}
|
||||
|
||||
// Do the update
|
||||
err = c.CObject.Update()
|
||||
if err != nil {
|
||||
models.Log.Error(err.Error())
|
||||
|
||||
if models.IsErrNeedToBeListAdmin(err) {
|
||||
return echo.NewHTTPError(http.StatusForbidden, "You need to be list admin to do that.")
|
||||
}
|
||||
|
|
6
vendor/github.com/op/go-logging/.travis.yml
generated
vendored
Normal file
6
vendor/github.com/op/go-logging/.travis.yml
generated
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
language: go
|
||||
|
||||
go:
|
||||
- 1.0
|
||||
- 1.1
|
||||
- tip
|
5
vendor/github.com/op/go-logging/CONTRIBUTORS
generated
vendored
Normal file
5
vendor/github.com/op/go-logging/CONTRIBUTORS
generated
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
Alec Thomas <alec@swapoff.org>
|
||||
Guilhem Lettron <guilhem.lettron@optiflows.com>
|
||||
Ivan Daniluk <ivan.daniluk@gmail.com>
|
||||
Nimi Wariboko Jr <nimi@channelmeter.com>
|
||||
Róbert Selvek <robert.selvek@gmail.com>
|
27
vendor/github.com/op/go-logging/LICENSE
generated
vendored
Normal file
27
vendor/github.com/op/go-logging/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
Copyright (c) 2013 Örjan Persson. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
89
vendor/github.com/op/go-logging/README.md
generated
vendored
Normal file
89
vendor/github.com/op/go-logging/README.md
generated
vendored
Normal file
|
@ -0,0 +1,89 @@
|
|||
## Golang logging library
|
||||
|
||||
[![godoc](http://img.shields.io/badge/godoc-reference-blue.svg?style=flat)](https://godoc.org/github.com/op/go-logging) [![build](https://img.shields.io/travis/op/go-logging.svg?style=flat)](https://travis-ci.org/op/go-logging)
|
||||
|
||||
Package logging implements a logging infrastructure for Go. Its output format
|
||||
is customizable and supports different logging backends like syslog, file and
|
||||
memory. Multiple backends can be utilized with different log levels per backend
|
||||
and logger.
|
||||
|
||||
## Example
|
||||
|
||||
Let's have a look at an [example](examples/example.go) which demonstrates most
|
||||
of the features found in this library.
|
||||
|
||||
[![Example Output](examples/example.png)](examples/example.go)
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/op/go-logging"
|
||||
)
|
||||
|
||||
var log = logging.MustGetLogger("example")
|
||||
|
||||
// Example format string. Everything except the message has a custom color
|
||||
// which is dependent on the log level. Many fields have a custom output
|
||||
// formatting too, eg. the time returns the hour down to the milli second.
|
||||
var format = logging.MustStringFormatter(
|
||||
`%{color}%{time:15:04:05.000} %{shortfunc} ▶ %{level:.4s} %{id:03x}%{color:reset} %{message}`,
|
||||
)
|
||||
|
||||
// Password is just an example type implementing the Redactor interface. Any
|
||||
// time this is logged, the Redacted() function will be called.
|
||||
type Password string
|
||||
|
||||
func (p Password) Redacted() interface{} {
|
||||
return logging.Redact(string(p))
|
||||
}
|
||||
|
||||
func main() {
|
||||
// For demo purposes, create two backend for os.Stderr.
|
||||
backend1 := logging.NewLogBackend(os.Stderr, "", 0)
|
||||
backend2 := logging.NewLogBackend(os.Stderr, "", 0)
|
||||
|
||||
// For messages written to backend2 we want to add some additional
|
||||
// information to the output, including the used log level and the name of
|
||||
// the function.
|
||||
backend2Formatter := logging.NewBackendFormatter(backend2, format)
|
||||
|
||||
// Only errors and more severe messages should be sent to backend1
|
||||
backend1Leveled := logging.AddModuleLevel(backend1)
|
||||
backend1Leveled.SetLevel(logging.ERROR, "")
|
||||
|
||||
// Set the backends to be used.
|
||||
logging.SetBackend(backend1Leveled, backend2Formatter)
|
||||
|
||||
log.Debugf("debug %s", Password("secret"))
|
||||
log.Info("info")
|
||||
log.Notice("notice")
|
||||
log.Warning("warning")
|
||||
log.Error("err")
|
||||
log.Critical("crit")
|
||||
}
|
||||
```
|
||||
|
||||
## Installing
|
||||
|
||||
### Using *go get*
|
||||
|
||||
$ go get github.com/op/go-logging
|
||||
|
||||
After this command *go-logging* is ready to use. Its source will be in:
|
||||
|
||||
$GOPATH/src/pkg/github.com/op/go-logging
|
||||
|
||||
You can use `go get -u` to update the package.
|
||||
|
||||
## Documentation
|
||||
|
||||
For docs, see http://godoc.org/github.com/op/go-logging or run:
|
||||
|
||||
$ godoc github.com/op/go-logging
|
||||
|
||||
## Additional resources
|
||||
|
||||
* [wslog](https://godoc.org/github.com/cryptix/go/logging/wslog) -- exposes log messages through a WebSocket.
|
39
vendor/github.com/op/go-logging/backend.go
generated
vendored
Normal file
39
vendor/github.com/op/go-logging/backend.go
generated
vendored
Normal file
|
@ -0,0 +1,39 @@
|
|||
// Copyright 2013, Örjan Persson. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package logging
|
||||
|
||||
// defaultBackend is the backend used for all logging calls.
|
||||
var defaultBackend LeveledBackend
|
||||
|
||||
// Backend is the interface which a log backend need to implement to be able to
|
||||
// be used as a logging backend.
|
||||
type Backend interface {
|
||||
Log(Level, int, *Record) error
|
||||
}
|
||||
|
||||
// SetBackend replaces the backend currently set with the given new logging
|
||||
// backend.
|
||||
func SetBackend(backends ...Backend) LeveledBackend {
|
||||
var backend Backend
|
||||
if len(backends) == 1 {
|
||||
backend = backends[0]
|
||||
} else {
|
||||
backend = MultiLogger(backends...)
|
||||
}
|
||||
|
||||
defaultBackend = AddModuleLevel(backend)
|
||||
return defaultBackend
|
||||
}
|
||||
|
||||
// SetLevel sets the logging level for the specified module. The module
|
||||
// corresponds to the string specified in GetLogger.
|
||||
func SetLevel(level Level, module string) {
|
||||
defaultBackend.SetLevel(level, module)
|
||||
}
|
||||
|
||||
// GetLevel returns the logging level for the specified module.
|
||||
func GetLevel(module string) Level {
|
||||
return defaultBackend.GetLevel(module)
|
||||
}
|
400
vendor/github.com/op/go-logging/format.go
generated
vendored
Normal file
400
vendor/github.com/op/go-logging/format.go
generated
vendored
Normal file
|
@ -0,0 +1,400 @@
|
|||
// Copyright 2013, Örjan Persson. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package logging
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// TODO see Formatter interface in fmt/print.go
|
||||
// TODO try text/template, maybe it have enough performance
|
||||
// TODO other template systems?
|
||||
// TODO make it possible to specify formats per backend?
|
||||
type fmtVerb int
|
||||
|
||||
const (
|
||||
fmtVerbTime fmtVerb = iota
|
||||
fmtVerbLevel
|
||||
fmtVerbID
|
||||
fmtVerbPid
|
||||
fmtVerbProgram
|
||||
fmtVerbModule
|
||||
fmtVerbMessage
|
||||
fmtVerbLongfile
|
||||
fmtVerbShortfile
|
||||
fmtVerbLongpkg
|
||||
fmtVerbShortpkg
|
||||
fmtVerbLongfunc
|
||||
fmtVerbShortfunc
|
||||
fmtVerbCallpath
|
||||
fmtVerbLevelColor
|
||||
|
||||
// Keep last, there are no match for these below.
|
||||
fmtVerbUnknown
|
||||
fmtVerbStatic
|
||||
)
|
||||
|
||||
var fmtVerbs = []string{
|
||||
"time",
|
||||
"level",
|
||||
"id",
|
||||
"pid",
|
||||
"program",
|
||||
"module",
|
||||
"message",
|
||||
"longfile",
|
||||
"shortfile",
|
||||
"longpkg",
|
||||
"shortpkg",
|
||||
"longfunc",
|
||||
"shortfunc",
|
||||
"callpath",
|
||||
"color",
|
||||
}
|
||||
|
||||
const rfc3339Milli = "2006-01-02T15:04:05.999Z07:00"
|
||||
|
||||
var defaultVerbsLayout = []string{
|
||||
rfc3339Milli,
|
||||
"s",
|
||||
"d",
|
||||
"d",
|
||||
"s",
|
||||
"s",
|
||||
"s",
|
||||
"s",
|
||||
"s",
|
||||
"s",
|
||||
"s",
|
||||
"s",
|
||||
"s",
|
||||
"s",
|
||||
"",
|
||||
}
|
||||
|
||||
var (
|
||||
pid = os.Getpid()
|
||||
program = filepath.Base(os.Args[0])
|
||||
)
|
||||
|
||||
func getFmtVerbByName(name string) fmtVerb {
|
||||
for i, verb := range fmtVerbs {
|
||||
if name == verb {
|
||||
return fmtVerb(i)
|
||||
}
|
||||
}
|
||||
return fmtVerbUnknown
|
||||
}
|
||||
|
||||
// Formatter is the required interface for a custom log record formatter.
|
||||
type Formatter interface {
|
||||
Format(calldepth int, r *Record, w io.Writer) error
|
||||
}
|
||||
|
||||
// formatter is used by all backends unless otherwise overriden.
|
||||
var formatter struct {
|
||||
sync.RWMutex
|
||||
def Formatter
|
||||
}
|
||||
|
||||
func getFormatter() Formatter {
|
||||
formatter.RLock()
|
||||
defer formatter.RUnlock()
|
||||
return formatter.def
|
||||
}
|
||||
|
||||
var (
|
||||
// DefaultFormatter is the default formatter used and is only the message.
|
||||
DefaultFormatter = MustStringFormatter("%{message}")
|
||||
|
||||
// GlogFormatter mimics the glog format
|
||||
GlogFormatter = MustStringFormatter("%{level:.1s}%{time:0102 15:04:05.999999} %{pid} %{shortfile}] %{message}")
|
||||
)
|
||||
|
||||
// SetFormatter sets the default formatter for all new backends. A backend will
|
||||
// fetch this value once it is needed to format a record. Note that backends
|
||||
// will cache the formatter after the first point. For now, make sure to set
|
||||
// the formatter before logging.
|
||||
func SetFormatter(f Formatter) {
|
||||
formatter.Lock()
|
||||
defer formatter.Unlock()
|
||||
formatter.def = f
|
||||
}
|
||||
|
||||
var formatRe = regexp.MustCompile(`%{([a-z]+)(?::(.*?[^\\]))?}`)
|
||||
|
||||
type part struct {
|
||||
verb fmtVerb
|
||||
layout string
|
||||
}
|
||||
|
||||
// stringFormatter contains a list of parts which explains how to build the
|
||||
// formatted string passed on to the logging backend.
|
||||
type stringFormatter struct {
|
||||
parts []part
|
||||
}
|
||||
|
||||
// NewStringFormatter returns a new Formatter which outputs the log record as a
|
||||
// string based on the 'verbs' specified in the format string.
|
||||
//
|
||||
// The verbs:
|
||||
//
|
||||
// General:
|
||||
// %{id} Sequence number for log message (uint64).
|
||||
// %{pid} Process id (int)
|
||||
// %{time} Time when log occurred (time.Time)
|
||||
// %{level} Log level (Level)
|
||||
// %{module} Module (string)
|
||||
// %{program} Basename of os.Args[0] (string)
|
||||
// %{message} Message (string)
|
||||
// %{longfile} Full file name and line number: /a/b/c/d.go:23
|
||||
// %{shortfile} Final file name element and line number: d.go:23
|
||||
// %{callpath} Callpath like main.a.b.c...c "..." meaning recursive call
|
||||
// %{color} ANSI color based on log level
|
||||
//
|
||||
// For normal types, the output can be customized by using the 'verbs' defined
|
||||
// in the fmt package, eg. '%{id:04d}' to make the id output be '%04d' as the
|
||||
// format string.
|
||||
//
|
||||
// For time.Time, use the same layout as time.Format to change the time format
|
||||
// when output, eg "2006-01-02T15:04:05.999Z-07:00".
|
||||
//
|
||||
// For the 'color' verb, the output can be adjusted to either use bold colors,
|
||||
// i.e., '%{color:bold}' or to reset the ANSI attributes, i.e.,
|
||||
// '%{color:reset}' Note that if you use the color verb explicitly, be sure to
|
||||
// reset it or else the color state will persist past your log message. e.g.,
|
||||
// "%{color:bold}%{time:15:04:05} %{level:-8s}%{color:reset} %{message}" will
|
||||
// just colorize the time and level, leaving the message uncolored.
|
||||
//
|
||||
// Colors on Windows is unfortunately not supported right now and is currently
|
||||
// a no-op.
|
||||
//
|
||||
// There's also a couple of experimental 'verbs'. These are exposed to get
|
||||
// feedback and needs a bit of tinkering. Hence, they might change in the
|
||||
// future.
|
||||
//
|
||||
// Experimental:
|
||||
// %{longpkg} Full package path, eg. github.com/go-logging
|
||||
// %{shortpkg} Base package path, eg. go-logging
|
||||
// %{longfunc} Full function name, eg. littleEndian.PutUint32
|
||||
// %{shortfunc} Base function name, eg. PutUint32
|
||||
// %{callpath} Call function path, eg. main.a.b.c
|
||||
func NewStringFormatter(format string) (Formatter, error) {
|
||||
var fmter = &stringFormatter{}
|
||||
|
||||
// Find the boundaries of all %{vars}
|
||||
matches := formatRe.FindAllStringSubmatchIndex(format, -1)
|
||||
if matches == nil {
|
||||
return nil, errors.New("logger: invalid log format: " + format)
|
||||
}
|
||||
|
||||
// Collect all variables and static text for the format
|
||||
prev := 0
|
||||
for _, m := range matches {
|
||||
start, end := m[0], m[1]
|
||||
if start > prev {
|
||||
fmter.add(fmtVerbStatic, format[prev:start])
|
||||
}
|
||||
|
||||
name := format[m[2]:m[3]]
|
||||
verb := getFmtVerbByName(name)
|
||||
if verb == fmtVerbUnknown {
|
||||
return nil, errors.New("logger: unknown variable: " + name)
|
||||
}
|
||||
|
||||
// Handle layout customizations or use the default. If this is not for the
|
||||
// time or color formatting, we need to prefix with %.
|
||||
layout := defaultVerbsLayout[verb]
|
||||
if m[4] != -1 {
|
||||
layout = format[m[4]:m[5]]
|
||||
}
|
||||
if verb != fmtVerbTime && verb != fmtVerbLevelColor {
|
||||
layout = "%" + layout
|
||||
}
|
||||
|
||||
fmter.add(verb, layout)
|
||||
prev = end
|
||||
}
|
||||
end := format[prev:]
|
||||
if end != "" {
|
||||
fmter.add(fmtVerbStatic, end)
|
||||
}
|
||||
|
||||
// Make a test run to make sure we can format it correctly.
|
||||
t, err := time.Parse(time.RFC3339, "2010-02-04T21:00:57-08:00")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
r := &Record{
|
||||
Id: 12345,
|
||||
Time: t,
|
||||
Module: "logger",
|
||||
Args: []interface{}{"go"},
|
||||
fmt: "hello %s",
|
||||
}
|
||||
if err := fmter.Format(0, r, &bytes.Buffer{}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return fmter, nil
|
||||
}
|
||||
|
||||
// MustStringFormatter is equivalent to NewStringFormatter with a call to panic
|
||||
// on error.
|
||||
func MustStringFormatter(format string) Formatter {
|
||||
f, err := NewStringFormatter(format)
|
||||
if err != nil {
|
||||
panic("Failed to initialized string formatter: " + err.Error())
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *stringFormatter) add(verb fmtVerb, layout string) {
|
||||
f.parts = append(f.parts, part{verb, layout})
|
||||
}
|
||||
|
||||
func (f *stringFormatter) Format(calldepth int, r *Record, output io.Writer) error {
|
||||
for _, part := range f.parts {
|
||||
if part.verb == fmtVerbStatic {
|
||||
output.Write([]byte(part.layout))
|
||||
} else if part.verb == fmtVerbTime {
|
||||
output.Write([]byte(r.Time.Format(part.layout)))
|
||||
} else if part.verb == fmtVerbLevelColor {
|
||||
doFmtVerbLevelColor(part.layout, r.Level, output)
|
||||
} else {
|
||||
var v interface{}
|
||||
switch part.verb {
|
||||
case fmtVerbLevel:
|
||||
v = r.Level
|
||||
break
|
||||
case fmtVerbID:
|
||||
v = r.Id
|
||||
break
|
||||
case fmtVerbPid:
|
||||
v = pid
|
||||
break
|
||||
case fmtVerbProgram:
|
||||
v = program
|
||||
break
|
||||
case fmtVerbModule:
|
||||
v = r.Module
|
||||
break
|
||||
case fmtVerbMessage:
|
||||
v = r.Message()
|
||||
break
|
||||
case fmtVerbLongfile, fmtVerbShortfile:
|
||||
_, file, line, ok := runtime.Caller(calldepth + 1)
|
||||
if !ok {
|
||||
file = "???"
|
||||
line = 0
|
||||
} else if part.verb == fmtVerbShortfile {
|
||||
file = filepath.Base(file)
|
||||
}
|
||||
v = fmt.Sprintf("%s:%d", file, line)
|
||||
case fmtVerbLongfunc, fmtVerbShortfunc,
|
||||
fmtVerbLongpkg, fmtVerbShortpkg:
|
||||
// TODO cache pc
|
||||
v = "???"
|
||||
if pc, _, _, ok := runtime.Caller(calldepth + 1); ok {
|
||||
if f := runtime.FuncForPC(pc); f != nil {
|
||||
v = formatFuncName(part.verb, f.Name())
|
||||
}
|
||||
}
|
||||
case fmtVerbCallpath:
|
||||
v = formatCallpath(calldepth + 1)
|
||||
default:
|
||||
panic("unhandled format part")
|
||||
}
|
||||
fmt.Fprintf(output, part.layout, v)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// formatFuncName tries to extract certain part of the runtime formatted
|
||||
// function name to some pre-defined variation.
|
||||
//
|
||||
// This function is known to not work properly if the package path or name
|
||||
// contains a dot.
|
||||
func formatFuncName(v fmtVerb, f string) string {
|
||||
i := strings.LastIndex(f, "/")
|
||||
j := strings.Index(f[i+1:], ".")
|
||||
if j < 1 {
|
||||
return "???"
|
||||
}
|
||||
pkg, fun := f[:i+j+1], f[i+j+2:]
|
||||
switch v {
|
||||
case fmtVerbLongpkg:
|
||||
return pkg
|
||||
case fmtVerbShortpkg:
|
||||
return path.Base(pkg)
|
||||
case fmtVerbLongfunc:
|
||||
return fun
|
||||
case fmtVerbShortfunc:
|
||||
i = strings.LastIndex(fun, ".")
|
||||
return fun[i+1:]
|
||||
}
|
||||
panic("unexpected func formatter")
|
||||
}
|
||||
|
||||
func formatCallpath(calldepth int) string {
|
||||
v := ""
|
||||
callers := make([]uintptr, 64)
|
||||
n := runtime.Callers(calldepth+2, callers)
|
||||
oldPc := callers[n-1]
|
||||
|
||||
recursiveCall := false
|
||||
for i := n - 3; i >= 0; i-- {
|
||||
pc := callers[i]
|
||||
if oldPc == pc {
|
||||
recursiveCall = true
|
||||
continue
|
||||
}
|
||||
oldPc = pc
|
||||
if recursiveCall {
|
||||
recursiveCall = false
|
||||
v += ".."
|
||||
}
|
||||
if i < n-3 {
|
||||
v += "."
|
||||
}
|
||||
if f := runtime.FuncForPC(pc); f != nil {
|
||||
v += formatFuncName(fmtVerbShortfunc, f.Name())
|
||||
}
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// backendFormatter combines a backend with a specific formatter making it
|
||||
// possible to have different log formats for different backends.
|
||||
type backendFormatter struct {
|
||||
b Backend
|
||||
f Formatter
|
||||
}
|
||||
|
||||
// NewBackendFormatter creates a new backend which makes all records that
|
||||
// passes through it beeing formatted by the specific formatter.
|
||||
func NewBackendFormatter(b Backend, f Formatter) Backend {
|
||||
return &backendFormatter{b, f}
|
||||
}
|
||||
|
||||
// Log implements the Log function required by the Backend interface.
|
||||
func (bf *backendFormatter) Log(level Level, calldepth int, r *Record) error {
|
||||
// Make a shallow copy of the record and replace any formatter
|
||||
r2 := *r
|
||||
r2.formatter = bf.f
|
||||
return bf.b.Log(level, calldepth+1, &r2)
|
||||
}
|
128
vendor/github.com/op/go-logging/level.go
generated
vendored
Normal file
128
vendor/github.com/op/go-logging/level.go
generated
vendored
Normal file
|
@ -0,0 +1,128 @@
|
|||
// Copyright 2013, Örjan Persson. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package logging
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// ErrInvalidLogLevel is used when an invalid log level has been used.
|
||||
var ErrInvalidLogLevel = errors.New("logger: invalid log level")
|
||||
|
||||
// Level defines all available log levels for log messages.
|
||||
type Level int
|
||||
|
||||
// Log levels.
|
||||
const (
|
||||
CRITICAL Level = iota
|
||||
ERROR
|
||||
WARNING
|
||||
NOTICE
|
||||
INFO
|
||||
DEBUG
|
||||
)
|
||||
|
||||
var levelNames = []string{
|
||||
"CRITICAL",
|
||||
"ERROR",
|
||||
"WARNING",
|
||||
"NOTICE",
|
||||
"INFO",
|
||||
"DEBUG",
|
||||
}
|
||||
|
||||
// String returns the string representation of a logging level.
|
||||
func (p Level) String() string {
|
||||
return levelNames[p]
|
||||
}
|
||||
|
||||
// LogLevel returns the log level from a string representation.
|
||||
func LogLevel(level string) (Level, error) {
|
||||
for i, name := range levelNames {
|
||||
if strings.EqualFold(name, level) {
|
||||
return Level(i), nil
|
||||
}
|
||||
}
|
||||
return ERROR, ErrInvalidLogLevel
|
||||
}
|
||||
|
||||
// Leveled interface is the interface required to be able to add leveled
|
||||
// logging.
|
||||
type Leveled interface {
|
||||
GetLevel(string) Level
|
||||
SetLevel(Level, string)
|
||||
IsEnabledFor(Level, string) bool
|
||||
}
|
||||
|
||||
// LeveledBackend is a log backend with additional knobs for setting levels on
|
||||
// individual modules to different levels.
|
||||
type LeveledBackend interface {
|
||||
Backend
|
||||
Leveled
|
||||
}
|
||||
|
||||
type moduleLeveled struct {
|
||||
levels map[string]Level
|
||||
backend Backend
|
||||
formatter Formatter
|
||||
once sync.Once
|
||||
}
|
||||
|
||||
// AddModuleLevel wraps a log backend with knobs to have different log levels
|
||||
// for different modules.
|
||||
func AddModuleLevel(backend Backend) LeveledBackend {
|
||||
var leveled LeveledBackend
|
||||
var ok bool
|
||||
if leveled, ok = backend.(LeveledBackend); !ok {
|
||||
leveled = &moduleLeveled{
|
||||
levels: make(map[string]Level),
|
||||
backend: backend,
|
||||
}
|
||||
}
|
||||
return leveled
|
||||
}
|
||||
|
||||
// GetLevel returns the log level for the given module.
|
||||
func (l *moduleLeveled) GetLevel(module string) Level {
|
||||
level, exists := l.levels[module]
|
||||
if exists == false {
|
||||
level, exists = l.levels[""]
|
||||
// no configuration exists, default to debug
|
||||
if exists == false {
|
||||
level = DEBUG
|
||||
}
|
||||
}
|
||||
return level
|
||||
}
|
||||
|
||||
// SetLevel sets the log level for the given module.
|
||||
func (l *moduleLeveled) SetLevel(level Level, module string) {
|
||||
l.levels[module] = level
|
||||
}
|
||||
|
||||
// IsEnabledFor will return true if logging is enabled for the given module.
|
||||
func (l *moduleLeveled) IsEnabledFor(level Level, module string) bool {
|
||||
return level <= l.GetLevel(module)
|
||||
}
|
||||
|
||||
func (l *moduleLeveled) Log(level Level, calldepth int, rec *Record) (err error) {
|
||||
if l.IsEnabledFor(level, rec.Module) {
|
||||
// TODO get rid of traces of formatter here. BackendFormatter should be used.
|
||||
rec.formatter = l.getFormatterAndCacheCurrent()
|
||||
err = l.backend.Log(level, calldepth+1, rec)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (l *moduleLeveled) getFormatterAndCacheCurrent() Formatter {
|
||||
l.once.Do(func() {
|
||||
if l.formatter == nil {
|
||||
l.formatter = getFormatter()
|
||||
}
|
||||
})
|
||||
return l.formatter
|
||||
}
|
109
vendor/github.com/op/go-logging/log_nix.go
generated
vendored
Normal file
109
vendor/github.com/op/go-logging/log_nix.go
generated
vendored
Normal file
|
@ -0,0 +1,109 @@
|
|||
// +build !windows
|
||||
|
||||
// Copyright 2013, Örjan Persson. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package logging
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
)
|
||||
|
||||
type color int
|
||||
|
||||
const (
|
||||
ColorBlack = iota + 30
|
||||
ColorRed
|
||||
ColorGreen
|
||||
ColorYellow
|
||||
ColorBlue
|
||||
ColorMagenta
|
||||
ColorCyan
|
||||
ColorWhite
|
||||
)
|
||||
|
||||
var (
|
||||
colors = []string{
|
||||
CRITICAL: ColorSeq(ColorMagenta),
|
||||
ERROR: ColorSeq(ColorRed),
|
||||
WARNING: ColorSeq(ColorYellow),
|
||||
NOTICE: ColorSeq(ColorGreen),
|
||||
DEBUG: ColorSeq(ColorCyan),
|
||||
}
|
||||
boldcolors = []string{
|
||||
CRITICAL: ColorSeqBold(ColorMagenta),
|
||||
ERROR: ColorSeqBold(ColorRed),
|
||||
WARNING: ColorSeqBold(ColorYellow),
|
||||
NOTICE: ColorSeqBold(ColorGreen),
|
||||
DEBUG: ColorSeqBold(ColorCyan),
|
||||
}
|
||||
)
|
||||
|
||||
// LogBackend utilizes the standard log module.
|
||||
type LogBackend struct {
|
||||
Logger *log.Logger
|
||||
Color bool
|
||||
ColorConfig []string
|
||||
}
|
||||
|
||||
// NewLogBackend creates a new LogBackend.
|
||||
func NewLogBackend(out io.Writer, prefix string, flag int) *LogBackend {
|
||||
return &LogBackend{Logger: log.New(out, prefix, flag)}
|
||||
}
|
||||
|
||||
// Log implements the Backend interface.
|
||||
func (b *LogBackend) Log(level Level, calldepth int, rec *Record) error {
|
||||
if b.Color {
|
||||
col := colors[level]
|
||||
if len(b.ColorConfig) > int(level) && b.ColorConfig[level] != "" {
|
||||
col = b.ColorConfig[level]
|
||||
}
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
buf.Write([]byte(col))
|
||||
buf.Write([]byte(rec.Formatted(calldepth + 1)))
|
||||
buf.Write([]byte("\033[0m"))
|
||||
// For some reason, the Go logger arbitrarily decided "2" was the correct
|
||||
// call depth...
|
||||
return b.Logger.Output(calldepth+2, buf.String())
|
||||
}
|
||||
|
||||
return b.Logger.Output(calldepth+2, rec.Formatted(calldepth+1))
|
||||
}
|
||||
|
||||
// ConvertColors takes a list of ints representing colors for log levels and
|
||||
// converts them into strings for ANSI color formatting
|
||||
func ConvertColors(colors []int, bold bool) []string {
|
||||
converted := []string{}
|
||||
for _, i := range colors {
|
||||
if bold {
|
||||
converted = append(converted, ColorSeqBold(color(i)))
|
||||
} else {
|
||||
converted = append(converted, ColorSeq(color(i)))
|
||||
}
|
||||
}
|
||||
|
||||
return converted
|
||||
}
|
||||
|
||||
func ColorSeq(color color) string {
|
||||
return fmt.Sprintf("\033[%dm", int(color))
|
||||
}
|
||||
|
||||
func ColorSeqBold(color color) string {
|
||||
return fmt.Sprintf("\033[%d;1m", int(color))
|
||||
}
|
||||
|
||||
func doFmtVerbLevelColor(layout string, level Level, output io.Writer) {
|
||||
if layout == "bold" {
|
||||
output.Write([]byte(boldcolors[level]))
|
||||
} else if layout == "reset" {
|
||||
output.Write([]byte("\033[0m"))
|
||||
} else {
|
||||
output.Write([]byte(colors[level]))
|
||||
}
|
||||
}
|
107
vendor/github.com/op/go-logging/log_windows.go
generated
vendored
Normal file
107
vendor/github.com/op/go-logging/log_windows.go
generated
vendored
Normal file
|
@ -0,0 +1,107 @@
|
|||
// +build windows
|
||||
// Copyright 2013, Örjan Persson. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package logging
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"log"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
var (
|
||||
kernel32DLL = syscall.NewLazyDLL("kernel32.dll")
|
||||
setConsoleTextAttributeProc = kernel32DLL.NewProc("SetConsoleTextAttribute")
|
||||
)
|
||||
|
||||
// Character attributes
|
||||
// Note:
|
||||
// -- The attributes are combined to produce various colors (e.g., Blue + Green will create Cyan).
|
||||
// Clearing all foreground or background colors results in black; setting all creates white.
|
||||
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms682088(v=vs.85).aspx#_win32_character_attributes.
|
||||
const (
|
||||
fgBlack = 0x0000
|
||||
fgBlue = 0x0001
|
||||
fgGreen = 0x0002
|
||||
fgCyan = 0x0003
|
||||
fgRed = 0x0004
|
||||
fgMagenta = 0x0005
|
||||
fgYellow = 0x0006
|
||||
fgWhite = 0x0007
|
||||
fgIntensity = 0x0008
|
||||
fgMask = 0x000F
|
||||
)
|
||||
|
||||
var (
|
||||
colors = []uint16{
|
||||
INFO: fgWhite,
|
||||
CRITICAL: fgMagenta,
|
||||
ERROR: fgRed,
|
||||
WARNING: fgYellow,
|
||||
NOTICE: fgGreen,
|
||||
DEBUG: fgCyan,
|
||||
}
|
||||
boldcolors = []uint16{
|
||||
INFO: fgWhite | fgIntensity,
|
||||
CRITICAL: fgMagenta | fgIntensity,
|
||||
ERROR: fgRed | fgIntensity,
|
||||
WARNING: fgYellow | fgIntensity,
|
||||
NOTICE: fgGreen | fgIntensity,
|
||||
DEBUG: fgCyan | fgIntensity,
|
||||
}
|
||||
)
|
||||
|
||||
type file interface {
|
||||
Fd() uintptr
|
||||
}
|
||||
|
||||
// LogBackend utilizes the standard log module.
|
||||
type LogBackend struct {
|
||||
Logger *log.Logger
|
||||
Color bool
|
||||
|
||||
// f is set to a non-nil value if the underlying writer which logs writes to
|
||||
// implements the file interface. This makes us able to colorise the output.
|
||||
f file
|
||||
}
|
||||
|
||||
// NewLogBackend creates a new LogBackend.
|
||||
func NewLogBackend(out io.Writer, prefix string, flag int) *LogBackend {
|
||||
b := &LogBackend{Logger: log.New(out, prefix, flag)}
|
||||
|
||||
// Unfortunately, the API used only takes an io.Writer where the Windows API
|
||||
// need the actual fd to change colors.
|
||||
if f, ok := out.(file); ok {
|
||||
b.f = f
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *LogBackend) Log(level Level, calldepth int, rec *Record) error {
|
||||
if b.Color && b.f != nil {
|
||||
buf := &bytes.Buffer{}
|
||||
setConsoleTextAttribute(b.f, colors[level])
|
||||
buf.Write([]byte(rec.Formatted(calldepth + 1)))
|
||||
err := b.Logger.Output(calldepth+2, buf.String())
|
||||
setConsoleTextAttribute(b.f, fgWhite)
|
||||
return err
|
||||
}
|
||||
return b.Logger.Output(calldepth+2, rec.Formatted(calldepth+1))
|
||||
}
|
||||
|
||||
// setConsoleTextAttribute sets the attributes of characters written to the
|
||||
// console screen buffer by the WriteFile or WriteConsole function.
|
||||
// See http://msdn.microsoft.com/en-us/library/windows/desktop/ms686047(v=vs.85).aspx.
|
||||
func setConsoleTextAttribute(f file, attribute uint16) bool {
|
||||
ok, _, _ := setConsoleTextAttributeProc.Call(f.Fd(), uintptr(attribute), 0)
|
||||
return ok != 0
|
||||
}
|
||||
|
||||
func doFmtVerbLevelColor(layout string, level Level, output io.Writer) {
|
||||
// TODO not supported on Windows since the io.Writer here is actually a
|
||||
// bytes.Buffer.
|
||||
}
|
249
vendor/github.com/op/go-logging/logger.go
generated
vendored
Normal file
249
vendor/github.com/op/go-logging/logger.go
generated
vendored
Normal file
|
@ -0,0 +1,249 @@
|
|||
// Copyright 2013, Örjan Persson. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package logging implements a logging infrastructure for Go. It supports
|
||||
// different logging backends like syslog, file and memory. Multiple backends
|
||||
// can be utilized with different log levels per backend and logger.
|
||||
package logging
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Redactor is an interface for types that may contain sensitive information
|
||||
// (like passwords), which shouldn't be printed to the log. The idea was found
|
||||
// in relog as part of the vitness project.
|
||||
type Redactor interface {
|
||||
Redacted() interface{}
|
||||
}
|
||||
|
||||
// Redact returns a string of * having the same length as s.
|
||||
func Redact(s string) string {
|
||||
return strings.Repeat("*", len(s))
|
||||
}
|
||||
|
||||
var (
|
||||
// Sequence number is incremented and utilized for all log records created.
|
||||
sequenceNo uint64
|
||||
|
||||
// timeNow is a customizable for testing purposes.
|
||||
timeNow = time.Now
|
||||
)
|
||||
|
||||
// Record represents a log record and contains the timestamp when the record
|
||||
// was created, an increasing id, filename and line and finally the actual
|
||||
// formatted log line.
|
||||
type Record struct {
|
||||
Id uint64
|
||||
Time time.Time
|
||||
Module string
|
||||
Level Level
|
||||
Args []interface{}
|
||||
|
||||
// message is kept as a pointer to have shallow copies update this once
|
||||
// needed.
|
||||
message *string
|
||||
fmt string
|
||||
formatter Formatter
|
||||
formatted string
|
||||
}
|
||||
|
||||
// Formatted returns the formatted log record string.
|
||||
func (r *Record) Formatted(calldepth int) string {
|
||||
if r.formatted == "" {
|
||||
var buf bytes.Buffer
|
||||
r.formatter.Format(calldepth+1, r, &buf)
|
||||
r.formatted = buf.String()
|
||||
}
|
||||
return r.formatted
|
||||
}
|
||||
|
||||
// Message returns the log record message.
|
||||
func (r *Record) Message() string {
|
||||
if r.message == nil {
|
||||
// Redact the arguments that implements the Redactor interface
|
||||
for i, arg := range r.Args {
|
||||
if redactor, ok := arg.(Redactor); ok == true {
|
||||
r.Args[i] = redactor.Redacted()
|
||||
}
|
||||
}
|
||||
msg := fmt.Sprintf(r.fmt, r.Args...)
|
||||
r.message = &msg
|
||||
}
|
||||
return *r.message
|
||||
}
|
||||
|
||||
// Logger is the actual logger which creates log records based on the functions
|
||||
// called and passes them to the underlying logging backend.
|
||||
type Logger struct {
|
||||
Module string
|
||||
backend LeveledBackend
|
||||
haveBackend bool
|
||||
|
||||
// ExtraCallDepth can be used to add additional call depth when getting the
|
||||
// calling function. This is normally used when wrapping a logger.
|
||||
ExtraCalldepth int
|
||||
}
|
||||
|
||||
// SetBackend overrides any previously defined backend for this logger.
|
||||
func (l *Logger) SetBackend(backend LeveledBackend) {
|
||||
l.backend = backend
|
||||
l.haveBackend = true
|
||||
}
|
||||
|
||||
// TODO call NewLogger and remove MustGetLogger?
|
||||
|
||||
// GetLogger creates and returns a Logger object based on the module name.
|
||||
func GetLogger(module string) (*Logger, error) {
|
||||
return &Logger{Module: module}, nil
|
||||
}
|
||||
|
||||
// MustGetLogger is like GetLogger but panics if the logger can't be created.
|
||||
// It simplifies safe initialization of a global logger for eg. a package.
|
||||
func MustGetLogger(module string) *Logger {
|
||||
logger, err := GetLogger(module)
|
||||
if err != nil {
|
||||
panic("logger: " + module + ": " + err.Error())
|
||||
}
|
||||
return logger
|
||||
}
|
||||
|
||||
// Reset restores the internal state of the logging library.
|
||||
func Reset() {
|
||||
// TODO make a global Init() method to be less magic? or make it such that
|
||||
// if there's no backends at all configured, we could use some tricks to
|
||||
// automatically setup backends based if we have a TTY or not.
|
||||
sequenceNo = 0
|
||||
b := SetBackend(NewLogBackend(os.Stderr, "", log.LstdFlags))
|
||||
b.SetLevel(DEBUG, "")
|
||||
SetFormatter(DefaultFormatter)
|
||||
timeNow = time.Now
|
||||
}
|
||||
|
||||
// IsEnabledFor returns true if the logger is enabled for the given level.
|
||||
func (l *Logger) IsEnabledFor(level Level) bool {
|
||||
return defaultBackend.IsEnabledFor(level, l.Module)
|
||||
}
|
||||
|
||||
func (l *Logger) log(lvl Level, format string, args ...interface{}) {
|
||||
if !l.IsEnabledFor(lvl) {
|
||||
return
|
||||
}
|
||||
|
||||
// Create the logging record and pass it in to the backend
|
||||
record := &Record{
|
||||
Id: atomic.AddUint64(&sequenceNo, 1),
|
||||
Time: timeNow(),
|
||||
Module: l.Module,
|
||||
Level: lvl,
|
||||
fmt: format,
|
||||
Args: args,
|
||||
}
|
||||
|
||||
// TODO use channels to fan out the records to all backends?
|
||||
// TODO in case of errors, do something (tricky)
|
||||
|
||||
// calldepth=2 brings the stack up to the caller of the level
|
||||
// methods, Info(), Fatal(), etc.
|
||||
// ExtraCallDepth allows this to be extended further up the stack in case we
|
||||
// are wrapping these methods, eg. to expose them package level
|
||||
if l.haveBackend {
|
||||
l.backend.Log(lvl, 2+l.ExtraCalldepth, record)
|
||||
return
|
||||
}
|
||||
|
||||
defaultBackend.Log(lvl, 2+l.ExtraCalldepth, record)
|
||||
}
|
||||
|
||||
// Fatal is equivalent to l.Critical(fmt.Sprint()) followed by a call to os.Exit(1).
|
||||
func (l *Logger) Fatal(args ...interface{}) {
|
||||
s := fmt.Sprint(args...)
|
||||
l.log(CRITICAL, "%s", s)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Fatalf is equivalent to l.Critical followed by a call to os.Exit(1).
|
||||
func (l *Logger) Fatalf(format string, args ...interface{}) {
|
||||
l.log(CRITICAL, format, args...)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Panic is equivalent to l.Critical(fmt.Sprint()) followed by a call to panic().
|
||||
func (l *Logger) Panic(args ...interface{}) {
|
||||
s := fmt.Sprint(args...)
|
||||
l.log(CRITICAL, "%s", s)
|
||||
panic(s)
|
||||
}
|
||||
|
||||
// Panicf is equivalent to l.Critical followed by a call to panic().
|
||||
func (l *Logger) Panicf(format string, args ...interface{}) {
|
||||
s := fmt.Sprintf(format, args...)
|
||||
l.log(CRITICAL, "%s", s)
|
||||
panic(s)
|
||||
}
|
||||
|
||||
// Critical logs a message using CRITICAL as log level.
|
||||
func (l *Logger) Critical(format string, args ...interface{}) {
|
||||
l.log(CRITICAL, format, args...)
|
||||
}
|
||||
|
||||
// Error logs a message using ERROR as log level.
|
||||
func (l *Logger) Error(format string, args ...interface{}) {
|
||||
l.log(ERROR, format, args...)
|
||||
}
|
||||
|
||||
// Errorf logs a message using ERROR as log level.
|
||||
func (l *Logger) Errorf(format string, args ...interface{}) {
|
||||
l.log(ERROR, format, args...)
|
||||
}
|
||||
|
||||
// Warning logs a message using WARNING as log level.
|
||||
func (l *Logger) Warning(format string, args ...interface{}) {
|
||||
l.log(WARNING, format, args...)
|
||||
}
|
||||
|
||||
// Warningf logs a message using WARNING as log level.
|
||||
func (l *Logger) Warningf(format string, args ...interface{}) {
|
||||
l.log(WARNING, format, args...)
|
||||
}
|
||||
|
||||
// Notice logs a message using NOTICE as log level.
|
||||
func (l *Logger) Notice(format string, args ...interface{}) {
|
||||
l.log(NOTICE, format, args...)
|
||||
}
|
||||
|
||||
// Noticef logs a message using NOTICE as log level.
|
||||
func (l *Logger) Noticef(format string, args ...interface{}) {
|
||||
l.log(NOTICE, format, args...)
|
||||
}
|
||||
|
||||
// Info logs a message using INFO as log level.
|
||||
func (l *Logger) Info(format string, args ...interface{}) {
|
||||
l.log(INFO, format, args...)
|
||||
}
|
||||
|
||||
// Infof logs a message using INFO as log level.
|
||||
func (l *Logger) Infof(format string, args ...interface{}) {
|
||||
l.log(INFO, format, args...)
|
||||
}
|
||||
|
||||
// Debug logs a message using DEBUG as log level.
|
||||
func (l *Logger) Debug(format string, args ...interface{}) {
|
||||
l.log(DEBUG, format, args...)
|
||||
}
|
||||
|
||||
// Debugf logs a message using DEBUG as log level.
|
||||
func (l *Logger) Debugf(format string, args ...interface{}) {
|
||||
l.log(DEBUG, format, args...)
|
||||
}
|
||||
|
||||
func init() {
|
||||
Reset()
|
||||
}
|
237
vendor/github.com/op/go-logging/memory.go
generated
vendored
Normal file
237
vendor/github.com/op/go-logging/memory.go
generated
vendored
Normal file
|
@ -0,0 +1,237 @@
|
|||
// Copyright 2013, Örjan Persson. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !appengine
|
||||
|
||||
package logging
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// TODO pick one of the memory backends and stick with it or share interface.
|
||||
|
||||
// InitForTesting is a convenient method when using logging in a test. Once
|
||||
// called, the time will be frozen to January 1, 1970 UTC.
|
||||
func InitForTesting(level Level) *MemoryBackend {
|
||||
Reset()
|
||||
|
||||
memoryBackend := NewMemoryBackend(10240)
|
||||
|
||||
leveledBackend := AddModuleLevel(memoryBackend)
|
||||
leveledBackend.SetLevel(level, "")
|
||||
SetBackend(leveledBackend)
|
||||
|
||||
timeNow = func() time.Time {
|
||||
return time.Unix(0, 0).UTC()
|
||||
}
|
||||
return memoryBackend
|
||||
}
|
||||
|
||||
// Node is a record node pointing to an optional next node.
|
||||
type node struct {
|
||||
next *node
|
||||
Record *Record
|
||||
}
|
||||
|
||||
// Next returns the next record node. If there's no node available, it will
|
||||
// return nil.
|
||||
func (n *node) Next() *node {
|
||||
return n.next
|
||||
}
|
||||
|
||||
// MemoryBackend is a simple memory based logging backend that will not produce
|
||||
// any output but merly keep records, up to the given size, in memory.
|
||||
type MemoryBackend struct {
|
||||
size int32
|
||||
maxSize int32
|
||||
head, tail unsafe.Pointer
|
||||
}
|
||||
|
||||
// NewMemoryBackend creates a simple in-memory logging backend.
|
||||
func NewMemoryBackend(size int) *MemoryBackend {
|
||||
return &MemoryBackend{maxSize: int32(size)}
|
||||
}
|
||||
|
||||
// Log implements the Log method required by Backend.
|
||||
func (b *MemoryBackend) Log(level Level, calldepth int, rec *Record) error {
|
||||
var size int32
|
||||
|
||||
n := &node{Record: rec}
|
||||
np := unsafe.Pointer(n)
|
||||
|
||||
// Add the record to the tail. If there's no records available, tail and
|
||||
// head will both be nil. When we successfully set the tail and the previous
|
||||
// value was nil, it's safe to set the head to the current value too.
|
||||
for {
|
||||
tailp := b.tail
|
||||
swapped := atomic.CompareAndSwapPointer(
|
||||
&b.tail,
|
||||
tailp,
|
||||
np,
|
||||
)
|
||||
if swapped == true {
|
||||
if tailp == nil {
|
||||
b.head = np
|
||||
} else {
|
||||
(*node)(tailp).next = n
|
||||
}
|
||||
size = atomic.AddInt32(&b.size, 1)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Since one record was added, we might have overflowed the list. Remove
|
||||
// a record if that is the case. The size will fluctate a bit, but
|
||||
// eventual consistent.
|
||||
if b.maxSize > 0 && size > b.maxSize {
|
||||
for {
|
||||
headp := b.head
|
||||
head := (*node)(b.head)
|
||||
if head.next == nil {
|
||||
break
|
||||
}
|
||||
swapped := atomic.CompareAndSwapPointer(
|
||||
&b.head,
|
||||
headp,
|
||||
unsafe.Pointer(head.next),
|
||||
)
|
||||
if swapped == true {
|
||||
atomic.AddInt32(&b.size, -1)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Head returns the oldest record node kept in memory. It can be used to
|
||||
// iterate over records, one by one, up to the last record.
|
||||
//
|
||||
// Note: new records can get added while iterating. Hence the number of records
|
||||
// iterated over might be larger than the maximum size.
|
||||
func (b *MemoryBackend) Head() *node {
|
||||
return (*node)(b.head)
|
||||
}
|
||||
|
||||
type event int
|
||||
|
||||
const (
|
||||
eventFlush event = iota
|
||||
eventStop
|
||||
)
|
||||
|
||||
// ChannelMemoryBackend is very similar to the MemoryBackend, except that it
|
||||
// internally utilizes a channel.
|
||||
type ChannelMemoryBackend struct {
|
||||
maxSize int
|
||||
size int
|
||||
incoming chan *Record
|
||||
events chan event
|
||||
mu sync.Mutex
|
||||
running bool
|
||||
flushWg sync.WaitGroup
|
||||
stopWg sync.WaitGroup
|
||||
head, tail *node
|
||||
}
|
||||
|
||||
// NewChannelMemoryBackend creates a simple in-memory logging backend which
|
||||
// utilizes a go channel for communication.
|
||||
//
|
||||
// Start will automatically be called by this function.
|
||||
func NewChannelMemoryBackend(size int) *ChannelMemoryBackend {
|
||||
backend := &ChannelMemoryBackend{
|
||||
maxSize: size,
|
||||
incoming: make(chan *Record, 1024),
|
||||
events: make(chan event),
|
||||
}
|
||||
backend.Start()
|
||||
return backend
|
||||
}
|
||||
|
||||
// Start launches the internal goroutine which starts processing data from the
|
||||
// input channel.
|
||||
func (b *ChannelMemoryBackend) Start() {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
|
||||
// Launch the goroutine unless it's already running.
|
||||
if b.running != true {
|
||||
b.running = true
|
||||
b.stopWg.Add(1)
|
||||
go b.process()
|
||||
}
|
||||
}
|
||||
|
||||
func (b *ChannelMemoryBackend) process() {
|
||||
defer b.stopWg.Done()
|
||||
for {
|
||||
select {
|
||||
case rec := <-b.incoming:
|
||||
b.insertRecord(rec)
|
||||
case e := <-b.events:
|
||||
switch e {
|
||||
case eventStop:
|
||||
return
|
||||
case eventFlush:
|
||||
for len(b.incoming) > 0 {
|
||||
b.insertRecord(<-b.incoming)
|
||||
}
|
||||
b.flushWg.Done()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (b *ChannelMemoryBackend) insertRecord(rec *Record) {
|
||||
prev := b.tail
|
||||
b.tail = &node{Record: rec}
|
||||
if prev == nil {
|
||||
b.head = b.tail
|
||||
} else {
|
||||
prev.next = b.tail
|
||||
}
|
||||
|
||||
if b.maxSize > 0 && b.size >= b.maxSize {
|
||||
b.head = b.head.next
|
||||
} else {
|
||||
b.size++
|
||||
}
|
||||
}
|
||||
|
||||
// Flush waits until all records in the buffered channel have been processed.
|
||||
func (b *ChannelMemoryBackend) Flush() {
|
||||
b.flushWg.Add(1)
|
||||
b.events <- eventFlush
|
||||
b.flushWg.Wait()
|
||||
}
|
||||
|
||||
// Stop signals the internal goroutine to exit and waits until it have.
|
||||
func (b *ChannelMemoryBackend) Stop() {
|
||||
b.mu.Lock()
|
||||
if b.running == true {
|
||||
b.running = false
|
||||
b.events <- eventStop
|
||||
}
|
||||
b.mu.Unlock()
|
||||
b.stopWg.Wait()
|
||||
}
|
||||
|
||||
// Log implements the Log method required by Backend.
|
||||
func (b *ChannelMemoryBackend) Log(level Level, calldepth int, rec *Record) error {
|
||||
b.incoming <- rec
|
||||
return nil
|
||||
}
|
||||
|
||||
// Head returns the oldest record node kept in memory. It can be used to
|
||||
// iterate over records, one by one, up to the last record.
|
||||
//
|
||||
// Note: new records can get added while iterating. Hence the number of records
|
||||
// iterated over might be larger than the maximum size.
|
||||
func (b *ChannelMemoryBackend) Head() *node {
|
||||
return b.head
|
||||
}
|
65
vendor/github.com/op/go-logging/multi.go
generated
vendored
Normal file
65
vendor/github.com/op/go-logging/multi.go
generated
vendored
Normal file
|
@ -0,0 +1,65 @@
|
|||
// Copyright 2013, Örjan Persson. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package logging
|
||||
|
||||
// TODO remove Level stuff from the multi logger. Do one thing.
|
||||
|
||||
// multiLogger is a log multiplexer which can be used to utilize multiple log
|
||||
// backends at once.
|
||||
type multiLogger struct {
|
||||
backends []LeveledBackend
|
||||
}
|
||||
|
||||
// MultiLogger creates a logger which contain multiple loggers.
|
||||
func MultiLogger(backends ...Backend) LeveledBackend {
|
||||
var leveledBackends []LeveledBackend
|
||||
for _, backend := range backends {
|
||||
leveledBackends = append(leveledBackends, AddModuleLevel(backend))
|
||||
}
|
||||
return &multiLogger{leveledBackends}
|
||||
}
|
||||
|
||||
// Log passes the log record to all backends.
|
||||
func (b *multiLogger) Log(level Level, calldepth int, rec *Record) (err error) {
|
||||
for _, backend := range b.backends {
|
||||
if backend.IsEnabledFor(level, rec.Module) {
|
||||
// Shallow copy of the record for the formatted cache on Record and get the
|
||||
// record formatter from the backend.
|
||||
r2 := *rec
|
||||
if e := backend.Log(level, calldepth+1, &r2); e != nil {
|
||||
err = e
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetLevel returns the highest level enabled by all backends.
|
||||
func (b *multiLogger) GetLevel(module string) Level {
|
||||
var level Level
|
||||
for _, backend := range b.backends {
|
||||
if backendLevel := backend.GetLevel(module); backendLevel > level {
|
||||
level = backendLevel
|
||||
}
|
||||
}
|
||||
return level
|
||||
}
|
||||
|
||||
// SetLevel propagates the same level to all backends.
|
||||
func (b *multiLogger) SetLevel(level Level, module string) {
|
||||
for _, backend := range b.backends {
|
||||
backend.SetLevel(level, module)
|
||||
}
|
||||
}
|
||||
|
||||
// IsEnabledFor returns true if any of the backends are enabled for it.
|
||||
func (b *multiLogger) IsEnabledFor(level Level, module string) bool {
|
||||
for _, backend := range b.backends {
|
||||
if backend.IsEnabledFor(level, module) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
53
vendor/github.com/op/go-logging/syslog.go
generated
vendored
Normal file
53
vendor/github.com/op/go-logging/syslog.go
generated
vendored
Normal file
|
@ -0,0 +1,53 @@
|
|||
// Copyright 2013, Örjan Persson. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//+build !windows,!plan9
|
||||
|
||||
package logging
|
||||
|
||||
import "log/syslog"
|
||||
|
||||
// SyslogBackend is a simple logger to syslog backend. It automatically maps
|
||||
// the internal log levels to appropriate syslog log levels.
|
||||
type SyslogBackend struct {
|
||||
Writer *syslog.Writer
|
||||
}
|
||||
|
||||
// NewSyslogBackend connects to the syslog daemon using UNIX sockets with the
|
||||
// given prefix. If prefix is not given, the prefix will be derived from the
|
||||
// launched command.
|
||||
func NewSyslogBackend(prefix string) (b *SyslogBackend, err error) {
|
||||
var w *syslog.Writer
|
||||
w, err = syslog.New(syslog.LOG_CRIT, prefix)
|
||||
return &SyslogBackend{w}, err
|
||||
}
|
||||
|
||||
// NewSyslogBackendPriority is the same as NewSyslogBackend, but with custom
|
||||
// syslog priority, like syslog.LOG_LOCAL3|syslog.LOG_DEBUG etc.
|
||||
func NewSyslogBackendPriority(prefix string, priority syslog.Priority) (b *SyslogBackend, err error) {
|
||||
var w *syslog.Writer
|
||||
w, err = syslog.New(priority, prefix)
|
||||
return &SyslogBackend{w}, err
|
||||
}
|
||||
|
||||
// Log implements the Backend interface.
|
||||
func (b *SyslogBackend) Log(level Level, calldepth int, rec *Record) error {
|
||||
line := rec.Formatted(calldepth + 1)
|
||||
switch level {
|
||||
case CRITICAL:
|
||||
return b.Writer.Crit(line)
|
||||
case ERROR:
|
||||
return b.Writer.Err(line)
|
||||
case WARNING:
|
||||
return b.Writer.Warning(line)
|
||||
case NOTICE:
|
||||
return b.Writer.Notice(line)
|
||||
case INFO:
|
||||
return b.Writer.Info(line)
|
||||
case DEBUG:
|
||||
return b.Writer.Debug(line)
|
||||
default:
|
||||
}
|
||||
panic("unhandled log level")
|
||||
}
|
28
vendor/github.com/op/go-logging/syslog_fallback.go
generated
vendored
Normal file
28
vendor/github.com/op/go-logging/syslog_fallback.go
generated
vendored
Normal file
|
@ -0,0 +1,28 @@
|
|||
// Copyright 2013, Örjan Persson. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//+build windows plan9
|
||||
|
||||
package logging
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type Priority int
|
||||
|
||||
type SyslogBackend struct {
|
||||
}
|
||||
|
||||
func NewSyslogBackend(prefix string) (b *SyslogBackend, err error) {
|
||||
return nil, fmt.Errorf("Platform does not support syslog")
|
||||
}
|
||||
|
||||
func NewSyslogBackendPriority(prefix string, priority Priority) (b *SyslogBackend, err error) {
|
||||
return nil, fmt.Errorf("Platform does not support syslog")
|
||||
}
|
||||
|
||||
func (b *SyslogBackend) Log(level Level, calldepth int, rec *Record) error {
|
||||
return fmt.Errorf("Platform does not support syslog")
|
||||
}
|
Loading…
Reference in a new issue