mirror of
https://github.com/kevin-DL/revel-cmd.git
synced 2026-01-19 13:45:19 +00:00
Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5f558aca4e | ||
|
|
facfe0ecaf | ||
|
|
ee53d2f399 | ||
|
|
3c48e1f83e | ||
|
|
19fb7d6776 | ||
|
|
1e7b5322f5 | ||
|
|
205c652f07 | ||
|
|
3de8b8c03c | ||
|
|
4a877b2a8a | ||
|
|
a0bafdc2a7 | ||
|
|
cdef0b75a8 | ||
|
|
e0d3f83ca8 | ||
|
|
87c9e56322 | ||
|
|
554e62574d | ||
|
|
32a3f08dde | ||
|
|
e6e1cad795 | ||
|
|
5e36cb1025 | ||
|
|
8c21a56302 | ||
|
|
031fde6009 | ||
|
|
09ca80add8 | ||
|
|
2d6c2eefa4 | ||
|
|
644d6e12bd |
@@ -1,11 +1,13 @@
|
|||||||
{
|
{
|
||||||
"GOLANG": {
|
"GOLANG": {
|
||||||
"ABC":[25, 35, 50, 70],
|
"ABC":[25, 35, 50, 70],
|
||||||
"BLOCK_NESTING":[5, 6, 7, 8],
|
"ARITY":[5,6,7,8],
|
||||||
"CYCLO":[20, 30, 45, 60],
|
"BLOCK_NESTING":[7, 9, 11, 13],
|
||||||
"TOO_MANY_IVARS": [15, 18, 20, 25],
|
"CYCLO":[20, 30, 45, 60],
|
||||||
|
"TOO_MANY_IVARS": [20, 25, 40, 45],
|
||||||
"TOO_MANY_FUNCTIONS": [20, 30, 40, 50],
|
"TOO_MANY_FUNCTIONS": [20, 30, 40, 50],
|
||||||
"TOTAL_COMPLEXITY": [150, 250, 400, 500],
|
"TOTAL_COMPLEXITY": [150, 250, 400, 500],
|
||||||
"LOC": [50, 75, 90, 120]
|
"LOC": [100, 175, 250, 320],
|
||||||
|
"TOTAL_LOC": [300, 400, 500, 600]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
11
.travis.yml
11
.travis.yml
@@ -1,10 +1,10 @@
|
|||||||
language: go
|
language: go
|
||||||
|
|
||||||
go:
|
go:
|
||||||
- "1.8.7"
|
- "1.8.x"
|
||||||
- "1.9"
|
- "1.9.x"
|
||||||
- "1.10"
|
- "1.10.x"
|
||||||
- "1.11"
|
- "1.11.x"
|
||||||
- "tip"
|
- "tip"
|
||||||
|
|
||||||
os:
|
os:
|
||||||
@@ -26,6 +26,8 @@ install:
|
|||||||
- export REVEL_BRANCH="develop"
|
- export REVEL_BRANCH="develop"
|
||||||
- 'if [[ "$TRAVIS_BRANCH" == "master" ]]; then export REVEL_BRANCH="master"; fi'
|
- 'if [[ "$TRAVIS_BRANCH" == "master" ]]; then export REVEL_BRANCH="master"; fi'
|
||||||
- 'echo "Travis branch: $TRAVIS_BRANCH, Revel dependency branch: $REVEL_BRANCH"'
|
- 'echo "Travis branch: $TRAVIS_BRANCH, Revel dependency branch: $REVEL_BRANCH"'
|
||||||
|
- git clone -b $REVEL_BRANCH git://github.com/revel/revel ../revel/
|
||||||
|
- git clone -b $REVEL_BRANCH git://github.com/revel/modules ../modules/
|
||||||
- go get -t -v github.com/revel/cmd/revel
|
- go get -t -v github.com/revel/cmd/revel
|
||||||
- go get -u github.com/golang/dep/cmd/dep
|
- go get -u github.com/golang/dep/cmd/dep
|
||||||
- echo $GOPATH
|
- echo $GOPATH
|
||||||
@@ -64,3 +66,4 @@ script:
|
|||||||
matrix:
|
matrix:
|
||||||
allow_failures:
|
allow_failures:
|
||||||
- go: tip
|
- go: tip
|
||||||
|
- os: windows
|
||||||
|
|||||||
28
README.md
28
README.md
@@ -21,3 +21,31 @@ Create a new application
|
|||||||
```commandline
|
```commandline
|
||||||
revel new my/app
|
revel new my/app
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Community
|
||||||
|
|
||||||
|
* [Gitter](https://gitter.im/revel/community)
|
||||||
|
* [StackOverflow](http://stackoverflow.com/questions/tagged/revel)
|
||||||
|
|
||||||
|
|
||||||
|
## Learn More
|
||||||
|
|
||||||
|
* [Manual, Samples, Godocs, etc](http://revel.github.io)
|
||||||
|
* [Apps using Revel](https://github.com/revel/revel/wiki/Apps-in-the-Wild)
|
||||||
|
* [Articles Featuring Revel](https://github.com/revel/revel/wiki/Articles)
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
* [Contributing Code Guidelines](https://github.com/revel/revel/blob/master/CONTRIBUTING.md)
|
||||||
|
* [Revel Contributors](https://github.com/revel/revel/graphs/contributors)
|
||||||
|
|
||||||
|
## Contributors
|
||||||
|
|
||||||
|
[](https://sourcerer.io/fame/notzippy/revel/cmd/links/0)
|
||||||
|
[](https://sourcerer.io/fame/notzippy/revel/cmd/links/1)
|
||||||
|
[](https://sourcerer.io/fame/notzippy/revel/cmd/links/2)
|
||||||
|
[](https://sourcerer.io/fame/notzippy/revel/cmd/links/3)
|
||||||
|
[](https://sourcerer.io/fame/notzippy/revel/cmd/links/4)
|
||||||
|
[](https://sourcerer.io/fame/notzippy/revel/cmd/links/5)
|
||||||
|
[](https://sourcerer.io/fame/notzippy/revel/cmd/links/6)
|
||||||
|
[](https://sourcerer.io/fame/notzippy/revel/cmd/links/7)
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import (
|
|||||||
|
|
||||||
"github.com/revel/cmd/model"
|
"github.com/revel/cmd/model"
|
||||||
"github.com/revel/cmd/utils"
|
"github.com/revel/cmd/utils"
|
||||||
"log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// App contains the configuration for running a Revel app. (Not for the app itself)
|
// App contains the configuration for running a Revel app. (Not for the app itself)
|
||||||
@@ -61,7 +60,7 @@ func NewAppCmd(binPath string, port int, runMode string, paths *model.RevelConta
|
|||||||
|
|
||||||
// Start the app server, and wait until it is ready to serve requests.
|
// Start the app server, and wait until it is ready to serve requests.
|
||||||
func (cmd AppCmd) Start(c *model.CommandConfig) error {
|
func (cmd AppCmd) Start(c *model.CommandConfig) error {
|
||||||
listeningWriter := &startupListeningWriter{os.Stdout, make(chan bool), c}
|
listeningWriter := &startupListeningWriter{os.Stdout, make(chan bool), c, &bytes.Buffer{}}
|
||||||
cmd.Stdout = listeningWriter
|
cmd.Stdout = listeningWriter
|
||||||
utils.Logger.Info("Exec app:", "path", cmd.Path, "args", cmd.Args, "dir", cmd.Dir, "env", cmd.Env)
|
utils.Logger.Info("Exec app:", "path", cmd.Path, "args", cmd.Args, "dir", cmd.Dir, "env", cmd.Env)
|
||||||
utils.CmdInit(cmd.Cmd, c.AppPath)
|
utils.CmdInit(cmd.Cmd, c.AppPath)
|
||||||
@@ -71,8 +70,11 @@ func (cmd AppCmd) Start(c *model.CommandConfig) error {
|
|||||||
|
|
||||||
select {
|
select {
|
||||||
case exitState := <-cmd.waitChan():
|
case exitState := <-cmd.waitChan():
|
||||||
println("Revel proxy is listening, point your browser to :", c.Run.Port)
|
fmt.Println("Startup failure view previous messages, \n Proxy is listening :", c.Run.Port)
|
||||||
return errors.New("revel/harness: app died reason: " + exitState)
|
err := utils.NewError("","Revel Run Error", "starting your application there was an exception. See terminal output, " + exitState,"")
|
||||||
|
// TODO pretiffy command line output
|
||||||
|
// err.MetaError = listeningWriter.getLastOutput()
|
||||||
|
return err
|
||||||
|
|
||||||
case <-time.After(60 * time.Second):
|
case <-time.After(60 * time.Second):
|
||||||
println("Revel proxy is listening, point your browser to :", c.Run.Port)
|
println("Revel proxy is listening, point your browser to :", c.Run.Port)
|
||||||
@@ -89,7 +91,7 @@ func (cmd AppCmd) Start(c *model.CommandConfig) error {
|
|||||||
|
|
||||||
// Run the app server inline. Never returns.
|
// Run the app server inline. Never returns.
|
||||||
func (cmd AppCmd) Run() {
|
func (cmd AppCmd) Run() {
|
||||||
log.Println("Exec app:", "path", cmd.Path, "args", cmd.Args)
|
utils.Logger.Info("Exec app:", "path", cmd.Path, "args", cmd.Args)
|
||||||
if err := cmd.Cmd.Run(); err != nil {
|
if err := cmd.Cmd.Run(); err != nil {
|
||||||
utils.Logger.Fatal("Error running:", "error", err)
|
utils.Logger.Fatal("Error running:", "error", err)
|
||||||
}
|
}
|
||||||
@@ -97,12 +99,44 @@ func (cmd AppCmd) Run() {
|
|||||||
|
|
||||||
// Kill terminates the app server if it's running.
|
// Kill terminates the app server if it's running.
|
||||||
func (cmd AppCmd) Kill() {
|
func (cmd AppCmd) Kill() {
|
||||||
|
|
||||||
if cmd.Cmd != nil && (cmd.ProcessState == nil || !cmd.ProcessState.Exited()) {
|
if cmd.Cmd != nil && (cmd.ProcessState == nil || !cmd.ProcessState.Exited()) {
|
||||||
|
// Send an interrupt signal to allow for a graceful shutdown
|
||||||
utils.Logger.Info("Killing revel server pid", "pid", cmd.Process.Pid)
|
utils.Logger.Info("Killing revel server pid", "pid", cmd.Process.Pid)
|
||||||
err := cmd.Process.Kill()
|
err := cmd.Process.Signal(os.Interrupt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Logger.Fatal("Failed to kill revel server:", "error", err)
|
utils.Logger.Fatal("Failed to kill revel server:", "error", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Wait for the shutdown
|
||||||
|
ch := make(chan bool, 1)
|
||||||
|
go func() {
|
||||||
|
s, err := cmd.Process.Wait()
|
||||||
|
defer func() {
|
||||||
|
ch <- true
|
||||||
|
}()
|
||||||
|
if err != nil {
|
||||||
|
utils.Logger.Info("Wait failed for process ", "error", err)
|
||||||
|
}
|
||||||
|
if s != nil {
|
||||||
|
utils.Logger.Info("Revel App exited", "state", s.String())
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Use a timer to ensure that the process exits
|
||||||
|
utils.Logger.Info("Waiting to exit")
|
||||||
|
select {
|
||||||
|
case <-ch:
|
||||||
|
return
|
||||||
|
case <-time.After(60 * time.Second):
|
||||||
|
// Kill the process
|
||||||
|
utils.Logger.Error(
|
||||||
|
"Revel app failed to exit in 60 seconds - killing.",
|
||||||
|
"processid", cmd.Process.Pid,
|
||||||
|
"killerror", cmd.Process.Kill())
|
||||||
|
}
|
||||||
|
|
||||||
|
utils.Logger.Info("Done Waiting to exit")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,8 +163,10 @@ type startupListeningWriter struct {
|
|||||||
dest io.Writer
|
dest io.Writer
|
||||||
notifyReady chan bool
|
notifyReady chan bool
|
||||||
c *model.CommandConfig
|
c *model.CommandConfig
|
||||||
|
buffer *bytes.Buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Writes to this output stream
|
||||||
func (w *startupListeningWriter) Write(p []byte) (int, error) {
|
func (w *startupListeningWriter) Write(p []byte) (int, error) {
|
||||||
if w.notifyReady != nil && bytes.Contains(p, []byte("Revel engine is listening on")) {
|
if w.notifyReady != nil && bytes.Contains(p, []byte("Revel engine is listening on")) {
|
||||||
w.notifyReady <- true
|
w.notifyReady <- true
|
||||||
@@ -142,5 +178,15 @@ func (w *startupListeningWriter) Write(p []byte) (int, error) {
|
|||||||
w.notifyReady = nil
|
w.notifyReady = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if w.notifyReady!=nil {
|
||||||
|
w.buffer.Write(p)
|
||||||
|
}
|
||||||
return w.dest.Write(p)
|
return w.dest.Write(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns the cleaned output from the response
|
||||||
|
// TODO clean the response more
|
||||||
|
func (w *startupListeningWriter) getLastOutput() string {
|
||||||
|
return w.buffer.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -231,6 +231,9 @@ func (h *Harness) Refresh() (err *utils.Error) {
|
|||||||
h.app.Port = h.port
|
h.app.Port = h.port
|
||||||
if err2 := h.app.Cmd(h.runMode).Start(h.config); err2 != nil {
|
if err2 := h.app.Cmd(h.runMode).Start(h.config); err2 != nil {
|
||||||
utils.Logger.Error("Could not start application", "error", err2)
|
utils.Logger.Error("Could not start application", "error", err2)
|
||||||
|
if err,k :=err2.(*utils.Error);k {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return &utils.Error{
|
return &utils.Error{
|
||||||
Title: "App failed to start up",
|
Title: "App failed to start up",
|
||||||
Description: err2.Error(),
|
Description: err2.Error(),
|
||||||
|
|||||||
174
logger/composite_multihandler.go
Normal file
174
logger/composite_multihandler.go
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
package logger
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/mattn/go-colorable"
|
||||||
|
"gopkg.in/natefinch/lumberjack.v2"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CompositeMultiHandler struct {
|
||||||
|
DebugHandler LogHandler
|
||||||
|
InfoHandler LogHandler
|
||||||
|
WarnHandler LogHandler
|
||||||
|
ErrorHandler LogHandler
|
||||||
|
CriticalHandler LogHandler
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCompositeMultiHandler() (*CompositeMultiHandler, LogHandler) {
|
||||||
|
cw := &CompositeMultiHandler{}
|
||||||
|
return cw, cw
|
||||||
|
}
|
||||||
|
func (h *CompositeMultiHandler) Log(r *Record) (err error) {
|
||||||
|
|
||||||
|
var handler LogHandler
|
||||||
|
|
||||||
|
switch r.Level {
|
||||||
|
case LvlInfo:
|
||||||
|
handler = h.InfoHandler
|
||||||
|
case LvlDebug:
|
||||||
|
handler = h.DebugHandler
|
||||||
|
case LvlWarn:
|
||||||
|
handler = h.WarnHandler
|
||||||
|
case LvlError:
|
||||||
|
handler = h.ErrorHandler
|
||||||
|
case LvlCrit:
|
||||||
|
handler = h.CriticalHandler
|
||||||
|
}
|
||||||
|
|
||||||
|
// Embed the caller function in the context
|
||||||
|
if handler != nil {
|
||||||
|
handler.Log(r)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *CompositeMultiHandler) SetHandler(handler LogHandler, replace bool, level LogLevel) {
|
||||||
|
if handler == nil {
|
||||||
|
// Ignore empty handler
|
||||||
|
return
|
||||||
|
}
|
||||||
|
source := &h.DebugHandler
|
||||||
|
switch level {
|
||||||
|
case LvlDebug:
|
||||||
|
source = &h.DebugHandler
|
||||||
|
case LvlInfo:
|
||||||
|
source = &h.InfoHandler
|
||||||
|
case LvlWarn:
|
||||||
|
source = &h.WarnHandler
|
||||||
|
case LvlError:
|
||||||
|
source = &h.ErrorHandler
|
||||||
|
case LvlCrit:
|
||||||
|
source = &h.CriticalHandler
|
||||||
|
}
|
||||||
|
|
||||||
|
if !replace && *source != nil {
|
||||||
|
// If we are not replacing the source make sure that the level handler is applied first
|
||||||
|
if _, isLevel := (*source).(*LevelFilterHandler); !isLevel {
|
||||||
|
*source = LevelHandler(level, *source)
|
||||||
|
}
|
||||||
|
// If this already was a list add a new logger to it
|
||||||
|
if ll, found := (*source).(*ListLogHandler); found {
|
||||||
|
ll.Add(handler)
|
||||||
|
} else {
|
||||||
|
*source = NewListLogHandler(*source, handler)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
*source = handler
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// For the multi handler set the handler, using the LogOptions defined
|
||||||
|
func (h *CompositeMultiHandler) SetHandlers(handler LogHandler, options *LogOptions) {
|
||||||
|
if len(options.Levels) == 0 {
|
||||||
|
options.Levels = LvlAllList
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set all levels
|
||||||
|
for _, lvl := range options.Levels {
|
||||||
|
h.SetHandler(handler, options.ReplaceExistingHandler, lvl)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
func (h *CompositeMultiHandler) SetJson(writer io.Writer, options *LogOptions) {
|
||||||
|
handler := CallerFileHandler(StreamHandler(writer, JsonFormatEx(
|
||||||
|
options.GetBoolDefault("pretty", false),
|
||||||
|
options.GetBoolDefault("lineSeparated", true),
|
||||||
|
)))
|
||||||
|
if options.HandlerWrap != nil {
|
||||||
|
handler = options.HandlerWrap.SetChild(handler)
|
||||||
|
}
|
||||||
|
h.SetHandlers(handler, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use built in rolling function
|
||||||
|
func (h *CompositeMultiHandler) SetJsonFile(filePath string, options *LogOptions) {
|
||||||
|
writer := &lumberjack.Logger{
|
||||||
|
Filename: filePath,
|
||||||
|
MaxSize: options.GetIntDefault("maxSizeMB", 1024), // megabytes
|
||||||
|
MaxAge: options.GetIntDefault("maxAgeDays", 7), //days
|
||||||
|
MaxBackups: options.GetIntDefault("maxBackups", 7),
|
||||||
|
Compress: options.GetBoolDefault("compress", true),
|
||||||
|
}
|
||||||
|
h.SetJson(writer, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *CompositeMultiHandler) SetTerminal(writer io.Writer, options *LogOptions) {
|
||||||
|
streamHandler := StreamHandler(
|
||||||
|
writer,
|
||||||
|
TerminalFormatHandler(
|
||||||
|
options.GetBoolDefault("noColor", false),
|
||||||
|
options.GetBoolDefault("smallDate", true)))
|
||||||
|
|
||||||
|
if os.Stdout == writer {
|
||||||
|
streamHandler = StreamHandler(
|
||||||
|
colorable.NewColorableStdout(),
|
||||||
|
TerminalFormatHandler(
|
||||||
|
options.GetBoolDefault("noColor", false),
|
||||||
|
options.GetBoolDefault("smallDate", true)))
|
||||||
|
} else if os.Stderr == writer {
|
||||||
|
streamHandler = StreamHandler(
|
||||||
|
colorable.NewColorableStderr(),
|
||||||
|
TerminalFormatHandler(
|
||||||
|
options.GetBoolDefault("noColor", false),
|
||||||
|
options.GetBoolDefault("smallDate", true)))
|
||||||
|
}
|
||||||
|
handler := CallerFileHandler(streamHandler)
|
||||||
|
|
||||||
|
if options.HandlerWrap != nil {
|
||||||
|
handler = options.HandlerWrap.SetChild(handler)
|
||||||
|
}
|
||||||
|
h.SetHandlers(handler, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use built in rolling function
|
||||||
|
func (h *CompositeMultiHandler) SetTerminalFile(filePath string, options *LogOptions) {
|
||||||
|
writer := &lumberjack.Logger{
|
||||||
|
Filename: filePath,
|
||||||
|
MaxSize: options.GetIntDefault("maxSizeMB", 1024), // megabytes
|
||||||
|
MaxAge: options.GetIntDefault("maxAgeDays", 7), //days
|
||||||
|
MaxBackups: options.GetIntDefault("maxBackups", 7),
|
||||||
|
Compress: options.GetBoolDefault("compress", true),
|
||||||
|
}
|
||||||
|
h.SetTerminal(writer, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *CompositeMultiHandler) Disable(levels ...LogLevel) {
|
||||||
|
if len(levels) == 0 {
|
||||||
|
levels = LvlAllList
|
||||||
|
}
|
||||||
|
for _, level := range levels {
|
||||||
|
switch level {
|
||||||
|
case LvlDebug:
|
||||||
|
h.DebugHandler = nil
|
||||||
|
case LvlInfo:
|
||||||
|
h.InfoHandler = nil
|
||||||
|
case LvlWarn:
|
||||||
|
h.WarnHandler = nil
|
||||||
|
case LvlError:
|
||||||
|
h.ErrorHandler = nil
|
||||||
|
case LvlCrit:
|
||||||
|
h.CriticalHandler = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,8 +3,13 @@
|
|||||||
These facilities all currently use the logging library called log15 at
|
These facilities all currently use the logging library called log15 at
|
||||||
https://github.com/inconshreveable/log15
|
https://github.com/inconshreveable/log15
|
||||||
|
|
||||||
Wrappers for the handlers are written here to provide a kind of isolation layer for Revel
|
Defining handlers happens as follows
|
||||||
in case sometime in the future we would like to switch to another source to implement logging
|
1) ALL handlers (log.all.output) replace any existing handlers
|
||||||
|
2) Output handlers (log.error.output) replace any existing handlers
|
||||||
|
3) Filter handlers (log.xxx.filter, log.xxx.nfilter) append to existing handlers,
|
||||||
|
note log.all.filter is treated as a filter handler, so it will NOT replace existing ones
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
*/
|
*/
|
||||||
package logger
|
package logger
|
||||||
|
|||||||
@@ -1,82 +1,93 @@
|
|||||||
package logger
|
package logger
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
|
||||||
|
|
||||||
colorable "github.com/mattn/go-colorable"
|
|
||||||
"github.com/revel/log15"
|
|
||||||
"gopkg.in/natefinch/lumberjack.v2"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type LevelFilterHandler struct {
|
||||||
|
Level LogLevel
|
||||||
|
h LogHandler
|
||||||
|
}
|
||||||
|
|
||||||
// Filters out records which do not match the level
|
// Filters out records which do not match the level
|
||||||
// Uses the `log15.FilterHandler` to perform this task
|
// Uses the `log15.FilterHandler` to perform this task
|
||||||
func LevelHandler(lvl LogLevel, h LogHandler) LogHandler {
|
func LevelHandler(lvl LogLevel, h LogHandler) LogHandler {
|
||||||
l15Lvl := log15.Lvl(lvl)
|
return &LevelFilterHandler{lvl, h}
|
||||||
return log15.FilterHandler(func(r *log15.Record) (pass bool) {
|
}
|
||||||
return r.Lvl == l15Lvl
|
|
||||||
}, h)
|
// The implementation of the Log
|
||||||
|
func (h LevelFilterHandler) Log(r *Record) error {
|
||||||
|
if r.Level == h.Level {
|
||||||
|
return h.h.Log(r)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filters out records which do not match the level
|
// Filters out records which do not match the level
|
||||||
// Uses the `log15.FilterHandler` to perform this task
|
// Uses the `log15.FilterHandler` to perform this task
|
||||||
func MinLevelHandler(lvl LogLevel, h LogHandler) LogHandler {
|
func MinLevelHandler(lvl LogLevel, h LogHandler) LogHandler {
|
||||||
l15Lvl := log15.Lvl(lvl)
|
return FilterHandler(func(r *Record) (pass bool) {
|
||||||
return log15.FilterHandler(func(r *log15.Record) (pass bool) {
|
return r.Level <= lvl
|
||||||
return r.Lvl <= l15Lvl
|
|
||||||
}, h)
|
}, h)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filters out records which match the level
|
// Filters out records which match the level
|
||||||
// Uses the `log15.FilterHandler` to perform this task
|
// Uses the `log15.FilterHandler` to perform this task
|
||||||
func NotLevelHandler(lvl LogLevel, h LogHandler) LogHandler {
|
func NotLevelHandler(lvl LogLevel, h LogHandler) LogHandler {
|
||||||
l15Lvl := log15.Lvl(lvl)
|
return FilterHandler(func(r *Record) (pass bool) {
|
||||||
return log15.FilterHandler(func(r *log15.Record) (pass bool) {
|
return r.Level != lvl
|
||||||
return r.Lvl != l15Lvl
|
|
||||||
}, h)
|
}, h)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adds in a context called `caller` to the record (contains file name and line number like `foo.go:12`)
|
|
||||||
// Uses the `log15.CallerFileHandler` to perform this task
|
|
||||||
func CallerFileHandler(h LogHandler) LogHandler {
|
func CallerFileHandler(h LogHandler) LogHandler {
|
||||||
return log15.CallerFileHandler(h)
|
return FuncHandler(func(r *Record) error {
|
||||||
|
r.Context.Add("caller", fmt.Sprint(r.Call))
|
||||||
|
return h.Log(r)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adds in a context called `caller` to the record (contains file name and line number like `foo.go:12`)
|
// Adds in a context called `caller` to the record (contains file name and line number like `foo.go:12`)
|
||||||
// Uses the `log15.CallerFuncHandler` to perform this task
|
// Uses the `log15.CallerFuncHandler` to perform this task
|
||||||
func CallerFuncHandler(h LogHandler) LogHandler {
|
func CallerFuncHandler(h LogHandler) LogHandler {
|
||||||
return log15.CallerFuncHandler(h)
|
return CallerFuncHandler(h)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filters out records which match the key value pair
|
// Filters out records which match the key value pair
|
||||||
// Uses the `log15.MatchFilterHandler` to perform this task
|
// Uses the `log15.MatchFilterHandler` to perform this task
|
||||||
func MatchHandler(key string, value interface{}, h LogHandler) LogHandler {
|
func MatchHandler(key string, value interface{}, h LogHandler) LogHandler {
|
||||||
return log15.MatchFilterHandler(key, value, h)
|
return MatchFilterHandler(key, value, h)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MatchFilterHandler returns a Handler that only writes records
|
||||||
|
// to the wrapped Handler if the given key in the logged
|
||||||
|
// context matches the value. For example, to only log records
|
||||||
|
// from your ui package:
|
||||||
|
//
|
||||||
|
// log.MatchFilterHandler("pkg", "app/ui", log.StdoutHandler)
|
||||||
|
//
|
||||||
|
func MatchFilterHandler(key string, value interface{}, h LogHandler) LogHandler {
|
||||||
|
return FilterHandler(func(r *Record) (pass bool) {
|
||||||
|
return r.Context[key] == value
|
||||||
|
}, h)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If match then A handler is called otherwise B handler is called
|
// If match then A handler is called otherwise B handler is called
|
||||||
func MatchAbHandler(key string, value interface{}, a, b LogHandler) LogHandler {
|
func MatchAbHandler(key string, value interface{}, a, b LogHandler) LogHandler {
|
||||||
return log15.FuncHandler(func(r *log15.Record) error {
|
return FuncHandler(func(r *Record) error {
|
||||||
for i := 0; i < len(r.Ctx); i += 2 {
|
if r.Context[key] == value {
|
||||||
if r.Ctx[i] == key {
|
return a.Log(r)
|
||||||
if r.Ctx[i+1] == value {
|
} else if b != nil {
|
||||||
if a != nil {
|
|
||||||
return a.Log(r)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if b != nil {
|
|
||||||
return b.Log(r)
|
return b.Log(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// The nil handler is used if logging for a specific request needs to be turned off
|
// The nil handler is used if logging for a specific request needs to be turned off
|
||||||
func NilHandler() LogHandler {
|
func NilHandler() LogHandler {
|
||||||
return log15.FuncHandler(func(r *log15.Record) error {
|
return FuncHandler(func(r *Record) error {
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -93,80 +104,83 @@ func NotMatchMapHandler(matchMap map[string]interface{}, a LogHandler) LogHandle
|
|||||||
|
|
||||||
// Rather then chaining multiple filter handlers, process all here
|
// Rather then chaining multiple filter handlers, process all here
|
||||||
func matchMapHandler(matchMap map[string]interface{}, inverse bool, a LogHandler) LogHandler {
|
func matchMapHandler(matchMap map[string]interface{}, inverse bool, a LogHandler) LogHandler {
|
||||||
return log15.FuncHandler(func(r *log15.Record) error {
|
return FuncHandler(func(r *Record) error {
|
||||||
checkMap := map[string]bool{}
|
matchCount := 0
|
||||||
// Copy the map to a bool
|
for k, v := range matchMap {
|
||||||
for i := 0; i < len(r.Ctx); i += 2 {
|
value, found := r.Context[k]
|
||||||
if value, found := matchMap[r.Ctx[i].(string)]; found && value == r.Ctx[i+1] {
|
if !found {
|
||||||
checkMap[r.Ctx[i].(string)] = true
|
return nil
|
||||||
|
}
|
||||||
|
// Test for two failure cases
|
||||||
|
if value == v && inverse || value != v && !inverse {
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
matchCount++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(checkMap) == len(matchMap) {
|
if matchCount != len(matchMap) {
|
||||||
if !inverse {
|
return nil
|
||||||
return a.Log(r)
|
|
||||||
}
|
|
||||||
} else if inverse {
|
|
||||||
return a.Log(r)
|
|
||||||
}
|
}
|
||||||
return nil
|
return a.Log(r)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filters out records which do not match the key value pair
|
// Filters out records which do not match the key value pair
|
||||||
// Uses the `log15.FilterHandler` to perform this task
|
// Uses the `log15.FilterHandler` to perform this task
|
||||||
func NotMatchHandler(key string, value interface{}, h LogHandler) LogHandler {
|
func NotMatchHandler(key string, value interface{}, h LogHandler) LogHandler {
|
||||||
return log15.FilterHandler(func(r *log15.Record) (pass bool) {
|
return FilterHandler(func(r *Record) (pass bool) {
|
||||||
switch key {
|
return r.Context[key] != value
|
||||||
case r.KeyNames.Lvl:
|
|
||||||
return r.Lvl != value
|
|
||||||
case r.KeyNames.Time:
|
|
||||||
return r.Time != value
|
|
||||||
case r.KeyNames.Msg:
|
|
||||||
return r.Msg != value
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < len(r.Ctx); i += 2 {
|
|
||||||
if r.Ctx[i] == key {
|
|
||||||
return r.Ctx[i+1] == value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}, h)
|
}, h)
|
||||||
}
|
}
|
||||||
|
|
||||||
func MultiHandler(hs ...LogHandler) LogHandler {
|
func MultiHandler(hs ...LogHandler) LogHandler {
|
||||||
// Convert the log handlers to log15.Handlers
|
return FuncHandler(func(r *Record) error {
|
||||||
handlers := []log15.Handler{}
|
for _, h := range hs {
|
||||||
for _, h := range hs {
|
// what to do about failures?
|
||||||
if h != nil {
|
h.Log(r)
|
||||||
handlers = append(handlers, h)
|
|
||||||
}
|
}
|
||||||
}
|
return nil
|
||||||
|
})
|
||||||
return log15.MultiHandler(handlers...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Outputs the records to the passed in stream
|
// StreamHandler writes log records to an io.Writer
|
||||||
// Uses the `log15.StreamHandler` to perform this task
|
// with the given format. StreamHandler can be used
|
||||||
|
// to easily begin writing log records to other
|
||||||
|
// outputs.
|
||||||
|
//
|
||||||
|
// StreamHandler wraps itself with LazyHandler and SyncHandler
|
||||||
|
// to evaluate Lazy objects and perform safe concurrent writes.
|
||||||
func StreamHandler(wr io.Writer, fmtr LogFormat) LogHandler {
|
func StreamHandler(wr io.Writer, fmtr LogFormat) LogHandler {
|
||||||
return log15.StreamHandler(wr, fmtr)
|
h := FuncHandler(func(r *Record) error {
|
||||||
|
_, err := wr.Write(fmtr.Format(r))
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
return LazyHandler(SyncHandler(h))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter handler, this is the only
|
// Filter handler
|
||||||
// Uses the `log15.FilterHandler` to perform this task
|
func FilterHandler(fn func(r *Record) bool, h LogHandler) LogHandler {
|
||||||
func FilterHandler(fn func(r *log15.Record) bool, h LogHandler) LogHandler {
|
return FuncHandler(func(r *Record) error {
|
||||||
return log15.FilterHandler(fn, h)
|
if fn(r) {
|
||||||
|
return h.Log(r)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// List log handler handles a list of LogHandlers
|
||||||
type ListLogHandler struct {
|
type ListLogHandler struct {
|
||||||
handlers []LogHandler
|
handlers []LogHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create a new list of log handlers
|
||||||
func NewListLogHandler(h1, h2 LogHandler) *ListLogHandler {
|
func NewListLogHandler(h1, h2 LogHandler) *ListLogHandler {
|
||||||
ll := &ListLogHandler{handlers: []LogHandler{h1, h2}}
|
ll := &ListLogHandler{handlers: []LogHandler{h1, h2}}
|
||||||
return ll
|
return ll
|
||||||
}
|
}
|
||||||
func (ll *ListLogHandler) Log(r *log15.Record) (err error) {
|
|
||||||
|
// Log the record
|
||||||
|
func (ll *ListLogHandler) Log(r *Record) (err error) {
|
||||||
for _, handler := range ll.handlers {
|
for _, handler := range ll.handlers {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err = handler.Log(r)
|
err = handler.Log(r)
|
||||||
@@ -176,11 +190,15 @@ func (ll *ListLogHandler) Log(r *log15.Record) (err error) {
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add another log handler
|
||||||
func (ll *ListLogHandler) Add(h LogHandler) {
|
func (ll *ListLogHandler) Add(h LogHandler) {
|
||||||
if h != nil {
|
if h != nil {
|
||||||
ll.handlers = append(ll.handlers, h)
|
ll.handlers = append(ll.handlers, h)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove a log handler
|
||||||
func (ll *ListLogHandler) Del(h LogHandler) {
|
func (ll *ListLogHandler) Del(h LogHandler) {
|
||||||
if h != nil {
|
if h != nil {
|
||||||
for i, handler := range ll.handlers {
|
for i, handler := range ll.handlers {
|
||||||
@@ -190,161 +208,3 @@ func (ll *ListLogHandler) Del(h LogHandler) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type CompositeMultiHandler struct {
|
|
||||||
DebugHandler LogHandler
|
|
||||||
InfoHandler LogHandler
|
|
||||||
WarnHandler LogHandler
|
|
||||||
ErrorHandler LogHandler
|
|
||||||
CriticalHandler LogHandler
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewCompositeMultiHandler() (*CompositeMultiHandler, LogHandler) {
|
|
||||||
cw := &CompositeMultiHandler{}
|
|
||||||
return cw, cw
|
|
||||||
}
|
|
||||||
func (h *CompositeMultiHandler) Log(r *log15.Record) (err error) {
|
|
||||||
|
|
||||||
var handler LogHandler
|
|
||||||
switch r.Lvl {
|
|
||||||
case log15.LvlInfo:
|
|
||||||
handler = h.InfoHandler
|
|
||||||
case log15.LvlDebug:
|
|
||||||
handler = h.DebugHandler
|
|
||||||
case log15.LvlWarn:
|
|
||||||
handler = h.WarnHandler
|
|
||||||
case log15.LvlError:
|
|
||||||
handler = h.ErrorHandler
|
|
||||||
case log15.LvlCrit:
|
|
||||||
handler = h.CriticalHandler
|
|
||||||
}
|
|
||||||
|
|
||||||
// Embed the caller function in the context
|
|
||||||
if handler != nil {
|
|
||||||
handler.Log(r)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
func (h *CompositeMultiHandler) SetHandler(handler LogHandler, replace bool, level LogLevel) {
|
|
||||||
if handler == nil {
|
|
||||||
// Ignore empty handler
|
|
||||||
return
|
|
||||||
}
|
|
||||||
source := &h.DebugHandler
|
|
||||||
switch level {
|
|
||||||
case LvlDebug:
|
|
||||||
source = &h.DebugHandler
|
|
||||||
case LvlInfo:
|
|
||||||
source = &h.InfoHandler
|
|
||||||
case LvlWarn:
|
|
||||||
source = &h.WarnHandler
|
|
||||||
case LvlError:
|
|
||||||
source = &h.ErrorHandler
|
|
||||||
case LvlCrit:
|
|
||||||
source = &h.CriticalHandler
|
|
||||||
}
|
|
||||||
|
|
||||||
if !replace && *source != nil {
|
|
||||||
// If this already was a list add a new logger to it
|
|
||||||
if ll, found := (*source).(*ListLogHandler); found {
|
|
||||||
ll.Add(handler)
|
|
||||||
} else {
|
|
||||||
*source = NewListLogHandler(*source, handler)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
*source = handler
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *CompositeMultiHandler) SetHandlers(handler LogHandler, options *LogOptions) {
|
|
||||||
if len(options.Levels) == 0 {
|
|
||||||
options.Levels = LvlAllList
|
|
||||||
}
|
|
||||||
// Set all levels
|
|
||||||
for _, lvl := range options.Levels {
|
|
||||||
h.SetHandler(handler, options.ReplaceExistingHandler, lvl)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
func (h *CompositeMultiHandler) SetJson(writer io.Writer, options *LogOptions) {
|
|
||||||
handler := CallerFileHandler(StreamHandler(writer, log15.JsonFormatEx(
|
|
||||||
options.GetBoolDefault("pretty", false),
|
|
||||||
options.GetBoolDefault("lineSeparated", true),
|
|
||||||
)))
|
|
||||||
if options.HandlerWrap != nil {
|
|
||||||
handler = options.HandlerWrap.SetChild(handler)
|
|
||||||
}
|
|
||||||
h.SetHandlers(handler, options)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use built in rolling function
|
|
||||||
func (h *CompositeMultiHandler) SetJsonFile(filePath string, options *LogOptions) {
|
|
||||||
writer := &lumberjack.Logger{
|
|
||||||
Filename: filePath,
|
|
||||||
MaxSize: options.GetIntDefault("maxSizeMB", 1024), // megabytes
|
|
||||||
MaxAge: options.GetIntDefault("maxAgeDays", 7), //days
|
|
||||||
MaxBackups: options.GetIntDefault("maxBackups", 7),
|
|
||||||
Compress: options.GetBoolDefault("compress", true),
|
|
||||||
}
|
|
||||||
h.SetJson(writer, options)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *CompositeMultiHandler) SetTerminal(writer io.Writer, options *LogOptions) {
|
|
||||||
streamHandler := StreamHandler(
|
|
||||||
writer,
|
|
||||||
TerminalFormatHandler(
|
|
||||||
options.GetBoolDefault("noColor", false),
|
|
||||||
options.GetBoolDefault("smallDate", true)))
|
|
||||||
|
|
||||||
if os.Stdout == writer {
|
|
||||||
streamHandler = StreamHandler(
|
|
||||||
colorable.NewColorableStdout(),
|
|
||||||
TerminalFormatHandler(
|
|
||||||
options.GetBoolDefault("noColor", false),
|
|
||||||
options.GetBoolDefault("smallDate", true)))
|
|
||||||
} else if os.Stderr == writer {
|
|
||||||
streamHandler = StreamHandler(
|
|
||||||
colorable.NewColorableStderr(),
|
|
||||||
TerminalFormatHandler(
|
|
||||||
options.GetBoolDefault("noColor", false),
|
|
||||||
options.GetBoolDefault("smallDate", true)))
|
|
||||||
}
|
|
||||||
|
|
||||||
handler := CallerFileHandler(streamHandler)
|
|
||||||
if options.HandlerWrap != nil {
|
|
||||||
handler = options.HandlerWrap.SetChild(handler)
|
|
||||||
}
|
|
||||||
h.SetHandlers(handler, options)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use built in rolling function
|
|
||||||
func (h *CompositeMultiHandler) SetTerminalFile(filePath string, options *LogOptions) {
|
|
||||||
writer := &lumberjack.Logger{
|
|
||||||
Filename: filePath,
|
|
||||||
MaxSize: options.GetIntDefault("maxSizeMB", 1024), // megabytes
|
|
||||||
MaxAge: options.GetIntDefault("maxAgeDays", 7), //days
|
|
||||||
MaxBackups: options.GetIntDefault("maxBackups", 7),
|
|
||||||
Compress: options.GetBoolDefault("compress", true),
|
|
||||||
}
|
|
||||||
h.SetTerminal(writer, options)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *CompositeMultiHandler) Disable(levels ...LogLevel) {
|
|
||||||
if len(levels) == 0 {
|
|
||||||
levels = LvlAllList
|
|
||||||
}
|
|
||||||
for _, level := range levels {
|
|
||||||
switch level {
|
|
||||||
case LvlDebug:
|
|
||||||
h.DebugHandler = nil
|
|
||||||
case LvlInfo:
|
|
||||||
h.InfoHandler = nil
|
|
||||||
case LvlWarn:
|
|
||||||
h.WarnHandler = nil
|
|
||||||
case LvlError:
|
|
||||||
h.ErrorHandler = nil
|
|
||||||
case LvlCrit:
|
|
||||||
h.CriticalHandler = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
189
logger/init.go
Normal file
189
logger/init.go
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
package logger
|
||||||
|
|
||||||
|
// Get all handlers based on the Config (if available)
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/revel/config"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func InitializeFromConfig(basePath string, config *config.Context) (c *CompositeMultiHandler) {
|
||||||
|
// If running in test mode suppress anything that is not an error
|
||||||
|
if config != nil && config.BoolDefault(TEST_MODE_FLAG, false) {
|
||||||
|
// Preconfigure all the options
|
||||||
|
config.SetOption("log.info.output", "none")
|
||||||
|
config.SetOption("log.debug.output", "none")
|
||||||
|
config.SetOption("log.warn.output", "none")
|
||||||
|
config.SetOption("log.error.output", "stderr")
|
||||||
|
config.SetOption("log.crit.output", "stderr")
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the configuration has an all option we can skip some
|
||||||
|
c, _ = NewCompositeMultiHandler()
|
||||||
|
|
||||||
|
// Filters are assigned first, non filtered items override filters
|
||||||
|
if config != nil && !config.BoolDefault(TEST_MODE_FLAG, false) {
|
||||||
|
initAllLog(c, basePath, config)
|
||||||
|
}
|
||||||
|
initLogLevels(c, basePath, config)
|
||||||
|
if c.CriticalHandler == nil && c.ErrorHandler != nil {
|
||||||
|
c.CriticalHandler = c.ErrorHandler
|
||||||
|
}
|
||||||
|
if config != nil && !config.BoolDefault(TEST_MODE_FLAG, false) {
|
||||||
|
initFilterLog(c, basePath, config)
|
||||||
|
if c.CriticalHandler == nil && c.ErrorHandler != nil {
|
||||||
|
c.CriticalHandler = c.ErrorHandler
|
||||||
|
}
|
||||||
|
initRequestLog(c, basePath, config)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init the log.all configuration options
|
||||||
|
func initAllLog(c *CompositeMultiHandler, basePath string, config *config.Context) {
|
||||||
|
if config != nil {
|
||||||
|
extraLogFlag := config.BoolDefault(SPECIAL_USE_FLAG, false)
|
||||||
|
if output, found := config.String("log.all.output"); found {
|
||||||
|
// Set all output for the specified handler
|
||||||
|
if extraLogFlag {
|
||||||
|
log.Printf("Adding standard handler for levels to >%s< ", output)
|
||||||
|
}
|
||||||
|
initHandlerFor(c, output, basePath, NewLogOptions(config, true, nil, LvlAllList...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init the filter options
|
||||||
|
// log.all.filter ....
|
||||||
|
// log.error.filter ....
|
||||||
|
func initFilterLog(c *CompositeMultiHandler, basePath string, config *config.Context) {
|
||||||
|
|
||||||
|
if config != nil {
|
||||||
|
extraLogFlag := config.BoolDefault(SPECIAL_USE_FLAG, false)
|
||||||
|
|
||||||
|
for _, logFilter := range logFilterList {
|
||||||
|
// Init for all filters
|
||||||
|
for _, name := range []string{"all", "debug", "info", "warn", "error", "crit",
|
||||||
|
"trace", // TODO trace is deprecated
|
||||||
|
} {
|
||||||
|
optionList := config.Options(logFilter.LogPrefix + name + logFilter.LogSuffix)
|
||||||
|
for _, option := range optionList {
|
||||||
|
splitOptions := strings.Split(option, ".")
|
||||||
|
keyMap := map[string]interface{}{}
|
||||||
|
for x := 3; x < len(splitOptions); x += 2 {
|
||||||
|
keyMap[splitOptions[x]] = splitOptions[x+1]
|
||||||
|
}
|
||||||
|
phandler := logFilter.parentHandler(keyMap)
|
||||||
|
if extraLogFlag {
|
||||||
|
log.Printf("Adding key map handler %s %s output %s", option, name, config.StringDefault(option, ""))
|
||||||
|
fmt.Printf("Adding key map handler %s %s output %s matching %#v\n", option, name, config.StringDefault(option, ""), keyMap)
|
||||||
|
}
|
||||||
|
|
||||||
|
if name == "all" {
|
||||||
|
initHandlerFor(c, config.StringDefault(option, ""), basePath, NewLogOptions(config, false, phandler))
|
||||||
|
} else {
|
||||||
|
initHandlerFor(c, config.StringDefault(option, ""), basePath, NewLogOptions(config, false, phandler, toLevel[name]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init the log.error, log.warn etc configuration options
|
||||||
|
func initLogLevels(c *CompositeMultiHandler, basePath string, config *config.Context) {
|
||||||
|
for _, name := range []string{"debug", "info", "warn", "error", "crit",
|
||||||
|
"trace", // TODO trace is deprecated
|
||||||
|
} {
|
||||||
|
if config != nil {
|
||||||
|
extraLogFlag := config.BoolDefault(SPECIAL_USE_FLAG, false)
|
||||||
|
output, found := config.String("log." + name + ".output")
|
||||||
|
if found {
|
||||||
|
if extraLogFlag {
|
||||||
|
log.Printf("Adding standard handler %s output %s", name, output)
|
||||||
|
}
|
||||||
|
initHandlerFor(c, output, basePath, NewLogOptions(config, true, nil, toLevel[name]))
|
||||||
|
}
|
||||||
|
// Gets the list of options with said prefix
|
||||||
|
} else {
|
||||||
|
initHandlerFor(c, "stderr", basePath, NewLogOptions(config, true, nil, toLevel[name]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init the request log options
|
||||||
|
func initRequestLog(c *CompositeMultiHandler, basePath string, config *config.Context) {
|
||||||
|
// Request logging to a separate output handler
|
||||||
|
// This takes the InfoHandlers and adds a MatchAbHandler handler to it to direct
|
||||||
|
// context with the word "section=requestlog" to that handler.
|
||||||
|
// Note if request logging is not enabled the MatchAbHandler will not be added and the
|
||||||
|
// request log messages will be sent out the INFO handler
|
||||||
|
outputRequest := "stdout"
|
||||||
|
if config != nil {
|
||||||
|
outputRequest = config.StringDefault("log.request.output", "")
|
||||||
|
}
|
||||||
|
oldInfo := c.InfoHandler
|
||||||
|
c.InfoHandler = nil
|
||||||
|
if outputRequest != "" {
|
||||||
|
initHandlerFor(c, outputRequest, basePath, NewLogOptions(config, false, nil, LvlInfo))
|
||||||
|
}
|
||||||
|
if c.InfoHandler != nil || oldInfo != nil {
|
||||||
|
if c.InfoHandler == nil {
|
||||||
|
c.InfoHandler = oldInfo
|
||||||
|
} else {
|
||||||
|
c.InfoHandler = MatchAbHandler("section", "requestlog", c.InfoHandler, oldInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a handler for the level using the output string
|
||||||
|
// Accept formats for output string are
|
||||||
|
// LogFunctionMap[value] callback function
|
||||||
|
// `stdout` `stderr` `full/file/path/to/location/app.log` `full/file/path/to/location/app.json`
|
||||||
|
func initHandlerFor(c *CompositeMultiHandler, output, basePath string, options *LogOptions) {
|
||||||
|
if options.Ctx != nil {
|
||||||
|
options.SetExtendedOptions(
|
||||||
|
"noColor", !options.Ctx.BoolDefault("log.colorize", true),
|
||||||
|
"smallDate", options.Ctx.BoolDefault("log.smallDate", true),
|
||||||
|
"maxSize", options.Ctx.IntDefault("log.maxsize", 1024*10),
|
||||||
|
"maxAge", options.Ctx.IntDefault("log.maxage", 14),
|
||||||
|
"maxBackups", options.Ctx.IntDefault("log.maxbackups", 14),
|
||||||
|
"compressBackups", !options.Ctx.BoolDefault("log.compressBackups", true),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
output = strings.TrimSpace(output)
|
||||||
|
if funcHandler, found := LogFunctionMap[output]; found {
|
||||||
|
funcHandler(c, options)
|
||||||
|
} else {
|
||||||
|
switch output {
|
||||||
|
case "":
|
||||||
|
fallthrough
|
||||||
|
case "off":
|
||||||
|
// No handler, discard data
|
||||||
|
default:
|
||||||
|
// Write to file specified
|
||||||
|
if !filepath.IsAbs(output) {
|
||||||
|
output = filepath.Join(basePath, output)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.MkdirAll(filepath.Dir(output), 0755); err != nil {
|
||||||
|
log.Panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasSuffix(output, "json") {
|
||||||
|
c.SetJsonFile(output, options)
|
||||||
|
} else {
|
||||||
|
// Override defaults for a terminal file
|
||||||
|
options.SetExtendedOptions("noColor", true)
|
||||||
|
options.SetExtendedOptions("smallDate", false)
|
||||||
|
c.SetTerminalFile(output, options)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
273
logger/init_test.go
Normal file
273
logger/init_test.go
Normal file
@@ -0,0 +1,273 @@
|
|||||||
|
// Copyright (c) 2012-2018 The Revel Framework Authors, All rights reserved.
|
||||||
|
// Revel Framework source code and usage is governed by a MIT style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package logger_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/revel/config"
|
||||||
|
"github.com/revel/revel/logger"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
// A counter for the tester
|
||||||
|
testCounter struct {
|
||||||
|
debug, info, warn, error, critical int
|
||||||
|
}
|
||||||
|
// The data to tes
|
||||||
|
testData struct {
|
||||||
|
config []string
|
||||||
|
result testResult
|
||||||
|
tc *testCounter
|
||||||
|
}
|
||||||
|
// The test result
|
||||||
|
testResult struct {
|
||||||
|
debug, info, warn, error, critical int
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Single test cases
|
||||||
|
var singleCases = []testData{
|
||||||
|
{config: []string{"log.crit.output"},
|
||||||
|
result: testResult{0, 0, 0, 0, 1}},
|
||||||
|
{config: []string{"log.error.output"},
|
||||||
|
result: testResult{0, 0, 0, 1, 1}},
|
||||||
|
{config: []string{"log.warn.output"},
|
||||||
|
result: testResult{0, 0, 1, 0, 0}},
|
||||||
|
{config: []string{"log.info.output"},
|
||||||
|
result: testResult{0, 1, 0, 0, 0}},
|
||||||
|
{config: []string{"log.debug.output"},
|
||||||
|
result: testResult{1, 0, 0, 0, 0}},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test singles
|
||||||
|
func TestSingleCases(t *testing.T) {
|
||||||
|
rootLog := logger.New()
|
||||||
|
for _, testCase := range singleCases {
|
||||||
|
testCase.logTest(rootLog, t)
|
||||||
|
testCase.validate(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter test cases
|
||||||
|
var filterCases = []testData{
|
||||||
|
{config: []string{"log.crit.filter.module.app"},
|
||||||
|
result: testResult{0, 0, 0, 0, 1}},
|
||||||
|
{config: []string{"log.crit.filter.module.appa"},
|
||||||
|
result: testResult{0, 0, 0, 0, 0}},
|
||||||
|
{config: []string{"log.error.filter.module.app"},
|
||||||
|
result: testResult{0, 0, 0, 1, 1}},
|
||||||
|
{config: []string{"log.error.filter.module.appa"},
|
||||||
|
result: testResult{0, 0, 0, 0, 0}},
|
||||||
|
{config: []string{"log.warn.filter.module.app"},
|
||||||
|
result: testResult{0, 0, 1, 0, 0}},
|
||||||
|
{config: []string{"log.warn.filter.module.appa"},
|
||||||
|
result: testResult{0, 0, 0, 0, 0}},
|
||||||
|
{config: []string{"log.info.filter.module.app"},
|
||||||
|
result: testResult{0, 1, 0, 0, 0}},
|
||||||
|
{config: []string{"log.info.filter.module.appa"},
|
||||||
|
result: testResult{0, 0, 0, 0, 0}},
|
||||||
|
{config: []string{"log.debug.filter.module.app"},
|
||||||
|
result: testResult{1, 0, 0, 0, 0}},
|
||||||
|
{config: []string{"log.debug.filter.module.appa"},
|
||||||
|
result: testResult{0, 0, 0, 0, 0}},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter test
|
||||||
|
func TestFilterCases(t *testing.T) {
|
||||||
|
rootLog := logger.New("module", "app")
|
||||||
|
for _, testCase := range filterCases {
|
||||||
|
testCase.logTest(rootLog, t)
|
||||||
|
testCase.validate(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inverse test cases
|
||||||
|
var nfilterCases = []testData{
|
||||||
|
{config: []string{"log.crit.nfilter.module.appa"},
|
||||||
|
result: testResult{0, 0, 0, 0, 1}},
|
||||||
|
{config: []string{"log.crit.nfilter.modules.appa"},
|
||||||
|
result: testResult{0, 0, 0, 0, 0}},
|
||||||
|
{config: []string{"log.crit.nfilter.module.app"},
|
||||||
|
result: testResult{0, 0, 0, 0, 0}},
|
||||||
|
{config: []string{"log.error.nfilter.module.appa"}, // Special case, when error is not nill critical inherits from error
|
||||||
|
result: testResult{0, 0, 0, 1, 1}},
|
||||||
|
{config: []string{"log.error.nfilter.module.app"},
|
||||||
|
result: testResult{0, 0, 0, 0, 0}},
|
||||||
|
{config: []string{"log.warn.nfilter.module.appa"},
|
||||||
|
result: testResult{0, 0, 1, 0, 0}},
|
||||||
|
{config: []string{"log.warn.nfilter.module.app"},
|
||||||
|
result: testResult{0, 0, 0, 0, 0}},
|
||||||
|
{config: []string{"log.info.nfilter.module.appa"},
|
||||||
|
result: testResult{0, 1, 0, 0, 0}},
|
||||||
|
{config: []string{"log.info.nfilter.module.app"},
|
||||||
|
result: testResult{0, 0, 0, 0, 0}},
|
||||||
|
{config: []string{"log.debug.nfilter.module.appa"},
|
||||||
|
result: testResult{1, 0, 0, 0, 0}},
|
||||||
|
{config: []string{"log.debug.nfilter.module.app"},
|
||||||
|
result: testResult{0, 0, 0, 0, 0}},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inverse test
|
||||||
|
func TestNotFilterCases(t *testing.T) {
|
||||||
|
rootLog := logger.New("module", "app")
|
||||||
|
for _, testCase := range nfilterCases {
|
||||||
|
testCase.logTest(rootLog, t)
|
||||||
|
testCase.validate(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// off test cases
|
||||||
|
var offCases = []testData{
|
||||||
|
{config: []string{"log.all.output", "log.error.output=off"},
|
||||||
|
result: testResult{1, 1, 1, 0, 1}},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Off test
|
||||||
|
func TestOffCases(t *testing.T) {
|
||||||
|
rootLog := logger.New("module", "app")
|
||||||
|
for _, testCase := range offCases {
|
||||||
|
testCase.logTest(rootLog, t)
|
||||||
|
testCase.validate(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Duplicate test cases
|
||||||
|
var duplicateCases = []testData{
|
||||||
|
{config: []string{"log.all.output", "log.error.output", "log.error.filter.module.app"},
|
||||||
|
result: testResult{1, 1, 1, 2, 1}},
|
||||||
|
}
|
||||||
|
|
||||||
|
// test duplicate cases
|
||||||
|
func TestDuplicateCases(t *testing.T) {
|
||||||
|
rootLog := logger.New("module", "app")
|
||||||
|
for _, testCase := range duplicateCases {
|
||||||
|
testCase.logTest(rootLog, t)
|
||||||
|
testCase.validate(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Contradicting cases
|
||||||
|
var contradictCases = []testData{
|
||||||
|
{config: []string{"log.all.output", "log.error.output=off", "log.all.output"},
|
||||||
|
result: testResult{1, 1, 1, 0, 1}},
|
||||||
|
{config: []string{"log.all.output", "log.error.output=off", "log.debug.filter.module.app"},
|
||||||
|
result: testResult{2, 1, 1, 0, 1}},
|
||||||
|
{config: []string{"log.all.filter.module.app", "log.info.output=off", "log.info.filter.module.app"},
|
||||||
|
result: testResult{1, 2, 1, 1, 1}},
|
||||||
|
{config: []string{"log.all.output", "log.info.output=off", "log.info.filter.module.app"},
|
||||||
|
result: testResult{1, 1, 1, 1, 1}},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Contradiction test
|
||||||
|
func TestContradictCases(t *testing.T) {
|
||||||
|
rootLog := logger.New("module", "app")
|
||||||
|
for _, testCase := range contradictCases {
|
||||||
|
testCase.logTest(rootLog, t)
|
||||||
|
testCase.validate(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// All test cases
|
||||||
|
var allCases = []testData{
|
||||||
|
{config: []string{"log.all.filter.module.app"},
|
||||||
|
result: testResult{1, 1, 1, 1, 1}},
|
||||||
|
{config: []string{"log.all.output"},
|
||||||
|
result: testResult{2, 2, 2, 2, 2}},
|
||||||
|
}
|
||||||
|
|
||||||
|
// All tests
|
||||||
|
func TestAllCases(t *testing.T) {
|
||||||
|
rootLog := logger.New("module", "app")
|
||||||
|
for i, testCase := range allCases {
|
||||||
|
testCase.logTest(rootLog, t)
|
||||||
|
allCases[i] = testCase
|
||||||
|
}
|
||||||
|
rootLog = logger.New()
|
||||||
|
for i, testCase := range allCases {
|
||||||
|
testCase.logTest(rootLog, t)
|
||||||
|
allCases[i] = testCase
|
||||||
|
}
|
||||||
|
for _, testCase := range allCases {
|
||||||
|
testCase.validate(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *testCounter) Log(r *logger.Record) error {
|
||||||
|
switch r.Level {
|
||||||
|
case logger.LvlDebug:
|
||||||
|
c.debug++
|
||||||
|
case logger.LvlInfo:
|
||||||
|
c.info++
|
||||||
|
case logger.LvlWarn:
|
||||||
|
c.warn++
|
||||||
|
case logger.LvlError:
|
||||||
|
c.error++
|
||||||
|
case logger.LvlCrit:
|
||||||
|
c.critical++
|
||||||
|
default:
|
||||||
|
panic("Unknown log level")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (td *testData) logTest(rootLog logger.MultiLogger, t *testing.T) {
|
||||||
|
if td.tc == nil {
|
||||||
|
td.tc = &testCounter{}
|
||||||
|
counterInit(td.tc)
|
||||||
|
}
|
||||||
|
newContext := config.NewContext()
|
||||||
|
for _, i := range td.config {
|
||||||
|
iout := strings.Split(i, "=")
|
||||||
|
if len(iout) > 1 {
|
||||||
|
newContext.SetOption(iout[0], iout[1])
|
||||||
|
} else {
|
||||||
|
newContext.SetOption(i, "test")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
newContext.SetOption("specialUseFlag", "true")
|
||||||
|
|
||||||
|
handler := logger.InitializeFromConfig("test", newContext)
|
||||||
|
|
||||||
|
rootLog.SetHandler(handler)
|
||||||
|
|
||||||
|
td.runLogTest(rootLog)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (td *testData) runLogTest(log logger.MultiLogger) {
|
||||||
|
log.Debug("test")
|
||||||
|
log.Info("test")
|
||||||
|
log.Warn("test")
|
||||||
|
log.Error("test")
|
||||||
|
log.Crit("test")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (td *testData) validate(t *testing.T) {
|
||||||
|
t.Logf("Test %#v expected %#v", td.tc, td.result)
|
||||||
|
assert.Equal(t, td.result.debug, td.tc.debug, "Debug failed "+strings.Join(td.config, " "))
|
||||||
|
assert.Equal(t, td.result.info, td.tc.info, "Info failed "+strings.Join(td.config, " "))
|
||||||
|
assert.Equal(t, td.result.warn, td.tc.warn, "Warn failed "+strings.Join(td.config, " "))
|
||||||
|
assert.Equal(t, td.result.error, td.tc.error, "Error failed "+strings.Join(td.config, " "))
|
||||||
|
assert.Equal(t, td.result.critical, td.tc.critical, "Critical failed "+strings.Join(td.config, " "))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add test to the function map
|
||||||
|
func counterInit(tc *testCounter) {
|
||||||
|
logger.LogFunctionMap["test"] = func(c *logger.CompositeMultiHandler, logOptions *logger.LogOptions) {
|
||||||
|
// Output to the test log and the stdout
|
||||||
|
outHandler := logger.LogHandler(
|
||||||
|
logger.NewListLogHandler(tc,
|
||||||
|
logger.StreamHandler(os.Stdout, logger.TerminalFormatHandler(false, true))),
|
||||||
|
)
|
||||||
|
if logOptions.HandlerWrap != nil {
|
||||||
|
outHandler = logOptions.HandlerWrap.SetChild(outHandler)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.SetHandlers(outHandler, logOptions)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,656 +0,0 @@
|
|||||||
package logger
|
|
||||||
|
|
||||||
|
|
||||||
// LoggedError is wrapper to differentiate logged panics from unexpected ones.
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
"go.uber.org/zap"
|
|
||||||
"go.uber.org/zap/zapcore"
|
|
||||||
"sync"
|
|
||||||
"go.uber.org/zap/buffer"
|
|
||||||
"time"
|
|
||||||
"encoding/base64"
|
|
||||||
"unicode/utf8"
|
|
||||||
"encoding/json"
|
|
||||||
"math"
|
|
||||||
|
|
||||||
"io"
|
|
||||||
)
|
|
||||||
|
|
||||||
type (
|
|
||||||
MultiLogger interface {
|
|
||||||
//log15.Logger
|
|
||||||
//// New returns a new Logger that has this logger's context plus the given context
|
|
||||||
New(ctx ...interface{}) MultiLogger
|
|
||||||
//
|
|
||||||
// The encoders job is to encode the
|
|
||||||
SetHandler(h LogHandler)
|
|
||||||
SetStackDepth(int) MultiLogger
|
|
||||||
//
|
|
||||||
//// Log a message at the given level with context key/value pairs
|
|
||||||
Debug(msg string, ctx ...interface{})
|
|
||||||
Debugf(msg string, params ...interface{})
|
|
||||||
Info(msg string, ctx ...interface{})
|
|
||||||
Infof(msg string, params ...interface{})
|
|
||||||
Warn(msg string, ctx ...interface{})
|
|
||||||
Warnf(msg string, params ...interface{})
|
|
||||||
Error(msg string, ctx ...interface{})
|
|
||||||
Errorf(msg string, params ...interface{})
|
|
||||||
Crit(msg string, ctx ...interface{})
|
|
||||||
Critf(msg string, params ...interface{})
|
|
||||||
|
|
||||||
//// Logs a message as an Crit and exits
|
|
||||||
Fatal(msg string, ctx ...interface{})
|
|
||||||
Fatalf(msg string, params ...interface{})
|
|
||||||
//// Logs a message as an Crit and panics
|
|
||||||
Panic(msg string, ctx ...interface{})
|
|
||||||
Panicf(msg string, params ...interface{})
|
|
||||||
}
|
|
||||||
|
|
||||||
// The log han
|
|
||||||
LogHandler interface {
|
|
||||||
Encode(Record) ([]byte, error)
|
|
||||||
GetLevel() Level
|
|
||||||
GetWriter() io.Writer
|
|
||||||
}
|
|
||||||
|
|
||||||
// The Record
|
|
||||||
Record struct {
|
|
||||||
Level Level
|
|
||||||
Time time.Time
|
|
||||||
LoggerName string
|
|
||||||
Message string
|
|
||||||
Caller EntryCaller
|
|
||||||
Stack string
|
|
||||||
Context []Field
|
|
||||||
}
|
|
||||||
|
|
||||||
// The fields passed in
|
|
||||||
Field interface {
|
|
||||||
GetKey() string
|
|
||||||
GetValueAsString() string
|
|
||||||
GetValue() interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
EntryCaller interface {
|
|
||||||
IsDefined() bool
|
|
||||||
GetPC() uintptr
|
|
||||||
GetFile() string
|
|
||||||
GetLine() int
|
|
||||||
}
|
|
||||||
|
|
||||||
// Called only if the logger needs
|
|
||||||
ResolveLaterLogger func() interface{}
|
|
||||||
|
|
||||||
FieldType int
|
|
||||||
Level int
|
|
||||||
)
|
|
||||||
|
|
||||||
type (
|
|
||||||
zapLogger struct {
|
|
||||||
logger *zap.SugaredLogger
|
|
||||||
coreList []*zapcore.Core
|
|
||||||
}
|
|
||||||
zapField struct {
|
|
||||||
Key string
|
|
||||||
Type FieldType
|
|
||||||
Integer int64
|
|
||||||
String string
|
|
||||||
Interface interface{}
|
|
||||||
}
|
|
||||||
zapEntryCaller struct {
|
|
||||||
Defined bool
|
|
||||||
PC uintptr
|
|
||||||
File string
|
|
||||||
Line int
|
|
||||||
}
|
|
||||||
zapEncoder struct {
|
|
||||||
lh LogHandler
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
func newLogger(addCaller bool) MultiLogger {
|
|
||||||
logger := zap.New(nil).WithOptions(zap.AddCaller())
|
|
||||||
l := &zapLogger{logger:logger.Sugar()}
|
|
||||||
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
|
|
||||||
// It is up to the handler to determine the synchronization to the output
|
|
||||||
// streams
|
|
||||||
func (z *zapLogger) SetHandler(lh LogHandler) {
|
|
||||||
// Swap out the logger when a new handler is attached
|
|
||||||
encoder := &zapEncoder{lh}
|
|
||||||
levelHandler := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
|
|
||||||
return lvl >= zapcore.Level(lh.GetLevel())
|
|
||||||
})
|
|
||||||
logger := zap.New(zapcore.NewCore(encoder, nil, levelHandler)).WithOptions(zap.AddCaller())
|
|
||||||
Logger.With("foo","bar").Desugar().Core()
|
|
||||||
}
|
|
||||||
|
|
||||||
var Logger *zap.SugaredLogger
|
|
||||||
|
|
||||||
func InitLogger(logLevel zapcore.Level) {
|
|
||||||
config :=zap.NewDevelopmentEncoderConfig()
|
|
||||||
config.EncodeLevel = zapcore.CapitalColorLevelEncoder
|
|
||||||
|
|
||||||
consoleEncoder := NewConsoleEncoder(config)
|
|
||||||
lowPriority := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
|
|
||||||
return lvl >= logLevel
|
|
||||||
})
|
|
||||||
|
|
||||||
consoleDebugging := zapcore.Lock(os.Stdout)
|
|
||||||
core := zapcore.NewTee(
|
|
||||||
zapcore.NewCore(consoleEncoder, consoleDebugging, lowPriority),
|
|
||||||
)
|
|
||||||
logger := zap.New(core).WithOptions(zap.AddCaller())
|
|
||||||
Logger = logger.Sugar()
|
|
||||||
}
|
|
||||||
type LoggedError struct{ error }
|
|
||||||
|
|
||||||
func NewLoggedError(err error) *LoggedError {
|
|
||||||
return &LoggedError{err}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Errorf(format string, args ...interface{}) {
|
|
||||||
// Ensure the user's command prompt starts on the next line.
|
|
||||||
if !strings.HasSuffix(format, "\n") {
|
|
||||||
format += "\n"
|
|
||||||
}
|
|
||||||
fmt.Fprintf(os.Stderr, format, args...)
|
|
||||||
panic(format) // Panic instead of os.Exit so that deferred will run.
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// This is all for the Console logger - a little wordy but it works
|
|
||||||
|
|
||||||
var _sliceEncoderPool = sync.Pool{
|
|
||||||
New: func() interface{} {
|
|
||||||
return &sliceArrayEncoder{elems: make([]interface{}, 0, 2)}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func getSliceEncoder() *sliceArrayEncoder {
|
|
||||||
return _sliceEncoderPool.Get().(*sliceArrayEncoder)
|
|
||||||
}
|
|
||||||
|
|
||||||
func putSliceEncoder(e *sliceArrayEncoder) {
|
|
||||||
e.elems = e.elems[:0]
|
|
||||||
_sliceEncoderPool.Put(e)
|
|
||||||
}
|
|
||||||
|
|
||||||
type consoleEncoder struct {
|
|
||||||
*zapcore.EncoderConfig
|
|
||||||
openNamespaces int
|
|
||||||
buf *buffer.Buffer
|
|
||||||
reflectBuf *buffer.Buffer
|
|
||||||
reflectEnc *json.Encoder
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
_pool = buffer.NewPool()
|
|
||||||
// Get retrieves a buffer from the pool, creating one if necessary.
|
|
||||||
Get = _pool.Get
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
// NewConsoleEncoder creates an encoder whose output is designed for human -
|
|
||||||
// rather than machine - consumption. It serializes the core log entry data
|
|
||||||
// (message, level, timestamp, etc.) in a plain-text format and leaves the
|
|
||||||
// structured context as JSON.
|
|
||||||
//
|
|
||||||
// Note that although the console encoder doesn't use the keys specified in the
|
|
||||||
// encoder configuration, it will omit any element whose key is set to the empty
|
|
||||||
// string.
|
|
||||||
func NewConsoleEncoder(cfg zapcore.EncoderConfig) zapcore.Encoder {
|
|
||||||
ec := &consoleEncoder{buf : Get(), reflectBuf: Get()}
|
|
||||||
ec.EncoderConfig = &cfg
|
|
||||||
return ec
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c consoleEncoder) Clone() zapcore.Encoder {
|
|
||||||
return &consoleEncoder{buf : Get(), reflectBuf: Get()}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c consoleEncoder) EncodeEntry(ent zapcore.Entry, fields []zapcore.Field) (*buffer.Buffer, error) {
|
|
||||||
line := Get()
|
|
||||||
|
|
||||||
var color = 0
|
|
||||||
switch ent.Level {
|
|
||||||
case zap.PanicLevel:
|
|
||||||
// Magenta
|
|
||||||
color = 35
|
|
||||||
case zap.ErrorLevel:
|
|
||||||
// Red
|
|
||||||
color = 31
|
|
||||||
case zap.WarnLevel:
|
|
||||||
// Yellow
|
|
||||||
color = 33
|
|
||||||
case zap.InfoLevel:
|
|
||||||
// Green
|
|
||||||
color = 32
|
|
||||||
case zap.DebugLevel:
|
|
||||||
// Cyan
|
|
||||||
color = 36
|
|
||||||
}
|
|
||||||
|
|
||||||
// We don't want the entry's metadata to be quoted and escaped (if it's
|
|
||||||
// encoded as strings), which means that we can't use the JSON encoder. The
|
|
||||||
// simplest option is to use the memory encoder and fmt.Fprint.
|
|
||||||
//
|
|
||||||
// If this ever becomes a performance bottleneck, we can implement
|
|
||||||
// ArrayEncoder for our plain-text format.
|
|
||||||
arr := getSliceEncoder()
|
|
||||||
if c.LevelKey != "" && c.EncodeLevel != nil {
|
|
||||||
arr.AppendString(fmt.Sprintf("\x1b[%dm%-5s\x1b[0m",color,ent.Level.CapitalString()))
|
|
||||||
}
|
|
||||||
if ent.LoggerName != "" && c.NameKey != "" {
|
|
||||||
nameEncoder := c.EncodeName
|
|
||||||
|
|
||||||
if nameEncoder == nil {
|
|
||||||
// Fall back to FullNameEncoder for backward compatibility.
|
|
||||||
nameEncoder = zapcore.FullNameEncoder
|
|
||||||
}
|
|
||||||
|
|
||||||
nameEncoder(ent.LoggerName, arr)
|
|
||||||
}
|
|
||||||
if c.TimeKey != "" && c.EncodeTime != nil {
|
|
||||||
arr.AppendString(ent.Time.Format("15:04:05"))
|
|
||||||
}
|
|
||||||
|
|
||||||
if ent.Caller.Defined && c.CallerKey != "" && c.EncodeCaller != nil {
|
|
||||||
c.EncodeCaller(ent.Caller, arr)
|
|
||||||
}
|
|
||||||
for i := range arr.elems {
|
|
||||||
if i > 0 {
|
|
||||||
line.AppendByte(' ')
|
|
||||||
}
|
|
||||||
fmt.Fprint(line, arr.elems[i])
|
|
||||||
}
|
|
||||||
putSliceEncoder(arr)
|
|
||||||
|
|
||||||
// Add the message itself.
|
|
||||||
if c.MessageKey != "" {
|
|
||||||
c.addTabIfNecessary(line)
|
|
||||||
line.AppendString(ent.Message)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add any structured context.
|
|
||||||
c.writeContext(line, fields)
|
|
||||||
|
|
||||||
// If there's no stacktrace key, honor that; this allows users to force
|
|
||||||
// single-line output.
|
|
||||||
if ent.Stack != "" && c.StacktraceKey != "" {
|
|
||||||
line.AppendByte('\n')
|
|
||||||
line.AppendString(ent.Stack)
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.LineEnding != "" {
|
|
||||||
line.AppendString(c.LineEnding)
|
|
||||||
} else {
|
|
||||||
line.AppendString(zapcore.DefaultLineEnding)
|
|
||||||
}
|
|
||||||
return line, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c consoleEncoder) writeContext(line *buffer.Buffer, extra []zapcore.Field) {
|
|
||||||
context := c.Clone().(*consoleEncoder)
|
|
||||||
defer context.buf.Free()
|
|
||||||
//
|
|
||||||
addFields(context, extra)
|
|
||||||
context.closeOpenNamespaces()
|
|
||||||
if context.buf.Len() == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
//
|
|
||||||
line.Write(context.buf.Bytes())
|
|
||||||
}
|
|
||||||
func addFields(enc zapcore.ObjectEncoder, fields []zapcore.Field) {
|
|
||||||
for i := range fields {
|
|
||||||
fields[i].AddTo(enc)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
func (c consoleEncoder) addTabIfNecessary(line *buffer.Buffer) {
|
|
||||||
if line.Len() > 0 {
|
|
||||||
line.AppendByte('\t')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
func (enc *consoleEncoder) AddArray(key string, arr zapcore.ArrayMarshaler) error {
|
|
||||||
enc.addKey(key)
|
|
||||||
return enc.AppendArray(arr)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (enc *consoleEncoder) AddObject(key string, obj zapcore.ObjectMarshaler) error {
|
|
||||||
enc.addKey(key)
|
|
||||||
return enc.AppendObject(obj)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (enc *consoleEncoder) AddBinary(key string, val []byte) {
|
|
||||||
enc.AddString(key, base64.StdEncoding.EncodeToString(val))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (enc *consoleEncoder) AddByteString(key string, val []byte) {
|
|
||||||
enc.addKey(key)
|
|
||||||
enc.AppendByteString(val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (enc *consoleEncoder) AddBool(key string, val bool) {
|
|
||||||
enc.addKey(key)
|
|
||||||
enc.AppendBool(val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (enc *consoleEncoder) AddComplex128(key string, val complex128) {
|
|
||||||
enc.addKey(key)
|
|
||||||
enc.AppendComplex128(val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (enc *consoleEncoder) AddDuration(key string, val time.Duration) {
|
|
||||||
enc.addKey(key)
|
|
||||||
enc.AppendDuration(val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (enc *consoleEncoder) AddFloat64(key string, val float64) {
|
|
||||||
enc.addKey(key)
|
|
||||||
enc.AppendFloat64(val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (enc *consoleEncoder) AddInt64(key string, val int64) {
|
|
||||||
enc.addKey(key)
|
|
||||||
enc.AppendInt64(val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (enc *consoleEncoder) AddReflected(key string, obj interface{}) error {
|
|
||||||
enc.resetReflectBuf()
|
|
||||||
err := enc.reflectEnc.Encode(obj)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
enc.reflectBuf.TrimNewline()
|
|
||||||
enc.addKey(key)
|
|
||||||
_, err = enc.buf.Write(enc.reflectBuf.Bytes())
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (enc *consoleEncoder) OpenNamespace(key string) {
|
|
||||||
enc.addKey(key)
|
|
||||||
enc.buf.AppendByte('{')
|
|
||||||
enc.openNamespaces++
|
|
||||||
}
|
|
||||||
|
|
||||||
func (enc *consoleEncoder) AddString(key, val string) {
|
|
||||||
enc.addKey(key)
|
|
||||||
enc.AppendString(val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (enc *consoleEncoder) AddTime(key string, val time.Time) {
|
|
||||||
enc.addKey(key)
|
|
||||||
enc.AppendTime(val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (enc *consoleEncoder) AddUint64(key string, val uint64) {
|
|
||||||
enc.addKey(key)
|
|
||||||
enc.AppendUint64(val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (enc *consoleEncoder) addKey(key string) {
|
|
||||||
// Print key in different color
|
|
||||||
enc.buf.AppendString(fmt.Sprintf(" \x1b[%dm%s\x1b[0m",36,key))
|
|
||||||
|
|
||||||
enc.buf.AppendByte('=')
|
|
||||||
}
|
|
||||||
|
|
||||||
func (enc *consoleEncoder) AppendArray(arr zapcore.ArrayMarshaler) error {
|
|
||||||
enc.buf.AppendByte('[')
|
|
||||||
err := arr.MarshalLogArray(enc)
|
|
||||||
enc.buf.AppendByte(']')
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (enc *consoleEncoder) AppendObject(obj zapcore.ObjectMarshaler) error {
|
|
||||||
enc.buf.AppendByte('{')
|
|
||||||
err := obj.MarshalLogObject(enc)
|
|
||||||
enc.buf.AppendByte('}')
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (enc *consoleEncoder) AppendBool(val bool) {
|
|
||||||
enc.buf.AppendBool(val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (enc *consoleEncoder) AppendByteString(val []byte) {
|
|
||||||
enc.buf.AppendByte('"')
|
|
||||||
enc.safeAddByteString(val)
|
|
||||||
enc.buf.AppendByte('"')
|
|
||||||
}
|
|
||||||
|
|
||||||
func (enc *consoleEncoder) AppendComplex128(val complex128) {
|
|
||||||
// Cast to a platform-independent, fixed-size type.
|
|
||||||
r, i := float64(real(val)), float64(imag(val))
|
|
||||||
enc.buf.AppendByte('"')
|
|
||||||
// Because we're always in a quoted string, we can use strconv without
|
|
||||||
// special-casing NaN and +/-Inf.
|
|
||||||
enc.buf.AppendFloat(r, 64)
|
|
||||||
enc.buf.AppendByte('+')
|
|
||||||
enc.buf.AppendFloat(i, 64)
|
|
||||||
enc.buf.AppendByte('i')
|
|
||||||
enc.buf.AppendByte('"')
|
|
||||||
}
|
|
||||||
|
|
||||||
func (enc *consoleEncoder) AppendDuration(val time.Duration) {
|
|
||||||
cur := enc.buf.Len()
|
|
||||||
enc.EncodeDuration(val, enc)
|
|
||||||
if cur == enc.buf.Len() {
|
|
||||||
// User-supplied EncodeDuration is a no-op. Fall back to nanoseconds to keep
|
|
||||||
// JSON valid.
|
|
||||||
enc.AppendInt64(int64(val))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (enc *consoleEncoder) AppendInt64(val int64) {
|
|
||||||
enc.buf.AppendInt(val)
|
|
||||||
}
|
|
||||||
func (enc *consoleEncoder) resetReflectBuf() {
|
|
||||||
if enc.reflectBuf == nil {
|
|
||||||
enc.reflectBuf = Get()
|
|
||||||
enc.reflectEnc = json.NewEncoder(enc.reflectBuf)
|
|
||||||
} else {
|
|
||||||
enc.reflectBuf.Reset()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func (enc *consoleEncoder) AppendReflected(val interface{}) error {
|
|
||||||
enc.resetReflectBuf()
|
|
||||||
err := enc.reflectEnc.Encode(val)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
enc.reflectBuf.TrimNewline()
|
|
||||||
_, err = enc.buf.Write(enc.reflectBuf.Bytes())
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (enc *consoleEncoder) AppendString(val string) {
|
|
||||||
enc.safeAddString(val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (enc *consoleEncoder) AppendTime(val time.Time) {
|
|
||||||
cur := enc.buf.Len()
|
|
||||||
enc.EncodeTime(val, enc)
|
|
||||||
if cur == enc.buf.Len() {
|
|
||||||
// User-supplied EncodeTime is a no-op. Fall back to nanos since epoch to keep
|
|
||||||
// output JSON valid.
|
|
||||||
enc.AppendInt64(val.UnixNano())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (enc *consoleEncoder) AppendUint64(val uint64) {
|
|
||||||
enc.buf.AppendUint(val)
|
|
||||||
}
|
|
||||||
func (enc *consoleEncoder) appendFloat(val float64, bitSize int) {
|
|
||||||
switch {
|
|
||||||
case math.IsNaN(val):
|
|
||||||
enc.buf.AppendString(`"NaN"`)
|
|
||||||
case math.IsInf(val, 1):
|
|
||||||
enc.buf.AppendString(`"+Inf"`)
|
|
||||||
case math.IsInf(val, -1):
|
|
||||||
enc.buf.AppendString(`"-Inf"`)
|
|
||||||
default:
|
|
||||||
enc.buf.AppendFloat(val, bitSize)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// safeAddString JSON-escapes a string and appends it to the internal buffer.
|
|
||||||
// Unlike the standard library's encoder, it doesn't attempt to protect the
|
|
||||||
// user from browser vulnerabilities or JSONP-related problems.
|
|
||||||
func (enc *consoleEncoder) safeAddString(s string) {
|
|
||||||
for i := 0; i < len(s); {
|
|
||||||
if enc.tryAddRuneSelf(s[i]) {
|
|
||||||
i++
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
r, size := utf8.DecodeRuneInString(s[i:])
|
|
||||||
if enc.tryAddRuneError(r, size) {
|
|
||||||
i++
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
enc.buf.AppendString(s[i : i+size])
|
|
||||||
i += size
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// safeAddByteString is no-alloc equivalent of safeAddString(string(s)) for s []byte.
|
|
||||||
func (enc *consoleEncoder) safeAddByteString(s []byte) {
|
|
||||||
for i := 0; i < len(s); {
|
|
||||||
if enc.tryAddRuneSelf(s[i]) {
|
|
||||||
i++
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
r, size := utf8.DecodeRune(s[i:])
|
|
||||||
if enc.tryAddRuneError(r, size) {
|
|
||||||
i++
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
enc.buf.Write(s[i : i+size])
|
|
||||||
i += size
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// tryAddRuneSelf appends b if it is valid UTF-8 character represented in a single byte.
|
|
||||||
func (enc *consoleEncoder) tryAddRuneSelf(b byte) bool {
|
|
||||||
if b >= utf8.RuneSelf {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if 0x20 <= b && b != '\\' && b != '"' {
|
|
||||||
enc.buf.AppendByte(b)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
switch b {
|
|
||||||
case '\\', '"':
|
|
||||||
enc.buf.AppendByte('\\')
|
|
||||||
enc.buf.AppendByte(b)
|
|
||||||
case '\n':
|
|
||||||
enc.buf.AppendByte('\\')
|
|
||||||
enc.buf.AppendByte('n')
|
|
||||||
case '\r':
|
|
||||||
enc.buf.AppendByte('\\')
|
|
||||||
enc.buf.AppendByte('r')
|
|
||||||
case '\t':
|
|
||||||
enc.buf.AppendByte('\\')
|
|
||||||
enc.buf.AppendByte('t')
|
|
||||||
default:
|
|
||||||
// Encode bytes < 0x20, except for the escape sequences above.
|
|
||||||
enc.buf.AppendString(`\u00`)
|
|
||||||
enc.buf.AppendByte(_hex[b>>4])
|
|
||||||
enc.buf.AppendByte(_hex[b&0xF])
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
func (enc *consoleEncoder) closeOpenNamespaces() {
|
|
||||||
for i := 0; i < enc.openNamespaces; i++ {
|
|
||||||
enc.buf.AppendByte('}')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
func (enc *consoleEncoder) tryAddRuneError(r rune, size int) bool {
|
|
||||||
if r == utf8.RuneError && size == 1 {
|
|
||||||
enc.buf.AppendString(`\ufffd`)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
func (enc *consoleEncoder) AddComplex64(k string, v complex64) { enc.AddComplex128(k, complex128(v)) }
|
|
||||||
func (enc *consoleEncoder) AddFloat32(k string, v float32) { enc.AddFloat64(k, float64(v)) }
|
|
||||||
func (enc *consoleEncoder) AddInt(k string, v int) { enc.AddInt64(k, int64(v)) }
|
|
||||||
func (enc *consoleEncoder) AddInt32(k string, v int32) { enc.AddInt64(k, int64(v)) }
|
|
||||||
func (enc *consoleEncoder) AddInt16(k string, v int16) { enc.AddInt64(k, int64(v)) }
|
|
||||||
func (enc *consoleEncoder) AddInt8(k string, v int8) { enc.AddInt64(k, int64(v)) }
|
|
||||||
func (enc *consoleEncoder) AddUint(k string, v uint) { enc.AddUint64(k, uint64(v)) }
|
|
||||||
func (enc *consoleEncoder) AddUint32(k string, v uint32) { enc.AddUint64(k, uint64(v)) }
|
|
||||||
func (enc *consoleEncoder) AddUint16(k string, v uint16) { enc.AddUint64(k, uint64(v)) }
|
|
||||||
func (enc *consoleEncoder) AddUint8(k string, v uint8) { enc.AddUint64(k, uint64(v)) }
|
|
||||||
func (enc *consoleEncoder) AddUintptr(k string, v uintptr) { enc.AddUint64(k, uint64(v)) }
|
|
||||||
func (enc *consoleEncoder) AppendComplex64(v complex64) { enc.AppendComplex128(complex128(v)) }
|
|
||||||
func (enc *consoleEncoder) AppendFloat64(v float64) { enc.appendFloat(v, 64) }
|
|
||||||
func (enc *consoleEncoder) AppendFloat32(v float32) { enc.appendFloat(float64(v), 32) }
|
|
||||||
func (enc *consoleEncoder) AppendInt(v int) { enc.AppendInt64(int64(v)) }
|
|
||||||
func (enc *consoleEncoder) AppendInt32(v int32) { enc.AppendInt64(int64(v)) }
|
|
||||||
func (enc *consoleEncoder) AppendInt16(v int16) { enc.AppendInt64(int64(v)) }
|
|
||||||
func (enc *consoleEncoder) AppendInt8(v int8) { enc.AppendInt64(int64(v)) }
|
|
||||||
func (enc *consoleEncoder) AppendUint(v uint) { enc.AppendUint64(uint64(v)) }
|
|
||||||
func (enc *consoleEncoder) AppendUint32(v uint32) { enc.AppendUint64(uint64(v)) }
|
|
||||||
func (enc *consoleEncoder) AppendUint16(v uint16) { enc.AppendUint64(uint64(v)) }
|
|
||||||
func (enc *consoleEncoder) AppendUint8(v uint8) { enc.AppendUint64(uint64(v)) }
|
|
||||||
func (enc *consoleEncoder) AppendUintptr(v uintptr) { enc.AppendUint64(uint64(v)) }
|
|
||||||
|
|
||||||
const _hex = "0123456789abcdef"
|
|
||||||
|
|
||||||
type sliceArrayEncoder struct {
|
|
||||||
elems []interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *sliceArrayEncoder) AppendArray(v zapcore.ArrayMarshaler) error {
|
|
||||||
enc := &sliceArrayEncoder{}
|
|
||||||
err := v.MarshalLogArray(enc)
|
|
||||||
s.elems = append(s.elems, enc.elems)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *sliceArrayEncoder) AppendObject(v zapcore.ObjectMarshaler) error {
|
|
||||||
m := zapcore.NewMapObjectEncoder()
|
|
||||||
err := v.MarshalLogObject(m)
|
|
||||||
s.elems = append(s.elems, m.Fields)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *sliceArrayEncoder) AppendReflected(v interface{}) error {
|
|
||||||
s.elems = append(s.elems, v)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *sliceArrayEncoder) AppendBool(v bool) { s.elems = append(s.elems, v) }
|
|
||||||
func (s *sliceArrayEncoder) AppendByteString(v []byte) { s.elems = append(s.elems, v) }
|
|
||||||
func (s *sliceArrayEncoder) AppendComplex128(v complex128) { s.elems = append(s.elems, v) }
|
|
||||||
func (s *sliceArrayEncoder) AppendComplex64(v complex64) { s.elems = append(s.elems, v) }
|
|
||||||
func (s *sliceArrayEncoder) AppendDuration(v time.Duration) { s.elems = append(s.elems, v) }
|
|
||||||
func (s *sliceArrayEncoder) AppendFloat64(v float64) { s.elems = append(s.elems, v) }
|
|
||||||
func (s *sliceArrayEncoder) AppendFloat32(v float32) { s.elems = append(s.elems, v) }
|
|
||||||
func (s *sliceArrayEncoder) AppendInt(v int) { s.elems = append(s.elems, v) }
|
|
||||||
func (s *sliceArrayEncoder) AppendInt64(v int64) { s.elems = append(s.elems, v) }
|
|
||||||
func (s *sliceArrayEncoder) AppendInt32(v int32) { s.elems = append(s.elems, v) }
|
|
||||||
func (s *sliceArrayEncoder) AppendInt16(v int16) { s.elems = append(s.elems, v) }
|
|
||||||
func (s *sliceArrayEncoder) AppendInt8(v int8) { s.elems = append(s.elems, v) }
|
|
||||||
func (s *sliceArrayEncoder) AppendString(v string) { s.elems = append(s.elems, v) }
|
|
||||||
func (s *sliceArrayEncoder) AppendTime(v time.Time) { s.elems = append(s.elems, v) }
|
|
||||||
func (s *sliceArrayEncoder) AppendUint(v uint) { s.elems = append(s.elems, v) }
|
|
||||||
func (s *sliceArrayEncoder) AppendUint64(v uint64) { s.elems = append(s.elems, v) }
|
|
||||||
func (s *sliceArrayEncoder) AppendUint32(v uint32) { s.elems = append(s.elems, v) }
|
|
||||||
func (s *sliceArrayEncoder) AppendUint16(v uint16) { s.elems = append(s.elems, v) }
|
|
||||||
func (s *sliceArrayEncoder) AppendUint8(v uint8) { s.elems = append(s.elems, v) }
|
|
||||||
func (s *sliceArrayEncoder) AppendUintptr(v uintptr) { s.elems = append(s.elems, v) }
|
|
||||||
37
logger/log_function_map.go
Normal file
37
logger/log_function_map.go
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
package logger
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// The log function map can be added to, so that you can specify your own logging mechanism
|
||||||
|
// it has defaults for off, stdout, stderr
|
||||||
|
var LogFunctionMap = map[string]func(*CompositeMultiHandler, *LogOptions){
|
||||||
|
// Do nothing - set the logger off
|
||||||
|
"off": func(c *CompositeMultiHandler, logOptions *LogOptions) {
|
||||||
|
// Only drop the results if there is a parent handler defined
|
||||||
|
if logOptions.HandlerWrap != nil {
|
||||||
|
for _, l := range logOptions.Levels {
|
||||||
|
c.SetHandler(logOptions.HandlerWrap.SetChild(NilHandler()), logOptions.ReplaceExistingHandler, l)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Clear existing handler
|
||||||
|
c.SetHandlers(NilHandler(), logOptions)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// Do nothing - set the logger off
|
||||||
|
"": func(*CompositeMultiHandler, *LogOptions) {},
|
||||||
|
// Set the levels to stdout, replace existing
|
||||||
|
"stdout": func(c *CompositeMultiHandler, logOptions *LogOptions) {
|
||||||
|
if logOptions.Ctx != nil {
|
||||||
|
logOptions.SetExtendedOptions(
|
||||||
|
"noColor", !logOptions.Ctx.BoolDefault("log.colorize", true),
|
||||||
|
"smallDate", logOptions.Ctx.BoolDefault("log.smallDate", true))
|
||||||
|
}
|
||||||
|
c.SetTerminal(os.Stdout, logOptions)
|
||||||
|
},
|
||||||
|
// Set the levels to stderr output to terminal
|
||||||
|
"stderr": func(c *CompositeMultiHandler, logOptions *LogOptions) {
|
||||||
|
c.SetTerminal(os.Stderr, logOptions)
|
||||||
|
},
|
||||||
|
}
|
||||||
215
logger/logger.go
215
logger/logger.go
@@ -3,9 +3,7 @@ package logger
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/revel/config"
|
"github.com/revel/config"
|
||||||
"github.com/revel/log15"
|
"time"
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// The LogHandler defines the interface to handle the log records
|
// The LogHandler defines the interface to handle the log records
|
||||||
@@ -13,51 +11,82 @@ type (
|
|||||||
// The Multilogger reduces the number of exposed defined logging variables,
|
// The Multilogger reduces the number of exposed defined logging variables,
|
||||||
// and allows the output to be easily refined
|
// and allows the output to be easily refined
|
||||||
MultiLogger interface {
|
MultiLogger interface {
|
||||||
//log15.Logger
|
// New returns a new Logger that has this logger's context plus the given context
|
||||||
//// New returns a new Logger that has this logger's context plus the given context
|
|
||||||
New(ctx ...interface{}) MultiLogger
|
New(ctx ...interface{}) MultiLogger
|
||||||
//
|
|
||||||
//// SetHandler updates the logger to write records to the specified handler.
|
// SetHandler updates the logger to write records to the specified handler.
|
||||||
SetHandler(h LogHandler)
|
SetHandler(h LogHandler)
|
||||||
|
|
||||||
|
// Set the stack depth for the logger
|
||||||
SetStackDepth(int) MultiLogger
|
SetStackDepth(int) MultiLogger
|
||||||
//
|
|
||||||
//// Log a message at the given level with context key/value pairs
|
// Log a message at the given level with context key/value pairs
|
||||||
Debug(msg string, ctx ...interface{})
|
Debug(msg string, ctx ...interface{})
|
||||||
|
|
||||||
|
// Log a message at the given level formatting message with the parameters
|
||||||
Debugf(msg string, params ...interface{})
|
Debugf(msg string, params ...interface{})
|
||||||
|
|
||||||
|
// Log a message at the given level with context key/value pairs
|
||||||
Info(msg string, ctx ...interface{})
|
Info(msg string, ctx ...interface{})
|
||||||
|
|
||||||
|
// Log a message at the given level formatting message with the parameters
|
||||||
Infof(msg string, params ...interface{})
|
Infof(msg string, params ...interface{})
|
||||||
|
|
||||||
|
// Log a message at the given level with context key/value pairs
|
||||||
Warn(msg string, ctx ...interface{})
|
Warn(msg string, ctx ...interface{})
|
||||||
|
|
||||||
|
// Log a message at the given level formatting message with the parameters
|
||||||
Warnf(msg string, params ...interface{})
|
Warnf(msg string, params ...interface{})
|
||||||
|
|
||||||
|
// Log a message at the given level with context key/value pairs
|
||||||
Error(msg string, ctx ...interface{})
|
Error(msg string, ctx ...interface{})
|
||||||
|
|
||||||
|
// Log a message at the given level formatting message with the parameters
|
||||||
Errorf(msg string, params ...interface{})
|
Errorf(msg string, params ...interface{})
|
||||||
|
|
||||||
|
// Log a message at the given level with context key/value pairs
|
||||||
Crit(msg string, ctx ...interface{})
|
Crit(msg string, ctx ...interface{})
|
||||||
|
|
||||||
|
// Log a message at the given level formatting message with the parameters
|
||||||
Critf(msg string, params ...interface{})
|
Critf(msg string, params ...interface{})
|
||||||
|
|
||||||
//// Logs a message as an Crit and exits
|
// Log a message at the given level with context key/value pairs and exits
|
||||||
Fatal(msg string, ctx ...interface{})
|
Fatal(msg string, ctx ...interface{})
|
||||||
|
|
||||||
|
// Log a message at the given level formatting message with the parameters and exits
|
||||||
Fatalf(msg string, params ...interface{})
|
Fatalf(msg string, params ...interface{})
|
||||||
//// Logs a message as an Crit and panics
|
|
||||||
|
// Log a message at the given level with context key/value pairs and panics
|
||||||
Panic(msg string, ctx ...interface{})
|
Panic(msg string, ctx ...interface{})
|
||||||
|
|
||||||
|
// Log a message at the given level formatting message with the parameters and panics
|
||||||
Panicf(msg string, params ...interface{})
|
Panicf(msg string, params ...interface{})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The log handler interface
|
||||||
LogHandler interface {
|
LogHandler interface {
|
||||||
log15.Handler
|
Log(*Record) error
|
||||||
|
//log15.Handler
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The log stack handler interface
|
||||||
LogStackHandler interface {
|
LogStackHandler interface {
|
||||||
LogHandler
|
LogHandler
|
||||||
GetStack() int
|
GetStack() int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The log handler interface which has child logs
|
||||||
ParentLogHandler interface {
|
ParentLogHandler interface {
|
||||||
SetChild(handler LogHandler) LogHandler
|
SetChild(handler LogHandler) LogHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The log format interface
|
||||||
LogFormat interface {
|
LogFormat interface {
|
||||||
log15.Format
|
Format(r *Record) []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
LogLevel log15.Lvl
|
// The log level type
|
||||||
RevelLogger struct {
|
LogLevel int
|
||||||
log15.Logger
|
|
||||||
}
|
|
||||||
|
|
||||||
// Used for the callback to LogFunctionMap
|
// Used for the callback to LogFunctionMap
|
||||||
LogOptions struct {
|
LogOptions struct {
|
||||||
@@ -67,125 +96,65 @@ type (
|
|||||||
Levels []LogLevel
|
Levels []LogLevel
|
||||||
ExtendedOptions map[string]interface{}
|
ExtendedOptions map[string]interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The log record
|
||||||
|
Record struct {
|
||||||
|
Message string // The message
|
||||||
|
Time time.Time // The time
|
||||||
|
Level LogLevel //The level
|
||||||
|
Call CallStack // The call stack if built
|
||||||
|
Context ContextMap // The context
|
||||||
|
}
|
||||||
|
|
||||||
|
// The lazy structure to implement a function to be invoked only if needed
|
||||||
|
Lazy struct {
|
||||||
|
Fn interface{} // the function
|
||||||
|
}
|
||||||
|
|
||||||
|
// Currently the only requirement for the callstack is to support the Formatter method
|
||||||
|
// which stack.Call does so we use that
|
||||||
|
CallStack interface {
|
||||||
|
fmt.Formatter // Requirement
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// FormatFunc returns a new Format object which uses
|
||||||
|
// the given function to perform record formatting.
|
||||||
|
func FormatFunc(f func(*Record) []byte) LogFormat {
|
||||||
|
return formatFunc(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
type formatFunc func(*Record) []byte
|
||||||
|
|
||||||
|
func (f formatFunc) Format(r *Record) []byte {
|
||||||
|
return f(r)
|
||||||
|
}
|
||||||
|
func NewRecord(message string, level LogLevel) *Record {
|
||||||
|
return &Record{Message: message, Context: ContextMap{}, Level: level}
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
LvlDebug = LogLevel(log15.LvlDebug)
|
LvlCrit LogLevel = iota // Critical
|
||||||
LvlInfo = LogLevel(log15.LvlInfo)
|
LvlError // Error
|
||||||
LvlWarn = LogLevel(log15.LvlWarn)
|
LvlWarn // Warning
|
||||||
LvlError = LogLevel(log15.LvlError)
|
LvlInfo // Information
|
||||||
LvlCrit = LogLevel(log15.LvlCrit)
|
LvlDebug // Debug
|
||||||
)
|
)
|
||||||
|
|
||||||
// A list of all the log levels
|
// A list of all the log levels
|
||||||
var LvlAllList = []LogLevel{LvlDebug, LvlInfo, LvlWarn, LvlError, LvlCrit}
|
var LvlAllList = []LogLevel{LvlDebug, LvlInfo, LvlWarn, LvlError, LvlCrit}
|
||||||
|
|
||||||
// The log function map can be added to, so that you can specify your own logging mechanism
|
// Implements the ParentLogHandler
|
||||||
var LogFunctionMap = map[string]func(*CompositeMultiHandler, *LogOptions){
|
|
||||||
// Do nothing - set the logger off
|
|
||||||
"off": func(c *CompositeMultiHandler, logOptions *LogOptions) {
|
|
||||||
// Only drop the results if there is a parent handler defined
|
|
||||||
if logOptions.HandlerWrap != nil {
|
|
||||||
for _, l := range logOptions.Levels {
|
|
||||||
c.SetHandler(logOptions.HandlerWrap.SetChild(NilHandler()), logOptions.ReplaceExistingHandler, l)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// Do nothing - set the logger off
|
|
||||||
"": func(*CompositeMultiHandler, *LogOptions) {},
|
|
||||||
// Set the levels to stdout, replace existing
|
|
||||||
"stdout": func(c *CompositeMultiHandler, logOptions *LogOptions) {
|
|
||||||
if logOptions.Ctx != nil {
|
|
||||||
logOptions.SetExtendedOptions(
|
|
||||||
"noColor", !logOptions.Ctx.BoolDefault("log.colorize", true),
|
|
||||||
"smallDate", logOptions.Ctx.BoolDefault("log.smallDate", true))
|
|
||||||
}
|
|
||||||
|
|
||||||
c.SetTerminal(os.Stdout, logOptions)
|
|
||||||
},
|
|
||||||
// Set the levels to stderr output to terminal
|
|
||||||
"stderr": func(c *CompositeMultiHandler, logOptions *LogOptions) {
|
|
||||||
c.SetTerminal(os.Stderr, logOptions)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the systems default logger
|
|
||||||
// Default logs will be captured and handled by revel at level info
|
|
||||||
func SetDefaultLog(fromLog MultiLogger) {
|
|
||||||
log.SetOutput(loggerRewrite{Logger: fromLog, Level: log15.LvlInfo, hideDeprecated: true})
|
|
||||||
// No need to show date and time, that will be logged with revel
|
|
||||||
log.SetFlags(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Formatted debug call
|
|
||||||
func (rl *RevelLogger) Debugf(msg string, param ...interface{}) {
|
|
||||||
rl.Debug(fmt.Sprintf(msg, param...))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Formatted info call
|
|
||||||
func (rl *RevelLogger) Infof(msg string, param ...interface{}) {
|
|
||||||
rl.Info(fmt.Sprintf(msg, param...))
|
|
||||||
}
|
|
||||||
func (rl *RevelLogger) Warnf(msg string, param ...interface{}) {
|
|
||||||
rl.Warn(fmt.Sprintf(msg, param...))
|
|
||||||
}
|
|
||||||
func (rl *RevelLogger) Errorf(msg string, param ...interface{}) {
|
|
||||||
rl.Error(fmt.Sprintf(msg, param...))
|
|
||||||
}
|
|
||||||
func (rl *RevelLogger) Critf(msg string, param ...interface{}) {
|
|
||||||
rl.Crit(fmt.Sprintf(msg, param...))
|
|
||||||
}
|
|
||||||
func (rl *RevelLogger) Fatalf(msg string, param ...interface{}) {
|
|
||||||
rl.Crit(fmt.Sprintf(msg, param...))
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
func (rl *RevelLogger) Panicf(msg string, param ...interface{}) {
|
|
||||||
rl.Crit(fmt.Sprintf(msg, param...))
|
|
||||||
panic(msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rl *RevelLogger) Fatal(msg string, ctx ...interface{}) {
|
|
||||||
rl.Crit(msg, ctx...)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rl *RevelLogger) Panic(msg string, ctx ...interface{}) {
|
|
||||||
rl.Crit(msg, ctx...)
|
|
||||||
panic(msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Override log15 method
|
|
||||||
func (rl *RevelLogger) New(ctx ...interface{}) MultiLogger {
|
|
||||||
old := &RevelLogger{Logger: rl.Logger.New(ctx...)}
|
|
||||||
return old
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the stack level to check for the caller
|
|
||||||
func (rl *RevelLogger) SetStackDepth(amount int) MultiLogger {
|
|
||||||
rl.Logger.SetStackDepth(amount) // Ignore the logger returned
|
|
||||||
return rl
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a new logger
|
|
||||||
func New(ctx ...interface{}) MultiLogger {
|
|
||||||
r := &RevelLogger{Logger: log15.New(ctx...)}
|
|
||||||
r.SetStackDepth(1)
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the handler in the Logger
|
|
||||||
func (rl *RevelLogger) SetHandler(h LogHandler) {
|
|
||||||
rl.Logger.SetHandler(h)
|
|
||||||
}
|
|
||||||
|
|
||||||
type parentLogHandler struct {
|
type parentLogHandler struct {
|
||||||
setChild func(handler LogHandler) LogHandler
|
setChild func(handler LogHandler) LogHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create a new parent log handler
|
||||||
func NewParentLogHandler(callBack func(child LogHandler) LogHandler) ParentLogHandler {
|
func NewParentLogHandler(callBack func(child LogHandler) LogHandler) ParentLogHandler {
|
||||||
return &parentLogHandler{callBack}
|
return &parentLogHandler{callBack}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sets the child of the log handler
|
||||||
func (p *parentLogHandler) SetChild(child LogHandler) LogHandler {
|
func (p *parentLogHandler) SetChild(child LogHandler) LogHandler {
|
||||||
return p.setChild(child)
|
return p.setChild(child)
|
||||||
}
|
}
|
||||||
@@ -208,18 +177,24 @@ func (l *LogOptions) SetExtendedOptions(options ...interface{}) {
|
|||||||
l.ExtendedOptions[options[x].(string)] = options[x+1]
|
l.ExtendedOptions[options[x].(string)] = options[x+1]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Gets a string option with default
|
||||||
func (l *LogOptions) GetStringDefault(option, value string) string {
|
func (l *LogOptions) GetStringDefault(option, value string) string {
|
||||||
if v, found := l.ExtendedOptions[option]; found {
|
if v, found := l.ExtendedOptions[option]; found {
|
||||||
return v.(string)
|
return v.(string)
|
||||||
}
|
}
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Gets an int option with default
|
||||||
func (l *LogOptions) GetIntDefault(option string, value int) int {
|
func (l *LogOptions) GetIntDefault(option string, value int) int {
|
||||||
if v, found := l.ExtendedOptions[option]; found {
|
if v, found := l.ExtendedOptions[option]; found {
|
||||||
return v.(int)
|
return v.(int)
|
||||||
}
|
}
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Gets a boolean option with default
|
||||||
func (l *LogOptions) GetBoolDefault(option string, value bool) bool {
|
func (l *LogOptions) GetBoolDefault(option string, value bool) bool {
|
||||||
if v, found := l.ExtendedOptions[option]; found {
|
if v, found := l.ExtendedOptions[option]; found {
|
||||||
return v.(bool)
|
return v.(bool)
|
||||||
|
|||||||
142
logger/revel_logger.go
Normal file
142
logger/revel_logger.go
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
package logger
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/revel/log15"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This type implements the MultiLogger
|
||||||
|
type RevelLogger struct {
|
||||||
|
log15.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the systems default logger
|
||||||
|
// Default logs will be captured and handled by revel at level info
|
||||||
|
func SetDefaultLog(fromLog MultiLogger) {
|
||||||
|
log.SetOutput(loggerRewrite{Logger: fromLog, Level: log15.LvlInfo, hideDeprecated: true})
|
||||||
|
// No need to show date and time, that will be logged with revel
|
||||||
|
log.SetFlags(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rl *RevelLogger) Debugf(msg string, param ...interface{}) {
|
||||||
|
rl.Debug(fmt.Sprintf(msg, param...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print a formatted info message
|
||||||
|
func (rl *RevelLogger) Infof(msg string, param ...interface{}) {
|
||||||
|
rl.Info(fmt.Sprintf(msg, param...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print a formatted warn message
|
||||||
|
func (rl *RevelLogger) Warnf(msg string, param ...interface{}) {
|
||||||
|
rl.Warn(fmt.Sprintf(msg, param...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print a formatted error message
|
||||||
|
func (rl *RevelLogger) Errorf(msg string, param ...interface{}) {
|
||||||
|
rl.Error(fmt.Sprintf(msg, param...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print a formatted critical message
|
||||||
|
func (rl *RevelLogger) Critf(msg string, param ...interface{}) {
|
||||||
|
rl.Crit(fmt.Sprintf(msg, param...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print a formatted fatal message
|
||||||
|
func (rl *RevelLogger) Fatalf(msg string, param ...interface{}) {
|
||||||
|
rl.Fatal(fmt.Sprintf(msg, param...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print a formatted panic message
|
||||||
|
func (rl *RevelLogger) Panicf(msg string, param ...interface{}) {
|
||||||
|
rl.Panic(fmt.Sprintf(msg, param...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print a critical message and call os.Exit(1)
|
||||||
|
func (rl *RevelLogger) Fatal(msg string, ctx ...interface{}) {
|
||||||
|
rl.Crit(msg, ctx...)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print a critical message and panic
|
||||||
|
func (rl *RevelLogger) Panic(msg string, ctx ...interface{}) {
|
||||||
|
rl.Crit(msg, ctx...)
|
||||||
|
panic(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Override log15 method
|
||||||
|
func (rl *RevelLogger) New(ctx ...interface{}) MultiLogger {
|
||||||
|
old := &RevelLogger{Logger: rl.Logger.New(ctx...)}
|
||||||
|
return old
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the stack level to check for the caller
|
||||||
|
func (rl *RevelLogger) SetStackDepth(amount int) MultiLogger {
|
||||||
|
rl.Logger.SetStackDepth(amount) // Ignore the logger returned
|
||||||
|
return rl
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new logger
|
||||||
|
func New(ctx ...interface{}) MultiLogger {
|
||||||
|
r := &RevelLogger{Logger: log15.New(ctx...)}
|
||||||
|
r.SetStackDepth(1)
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the handler in the Logger
|
||||||
|
func (rl *RevelLogger) SetHandler(h LogHandler) {
|
||||||
|
rl.Logger.SetHandler(callHandler(h.Log))
|
||||||
|
}
|
||||||
|
|
||||||
|
// The function wrapper to implement the callback
|
||||||
|
type callHandler func(r *Record) error
|
||||||
|
|
||||||
|
// Log implementation, reads the record and extracts the details from the log record
|
||||||
|
// Hiding the implementation.
|
||||||
|
func (c callHandler) Log(log *log15.Record) error {
|
||||||
|
ctx := log.Ctx
|
||||||
|
var ctxMap ContextMap
|
||||||
|
if len(ctx) > 0 {
|
||||||
|
ctxMap = make(ContextMap, len(ctx)/2)
|
||||||
|
|
||||||
|
for i := 0; i < len(ctx); i += 2 {
|
||||||
|
v := ctx[i]
|
||||||
|
key, ok := v.(string)
|
||||||
|
if !ok {
|
||||||
|
key = fmt.Sprintf("LOGGER_INVALID_KEY %v", v)
|
||||||
|
}
|
||||||
|
var value interface{}
|
||||||
|
if len(ctx) > i+1 {
|
||||||
|
value = ctx[i+1]
|
||||||
|
} else {
|
||||||
|
value = "LOGGER_VALUE_MISSING"
|
||||||
|
}
|
||||||
|
ctxMap[key] = value
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ctxMap = make(ContextMap, 0)
|
||||||
|
}
|
||||||
|
r := &Record{Message: log.Msg, Context: ctxMap, Time: log.Time, Level: LogLevel(log.Lvl), Call: CallStack(log.Call)}
|
||||||
|
return c(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internally used contextMap, allows conversion of map to map[string]string
|
||||||
|
type ContextMap map[string]interface{}
|
||||||
|
|
||||||
|
// Convert the context map to be string only values, any non string values are ignored
|
||||||
|
func (m ContextMap) StringMap() (newMap map[string]string) {
|
||||||
|
if m != nil {
|
||||||
|
newMap = map[string]string{}
|
||||||
|
for key, value := range m {
|
||||||
|
if svalue, isstring := value.(string); isstring {
|
||||||
|
newMap[key] = svalue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (m ContextMap) Add(key string, value interface{}) {
|
||||||
|
m[key] = value
|
||||||
|
}
|
||||||
@@ -2,8 +2,8 @@ package logger
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/revel/log15"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
@@ -19,9 +19,8 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// Name the log level
|
levelString = map[LogLevel]string{LvlDebug: "DEBUG",
|
||||||
toRevel = map[log15.Lvl]string{log15.LvlDebug: "DEBUG",
|
LvlInfo: "INFO", LvlWarn: "WARN", LvlError: "ERROR", LvlCrit: "CRIT"}
|
||||||
log15.LvlInfo: "INFO", log15.LvlWarn: "WARN", log15.LvlError: "ERROR", log15.LvlCrit: "CRIT"}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Outputs to the terminal in a format like below
|
// Outputs to the terminal in a format like below
|
||||||
@@ -31,53 +30,51 @@ func TerminalFormatHandler(noColor bool, smallDate bool) LogFormat {
|
|||||||
if smallDate {
|
if smallDate {
|
||||||
dateFormat = termSmallTimeFormat
|
dateFormat = termSmallTimeFormat
|
||||||
}
|
}
|
||||||
return log15.FormatFunc(func(r *log15.Record) []byte {
|
return FormatFunc(func(r *Record) []byte {
|
||||||
// Bash coloring http://misc.flogisoft.com/bash/tip_colors_and_formatting
|
// Bash coloring http://misc.flogisoft.com/bash/tip_colors_and_formatting
|
||||||
var color = 0
|
var color = 0
|
||||||
switch r.Lvl {
|
switch r.Level {
|
||||||
case log15.LvlCrit:
|
case LvlCrit:
|
||||||
// Magenta
|
// Magenta
|
||||||
color = 35
|
color = 35
|
||||||
case log15.LvlError:
|
case LvlError:
|
||||||
// Red
|
// Red
|
||||||
color = 31
|
color = 31
|
||||||
case log15.LvlWarn:
|
case LvlWarn:
|
||||||
// Yellow
|
// Yellow
|
||||||
color = 33
|
color = 33
|
||||||
case log15.LvlInfo:
|
case LvlInfo:
|
||||||
// Green
|
// Green
|
||||||
color = 32
|
color = 32
|
||||||
case log15.LvlDebug:
|
case LvlDebug:
|
||||||
// Cyan
|
// Cyan
|
||||||
color = 36
|
color = 36
|
||||||
}
|
}
|
||||||
|
|
||||||
b := &bytes.Buffer{}
|
b := &bytes.Buffer{}
|
||||||
caller := findInContext("caller", r.Ctx)
|
caller, _ := r.Context["caller"].(string)
|
||||||
module := findInContext("module", r.Ctx)
|
module, _ := r.Context["module"].(string)
|
||||||
if noColor == false && color > 0 {
|
if noColor == false && color > 0 {
|
||||||
if len(module) > 0 {
|
if len(module) > 0 {
|
||||||
fmt.Fprintf(b, "\x1b[%dm%-5s\x1b[0m %s %6s %13s: %-40s ", color, toRevel[r.Lvl], r.Time.Format(dateFormat), module, caller, r.Msg)
|
fmt.Fprintf(b, "\x1b[%dm%-5s\x1b[0m %s %6s %13s: %-40s ", color, levelString[r.Level], r.Time.Format(dateFormat), module, caller, r.Message)
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprintf(b, "\x1b[%dm%-5s\x1b[0m %s %13s: %-40s ", color, toRevel[r.Lvl], r.Time.Format(dateFormat), caller, r.Msg)
|
fmt.Fprintf(b, "\x1b[%dm%-5s\x1b[0m %s %13s: %-40s ", color, levelString[r.Level], r.Time.Format(dateFormat), caller, r.Message)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprintf(b, "%-5s %s %6s %13s: %-40s", toRevel[r.Lvl], r.Time.Format(dateFormat), module, caller, r.Msg)
|
fmt.Fprintf(b, "%-5s %s %6s %13s: %-40s", levelString[r.Level], r.Time.Format(dateFormat), module, caller, r.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < len(r.Ctx); i += 2 {
|
i := 0
|
||||||
|
for k, v := range r.Context {
|
||||||
if i != 0 {
|
if i != 0 {
|
||||||
b.WriteByte(' ')
|
b.WriteByte(' ')
|
||||||
}
|
}
|
||||||
|
i++
|
||||||
k, ok := r.Ctx[i].(string)
|
if k == "module" || k == "caller" {
|
||||||
if k == "caller" || k == "fn" || k == "module" {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
v := formatLogfmtValue(r.Ctx[i+1])
|
|
||||||
if !ok {
|
v := formatLogfmtValue(v)
|
||||||
k, v = errorKey, formatLogfmtValue(k)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: we should probably check that all of your key bytes aren't invalid
|
// TODO: we should probably check that all of your key bytes aren't invalid
|
||||||
if noColor == false && color > 0 {
|
if noColor == false && color > 0 {
|
||||||
@@ -94,15 +91,6 @@ func TerminalFormatHandler(noColor bool, smallDate bool) LogFormat {
|
|||||||
return b.Bytes()
|
return b.Bytes()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
func findInContext(key string, ctx []interface{}) string {
|
|
||||||
for i := 0; i < len(ctx); i += 2 {
|
|
||||||
k := ctx[i].(string)
|
|
||||||
if key == k {
|
|
||||||
return formatLogfmtValue(ctx[i+1])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// formatValue formats a value for serialization
|
// formatValue formats a value for serialization
|
||||||
func formatLogfmtValue(value interface{}) string {
|
func formatLogfmtValue(value interface{}) string {
|
||||||
@@ -132,6 +120,8 @@ func formatLogfmtValue(value interface{}) string {
|
|||||||
return escapeString(fmt.Sprintf("%+v", value))
|
return escapeString(fmt.Sprintf("%+v", value))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Format the value in json format
|
||||||
func formatShared(value interface{}) (result interface{}) {
|
func formatShared(value interface{}) (result interface{}) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if err := recover(); err != nil {
|
if err := recover(); err != nil {
|
||||||
@@ -158,10 +148,12 @@ func formatShared(value interface{}) (result interface{}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A reusuable buffer for outputting data
|
||||||
var stringBufPool = sync.Pool{
|
var stringBufPool = sync.Pool{
|
||||||
New: func() interface{} { return new(bytes.Buffer) },
|
New: func() interface{} { return new(bytes.Buffer) },
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Escape the string when needed
|
||||||
func escapeString(s string) string {
|
func escapeString(s string) string {
|
||||||
needsQuotes := false
|
needsQuotes := false
|
||||||
needsEscape := false
|
needsEscape := false
|
||||||
@@ -204,3 +196,50 @@ func escapeString(s string) string {
|
|||||||
stringBufPool.Put(e)
|
stringBufPool.Put(e)
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// JsonFormatEx formats log records as JSON objects. If pretty is true,
|
||||||
|
// records will be pretty-printed. If lineSeparated is true, records
|
||||||
|
// will be logged with a new line between each record.
|
||||||
|
func JsonFormatEx(pretty, lineSeparated bool) LogFormat {
|
||||||
|
jsonMarshal := json.Marshal
|
||||||
|
if pretty {
|
||||||
|
jsonMarshal = func(v interface{}) ([]byte, error) {
|
||||||
|
return json.MarshalIndent(v, "", " ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return FormatFunc(func(r *Record) []byte {
|
||||||
|
props := make(map[string]interface{})
|
||||||
|
|
||||||
|
props["t"] = r.Time
|
||||||
|
props["lvl"] = levelString[r.Level]
|
||||||
|
props["msg"] = r.Message
|
||||||
|
for k, v := range r.Context {
|
||||||
|
props[k] = formatJsonValue(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := jsonMarshal(props)
|
||||||
|
if err != nil {
|
||||||
|
b, _ = jsonMarshal(map[string]string{
|
||||||
|
errorKey: err.Error(),
|
||||||
|
})
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
if lineSeparated {
|
||||||
|
b = append(b, '\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
return b
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatJsonValue(value interface{}) interface{} {
|
||||||
|
value = formatShared(value)
|
||||||
|
switch value.(type) {
|
||||||
|
case int, int8, int16, int32, int64, float32, float64, uint, uint8, uint16, uint32, uint64, string:
|
||||||
|
return value
|
||||||
|
default:
|
||||||
|
return fmt.Sprintf("%+v", value)
|
||||||
|
}
|
||||||
|
}
|
||||||
226
logger/utils.go
226
logger/utils.go
@@ -1,13 +1,9 @@
|
|||||||
package logger
|
package logger
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/revel/config"
|
|
||||||
"github.com/revel/log15"
|
"github.com/revel/log15"
|
||||||
"gopkg.in/stack.v0"
|
"gopkg.in/stack.v0"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Utility package to make existing logging backwards compatible
|
// Utility package to make existing logging backwards compatible
|
||||||
@@ -20,6 +16,14 @@ var (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// The test mode flag overrides the default log level and shows only errors
|
||||||
|
TEST_MODE_FLAG = "testModeFlag"
|
||||||
|
// The special use flag enables showing messages when the logger is setup
|
||||||
|
SPECIAL_USE_FLAG = "specialUseFlag"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Returns the logger for the name
|
||||||
func GetLogger(name string, logger MultiLogger) (l *log.Logger) {
|
func GetLogger(name string, logger MultiLogger) (l *log.Logger) {
|
||||||
switch name {
|
switch name {
|
||||||
case "trace": // TODO trace is deprecated, replaced by debug
|
case "trace": // TODO trace is deprecated, replaced by debug
|
||||||
@@ -40,198 +44,26 @@ func GetLogger(name string, logger MultiLogger) (l *log.Logger) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get all handlers based on the Config (if available)
|
// Used by the initFilterLog to handle the filters
|
||||||
func InitializeFromConfig(basePath string, config *config.Context) (c *CompositeMultiHandler) {
|
var logFilterList = []struct {
|
||||||
// If running in test mode suppress anything that is not an error
|
LogPrefix, LogSuffix string
|
||||||
if config != nil && config.BoolDefault("testModeFlag", false) {
|
parentHandler func(map[string]interface{}) ParentLogHandler
|
||||||
config.SetOption("log.info.output", "off")
|
}{{
|
||||||
config.SetOption("log.debug.output", "off")
|
"log.", ".filter",
|
||||||
config.SetOption("log.warn.output", "off")
|
func(keyMap map[string]interface{}) ParentLogHandler {
|
||||||
config.SetOption("log.error.output", "stderr")
|
return NewParentLogHandler(func(child LogHandler) LogHandler {
|
||||||
config.SetOption("log.crit.output", "stderr")
|
return MatchMapHandler(keyMap, child)
|
||||||
}
|
})
|
||||||
|
|
||||||
// If the configuration has an all option we can skip some
|
},
|
||||||
c, _ = NewCompositeMultiHandler()
|
}, {
|
||||||
|
"log.", ".nfilter",
|
||||||
// Filters are assigned first, non filtered items override filters
|
func(keyMap map[string]interface{}) ParentLogHandler {
|
||||||
initAllLog(c, basePath, config)
|
return NewParentLogHandler(func(child LogHandler) LogHandler {
|
||||||
initLogLevels(c, basePath, config)
|
return NotMatchMapHandler(keyMap, child)
|
||||||
if c.CriticalHandler == nil && c.ErrorHandler != nil {
|
})
|
||||||
c.CriticalHandler = c.ErrorHandler
|
},
|
||||||
}
|
}}
|
||||||
initFilterLog(c, basePath, config)
|
|
||||||
if c.CriticalHandler == nil && c.ErrorHandler != nil {
|
|
||||||
c.CriticalHandler = c.ErrorHandler
|
|
||||||
}
|
|
||||||
initRequestLog(c, basePath, config)
|
|
||||||
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// Init the log.all configuration options
|
|
||||||
func initAllLog(c *CompositeMultiHandler, basePath string, config *config.Context) {
|
|
||||||
if config != nil {
|
|
||||||
extraLogFlag := config.BoolDefault("specialUseFlag", false)
|
|
||||||
if output, found := config.String("log.all.output"); found {
|
|
||||||
// Set all output for the specified handler
|
|
||||||
if extraLogFlag {
|
|
||||||
log.Printf("Adding standard handler for levels to >%s< ", output)
|
|
||||||
}
|
|
||||||
initHandlerFor(c, output, basePath, NewLogOptions(config, true, nil, LvlAllList...))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Init the filter options
|
|
||||||
// log.all.filter ....
|
|
||||||
// log.error.filter ....
|
|
||||||
func initFilterLog(c *CompositeMultiHandler, basePath string, config *config.Context) {
|
|
||||||
if config != nil {
|
|
||||||
extraLogFlag := config.BoolDefault("specialUseFlag", false)
|
|
||||||
|
|
||||||
// The commands to use
|
|
||||||
logFilterList := []struct {
|
|
||||||
LogPrefix, LogSuffix string
|
|
||||||
parentHandler func(map[string]interface{}) ParentLogHandler
|
|
||||||
}{{
|
|
||||||
"log.", ".filter",
|
|
||||||
func(keyMap map[string]interface{}) ParentLogHandler {
|
|
||||||
return NewParentLogHandler(func(child LogHandler) LogHandler {
|
|
||||||
return MatchMapHandler(keyMap, child)
|
|
||||||
})
|
|
||||||
|
|
||||||
},
|
|
||||||
}, {
|
|
||||||
"log.", ".nfilter",
|
|
||||||
func(keyMap map[string]interface{}) ParentLogHandler {
|
|
||||||
return NewParentLogHandler(func(child LogHandler) LogHandler {
|
|
||||||
return NotMatchMapHandler(keyMap, child)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
|
|
||||||
for _, logFilter := range logFilterList {
|
|
||||||
// Init for all filters
|
|
||||||
for _, name := range []string{"all", "debug", "info", "warn", "error", "crit",
|
|
||||||
"trace", // TODO trace is deprecated
|
|
||||||
} {
|
|
||||||
optionList := config.Options(logFilter.LogPrefix + name + logFilter.LogSuffix)
|
|
||||||
for _, option := range optionList {
|
|
||||||
splitOptions := strings.Split(option, ".")
|
|
||||||
keyMap := map[string]interface{}{}
|
|
||||||
for x := 3; x < len(splitOptions); x += 2 {
|
|
||||||
keyMap[splitOptions[x]] = splitOptions[x+1]
|
|
||||||
}
|
|
||||||
phandler := logFilter.parentHandler(keyMap)
|
|
||||||
if extraLogFlag {
|
|
||||||
log.Printf("Adding key map handler %s %s output %s", option, name, config.StringDefault(option, ""))
|
|
||||||
}
|
|
||||||
|
|
||||||
if name == "all" {
|
|
||||||
initHandlerFor(c, config.StringDefault(option, ""), basePath, NewLogOptions(config, false, phandler))
|
|
||||||
} else {
|
|
||||||
initHandlerFor(c, config.StringDefault(option, ""), basePath, NewLogOptions(config, false, phandler, toLevel[name]))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Init the log.error, log.warn etc configuration options
|
|
||||||
func initLogLevels(c *CompositeMultiHandler, basePath string, config *config.Context) {
|
|
||||||
for _, name := range []string{"debug", "info", "warn", "error", "crit",
|
|
||||||
"trace", // TODO trace is deprecated
|
|
||||||
} {
|
|
||||||
if config != nil {
|
|
||||||
extraLogFlag := config.BoolDefault("specialUseFlag", false)
|
|
||||||
output, found := config.String("log." + name + ".output")
|
|
||||||
if found {
|
|
||||||
if extraLogFlag {
|
|
||||||
log.Printf("Adding standard handler %s output %s", name, output)
|
|
||||||
}
|
|
||||||
initHandlerFor(c, output, basePath, NewLogOptions(config, true, nil, toLevel[name]))
|
|
||||||
}
|
|
||||||
// Gets the list of options with said prefix
|
|
||||||
} else {
|
|
||||||
initHandlerFor(c, "stderr", basePath, NewLogOptions(config, true, nil, toLevel[name]))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Init the request log options
|
|
||||||
func initRequestLog(c *CompositeMultiHandler, basePath string, config *config.Context) {
|
|
||||||
// Request logging to a separate output handler
|
|
||||||
// This takes the InfoHandlers and adds a MatchAbHandler handler to it to direct
|
|
||||||
// context with the word "section=requestlog" to that handler.
|
|
||||||
// Note if request logging is not enabled the MatchAbHandler will not be added and the
|
|
||||||
// request log messages will be sent out the INFO handler
|
|
||||||
outputRequest := "stdout"
|
|
||||||
if config != nil {
|
|
||||||
outputRequest = config.StringDefault("log.request.output", "")
|
|
||||||
}
|
|
||||||
oldInfo := c.InfoHandler
|
|
||||||
c.InfoHandler = nil
|
|
||||||
if outputRequest != "" {
|
|
||||||
initHandlerFor(c, outputRequest, basePath, NewLogOptions(config, false, nil, LvlInfo))
|
|
||||||
}
|
|
||||||
if c.InfoHandler != nil || oldInfo != nil {
|
|
||||||
if c.InfoHandler == nil {
|
|
||||||
c.InfoHandler = oldInfo
|
|
||||||
} else {
|
|
||||||
c.InfoHandler = MatchAbHandler("section", "requestlog", c.InfoHandler, oldInfo)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns a handler for the level using the output string
|
|
||||||
// Accept formats for output string are
|
|
||||||
// LogFunctionMap[value] callback function
|
|
||||||
// `stdout` `stderr` `full/file/path/to/location/app.log` `full/file/path/to/location/app.json`
|
|
||||||
func initHandlerFor(c *CompositeMultiHandler, output, basePath string, options *LogOptions) {
|
|
||||||
if options.Ctx != nil {
|
|
||||||
options.SetExtendedOptions(
|
|
||||||
"noColor", !options.Ctx.BoolDefault("log.colorize", true),
|
|
||||||
"smallDate", options.Ctx.BoolDefault("log.smallDate", true),
|
|
||||||
"maxSize", options.Ctx.IntDefault("log.maxsize", 1024*10),
|
|
||||||
"maxAge", options.Ctx.IntDefault("log.maxage", 14),
|
|
||||||
"maxBackups", options.Ctx.IntDefault("log.maxbackups", 14),
|
|
||||||
"compressBackups", !options.Ctx.BoolDefault("log.compressBackups", true),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
output = strings.TrimSpace(output)
|
|
||||||
if funcHandler, found := LogFunctionMap[output]; found {
|
|
||||||
funcHandler(c, options)
|
|
||||||
} else {
|
|
||||||
switch output {
|
|
||||||
case "":
|
|
||||||
fallthrough
|
|
||||||
case "off":
|
|
||||||
// No handler, discard data
|
|
||||||
default:
|
|
||||||
// Write to file specified
|
|
||||||
if !filepath.IsAbs(output) {
|
|
||||||
output = filepath.Join(basePath, output)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := os.MkdirAll(filepath.Dir(output), 0755); err != nil {
|
|
||||||
log.Panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.HasSuffix(output, "json") {
|
|
||||||
c.SetJsonFile(output, options)
|
|
||||||
} else {
|
|
||||||
// Override defaults for a terminal file
|
|
||||||
options.SetExtendedOptions("noColor", true)
|
|
||||||
options.SetExtendedOptions("smallDate", false)
|
|
||||||
c.SetTerminalFile(output, options)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// This structure and method will handle the old output format and log it to the new format
|
// This structure and method will handle the old output format and log it to the new format
|
||||||
type loggerRewrite struct {
|
type loggerRewrite struct {
|
||||||
@@ -240,8 +72,10 @@ type loggerRewrite struct {
|
|||||||
hideDeprecated bool
|
hideDeprecated bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The message indicating that a logger is using a deprecated log mechanism
|
||||||
var log_deprecated = []byte("* LOG DEPRECATED * ")
|
var log_deprecated = []byte("* LOG DEPRECATED * ")
|
||||||
|
|
||||||
|
// Implements the Write of the logger
|
||||||
func (lr loggerRewrite) Write(p []byte) (n int, err error) {
|
func (lr loggerRewrite) Write(p []byte) (n int, err error) {
|
||||||
if !lr.hideDeprecated {
|
if !lr.hideDeprecated {
|
||||||
p = append(log_deprecated, p...)
|
p = append(log_deprecated, p...)
|
||||||
@@ -270,7 +104,7 @@ func (lr loggerRewrite) Write(p []byte) (n int, err error) {
|
|||||||
|
|
||||||
// For logging purposes the call stack can be used to record the stack trace of a bad error
|
// For logging purposes the call stack can be used to record the stack trace of a bad error
|
||||||
// simply pass it as a context field in your log statement like
|
// simply pass it as a context field in your log statement like
|
||||||
// `controller.Log.Critc("This should not occur","stack",revel.NewCallStack())`
|
// `controller.Log.Crit("This should not occur","stack",revel.NewCallStack())`
|
||||||
func NewCallStack() interface{} {
|
func NewCallStack() interface{} {
|
||||||
return stack.Trace()
|
return stack.Trace()
|
||||||
}
|
}
|
||||||
|
|||||||
98
logger/wrap_handlers.go
Normal file
98
logger/wrap_handlers.go
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
package logger
|
||||||
|
|
||||||
|
// FuncHandler returns a Handler that logs records with the given
|
||||||
|
// function.
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Function handler wraps the declared function and returns the handler for it
|
||||||
|
func FuncHandler(fn func(r *Record) error) LogHandler {
|
||||||
|
return funcHandler(fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The type decleration for the function
|
||||||
|
type funcHandler func(r *Record) error
|
||||||
|
|
||||||
|
// The implementation of the Log
|
||||||
|
func (h funcHandler) Log(r *Record) error {
|
||||||
|
return h(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function allows you to do a full declaration for the log,
|
||||||
|
// it is recommended you use FuncHandler instead
|
||||||
|
func HandlerFunc(log func(message string, time time.Time, level LogLevel, call CallStack, context ContextMap) error) LogHandler {
|
||||||
|
return remoteHandler(log)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The type used for the HandlerFunc
|
||||||
|
type remoteHandler func(message string, time time.Time, level LogLevel, call CallStack, context ContextMap) error
|
||||||
|
|
||||||
|
// The Log implementation
|
||||||
|
func (c remoteHandler) Log(record *Record) error {
|
||||||
|
return c(record.Message, record.Time, record.Level, record.Call, record.Context)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SyncHandler can be wrapped around a handler to guarantee that
|
||||||
|
// only a single Log operation can proceed at a time. It's necessary
|
||||||
|
// for thread-safe concurrent writes.
|
||||||
|
func SyncHandler(h LogHandler) LogHandler {
|
||||||
|
var mu sync.Mutex
|
||||||
|
return FuncHandler(func(r *Record) error {
|
||||||
|
defer mu.Unlock()
|
||||||
|
mu.Lock()
|
||||||
|
return h.Log(r)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// LazyHandler writes all values to the wrapped handler after evaluating
|
||||||
|
// any lazy functions in the record's context. It is already wrapped
|
||||||
|
// around StreamHandler and SyslogHandler in this library, you'll only need
|
||||||
|
// it if you write your own Handler.
|
||||||
|
func LazyHandler(h LogHandler) LogHandler {
|
||||||
|
return FuncHandler(func(r *Record) error {
|
||||||
|
for k, v := range r.Context {
|
||||||
|
if lz, ok := v.(Lazy); ok {
|
||||||
|
value, err := evaluateLazy(lz)
|
||||||
|
if err != nil {
|
||||||
|
r.Context[errorKey] = "bad lazy " + k
|
||||||
|
} else {
|
||||||
|
v = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return h.Log(r)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func evaluateLazy(lz Lazy) (interface{}, error) {
|
||||||
|
t := reflect.TypeOf(lz.Fn)
|
||||||
|
|
||||||
|
if t.Kind() != reflect.Func {
|
||||||
|
return nil, fmt.Errorf("INVALID_LAZY, not func: %+v", lz.Fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.NumIn() > 0 {
|
||||||
|
return nil, fmt.Errorf("INVALID_LAZY, func takes args: %+v", lz.Fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.NumOut() == 0 {
|
||||||
|
return nil, fmt.Errorf("INVALID_LAZY, no func return val: %+v", lz.Fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
value := reflect.ValueOf(lz.Fn)
|
||||||
|
results := value.Call([]reflect.Value{})
|
||||||
|
if len(results) == 1 {
|
||||||
|
return results[0].Interface(), nil
|
||||||
|
} else {
|
||||||
|
values := make([]interface{}, len(results))
|
||||||
|
for i, v := range results {
|
||||||
|
values[i] = v.Interface()
|
||||||
|
}
|
||||||
|
return values, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,17 +1,22 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
// The constants
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/revel/cmd"
|
||||||
"github.com/revel/cmd/logger"
|
"github.com/revel/cmd/logger"
|
||||||
"github.com/revel/cmd/utils"
|
"github.com/revel/cmd/utils"
|
||||||
|
"go/ast"
|
||||||
"go/build"
|
"go/build"
|
||||||
|
"go/parser"
|
||||||
|
"go/token"
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// The constants
|
||||||
const (
|
const (
|
||||||
NEW COMMAND = iota + 1
|
NEW COMMAND = iota + 1
|
||||||
RUN
|
RUN
|
||||||
@@ -28,17 +33,20 @@ type (
|
|||||||
|
|
||||||
// The Command config for the line input
|
// The Command config for the line input
|
||||||
CommandConfig struct {
|
CommandConfig struct {
|
||||||
Index COMMAND // The index
|
Index COMMAND // The index
|
||||||
Verbose []bool `short:"v" long:"debug" description:"If set the logger is set to verbose"` // True if debug is active
|
Verbose []bool `short:"v" long:"debug" description:"If set the logger is set to verbose"` // True if debug is active
|
||||||
HistoricMode bool `long:"historic-run-mode" description:"If set the runmode is passed a string not json"` // True if debug is active
|
FrameworkVersion *Version // The framework version
|
||||||
ImportPath string // The import path (relative to a GOPATH)
|
CommandVersion *Version // The command version
|
||||||
GoPath string // The GoPath
|
HistoricMode bool `long:"historic-run-mode" description:"If set the runmode is passed a string not json"` // True if debug is active
|
||||||
GoCmd string // The full path to the go executable
|
ImportPath string // The import path (relative to a GOPATH)
|
||||||
SrcRoot string // The source root
|
GoPath string // The GoPath
|
||||||
AppPath string // The application path (absolute)
|
GoCmd string // The full path to the go executable
|
||||||
AppName string // The application name
|
SrcRoot string // The source root
|
||||||
PackageResolver func(pkgName string) error // a packge resolver for the config
|
AppPath string // The application path (absolute)
|
||||||
BuildFlags []string `short:"X" long:"build-flags" description:"These flags will be used when building the application. May be specified multiple times, only applicable for Build, Run, Package, Test commands"`
|
AppName string // The application name
|
||||||
|
Vendored bool // True if the application is vendored
|
||||||
|
PackageResolver func(pkgName string) error // a packge resolver for the config
|
||||||
|
BuildFlags []string `short:"X" long:"build-flags" description:"These flags will be used when building the application. May be specified multiple times, only applicable for Build, Run, Package, Test commands"`
|
||||||
// The new command
|
// The new command
|
||||||
New struct {
|
New struct {
|
||||||
ImportPath string `short:"a" long:"application-path" description:"Path to application folder" required:"false"`
|
ImportPath string `short:"a" long:"application-path" description:"Path to application folder" required:"false"`
|
||||||
@@ -57,7 +65,7 @@ type (
|
|||||||
Run struct {
|
Run struct {
|
||||||
ImportPath string `short:"a" long:"application-path" description:"Path to application folder" required:"false"`
|
ImportPath string `short:"a" long:"application-path" description:"Path to application folder" required:"false"`
|
||||||
Mode string `short:"m" long:"run-mode" description:"The mode to run the application in"`
|
Mode string `short:"m" long:"run-mode" description:"The mode to run the application in"`
|
||||||
Port int `short:"p" long:"port" default:"-1" description:"The port to listen" `
|
Port int `short:"p" long:"port" default:"-1" description:"The port to listen" `
|
||||||
NoProxy bool `short:"n" long:"no-proxy" description:"True if proxy server should not be started. This will only update the main and routes files on change"`
|
NoProxy bool `short:"n" long:"no-proxy" description:"True if proxy server should not be started. This will only update the main and routes files on change"`
|
||||||
} `command:"run"`
|
} `command:"run"`
|
||||||
// The package command
|
// The package command
|
||||||
@@ -80,12 +88,13 @@ type (
|
|||||||
// The version command
|
// The version command
|
||||||
Version struct {
|
Version struct {
|
||||||
ImportPath string `short:"a" long:"application-path" description:"Path to application folder" required:"false"`
|
ImportPath string `short:"a" long:"application-path" description:"Path to application folder" required:"false"`
|
||||||
|
Update bool `short:"u" long:"Update the framework and modules" required:"false"`
|
||||||
} `command:"version"`
|
} `command:"version"`
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// Updates the import path depending on the command
|
// Updates the import path depending on the command
|
||||||
func (c *CommandConfig) UpdateImportPath() bool {
|
func (c *CommandConfig) UpdateImportPath() error {
|
||||||
var importPath string
|
var importPath string
|
||||||
required := true
|
required := true
|
||||||
switch c.Index {
|
switch c.Index {
|
||||||
@@ -107,6 +116,7 @@ func (c *CommandConfig) UpdateImportPath() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(importPath) == 0 || filepath.IsAbs(importPath) || importPath[0] == '.' {
|
if len(importPath) == 0 || filepath.IsAbs(importPath) || importPath[0] == '.' {
|
||||||
|
utils.Logger.Info("Import path is absolute or not specified", "path", importPath)
|
||||||
// Try to determine the import path from the GO paths and the command line
|
// Try to determine the import path from the GO paths and the command line
|
||||||
currentPath, err := os.Getwd()
|
currentPath, err := os.Getwd()
|
||||||
if len(importPath) > 0 {
|
if len(importPath) > 0 {
|
||||||
@@ -116,7 +126,6 @@ func (c *CommandConfig) UpdateImportPath() bool {
|
|||||||
}
|
}
|
||||||
// For an absolute path
|
// For an absolute path
|
||||||
currentPath, _ = filepath.Abs(importPath)
|
currentPath, _ = filepath.Abs(importPath)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -128,6 +137,9 @@ func (c *CommandConfig) UpdateImportPath() bool {
|
|||||||
if len(importPath) > 4 && strings.ToLower(importPath[0:4]) == "src/" {
|
if len(importPath) > 4 && strings.ToLower(importPath[0:4]) == "src/" {
|
||||||
importPath = importPath[4:]
|
importPath = importPath[4:]
|
||||||
} else if importPath == "src" {
|
} else if importPath == "src" {
|
||||||
|
if c.Index != VERSION {
|
||||||
|
return fmt.Errorf("Invlaid import path, working dir is in GOPATH root")
|
||||||
|
}
|
||||||
importPath = ""
|
importPath = ""
|
||||||
}
|
}
|
||||||
utils.Logger.Info("Updated import path", "path", importPath)
|
utils.Logger.Info("Updated import path", "path", importPath)
|
||||||
@@ -138,23 +150,40 @@ func (c *CommandConfig) UpdateImportPath() bool {
|
|||||||
|
|
||||||
c.ImportPath = importPath
|
c.ImportPath = importPath
|
||||||
utils.Logger.Info("Returned import path", "path", importPath, "buildpath", build.Default.GOPATH)
|
utils.Logger.Info("Returned import path", "path", importPath, "buildpath", build.Default.GOPATH)
|
||||||
return (len(importPath) > 0 || !required)
|
if required && c.Index != NEW {
|
||||||
|
if err := c.SetVersions(); err != nil {
|
||||||
|
utils.Logger.Panic("Failed to fetch revel versions", "error", err)
|
||||||
|
}
|
||||||
|
if err:=c.FrameworkVersion.CompatibleFramework(c);err!=nil {
|
||||||
|
utils.Logger.Fatal("Compatibility Error", "message", err,
|
||||||
|
"Revel framework version", c.FrameworkVersion.String(), "Revel tool version", c.CommandVersion.String())
|
||||||
|
}
|
||||||
|
utils.Logger.Info("Revel versions", "revel-tool", c.CommandVersion.String(), "Revel Framework", c.FrameworkVersion.String())
|
||||||
|
}
|
||||||
|
if !required {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if len(importPath) == 0 {
|
||||||
|
return fmt.Errorf("Unable to determine import path from : %s", importPath)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Used to initialize the package resolver
|
// Used to initialize the package resolver
|
||||||
func (c *CommandConfig) InitPackageResolver() {
|
func (c *CommandConfig) InitPackageResolver() {
|
||||||
useVendor := utils.DirExists(filepath.Join(c.AppPath, "vendor"))
|
c.Vendored = utils.DirExists(filepath.Join(c.AppPath, "vendor"))
|
||||||
if c.Index == NEW && c.New.Vendored {
|
if c.Index == NEW && c.New.Vendored {
|
||||||
useVendor = true
|
c.Vendored = true
|
||||||
}
|
}
|
||||||
utils.Logger.Info("InitPackageResolver", "useVendor", useVendor, "path", c.AppPath)
|
|
||||||
|
utils.Logger.Info("InitPackageResolver", "useVendor", c.Vendored, "path", c.AppPath)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
depPath string
|
depPath string
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
|
|
||||||
if useVendor {
|
if c.Vendored {
|
||||||
utils.Logger.Info("Vendor folder detected, scanning for deps in path")
|
utils.Logger.Info("Vendor folder detected, scanning for deps in path")
|
||||||
depPath, err = exec.LookPath("dep")
|
depPath, err = exec.LookPath("dep")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -170,8 +199,8 @@ func (c *CommandConfig) InitPackageResolver() {
|
|||||||
//useVendor := utils.DirExists(filepath.Join(c.AppPath, "vendor"))
|
//useVendor := utils.DirExists(filepath.Join(c.AppPath, "vendor"))
|
||||||
|
|
||||||
var getCmd *exec.Cmd
|
var getCmd *exec.Cmd
|
||||||
utils.Logger.Info("Request for package ", "package", pkgName, "use vendor", useVendor)
|
utils.Logger.Info("Request for package ", "package", pkgName, "use vendor", c.Vendored)
|
||||||
if useVendor {
|
if c.Vendored {
|
||||||
utils.Logger.Info("Using dependency manager to import package", "package", pkgName)
|
utils.Logger.Info("Using dependency manager to import package", "package", pkgName)
|
||||||
|
|
||||||
if depPath == "" {
|
if depPath == "" {
|
||||||
@@ -182,10 +211,18 @@ func (c *CommandConfig) InitPackageResolver() {
|
|||||||
utils.Logger.Error("Missing package", "package", pkgName)
|
utils.Logger.Error("Missing package", "package", pkgName)
|
||||||
return fmt.Errorf("Missing package %s", pkgName)
|
return fmt.Errorf("Missing package %s", pkgName)
|
||||||
}
|
}
|
||||||
getCmd = exec.Command(depPath, "ensure", "-add", pkgName)
|
// Check to see if the package exists locally
|
||||||
|
_, err := build.Import(pkgName, c.AppPath, build.FindOnly)
|
||||||
|
if err != nil {
|
||||||
|
getCmd = exec.Command(depPath, "ensure", "-add", pkgName)
|
||||||
|
} else {
|
||||||
|
getCmd = exec.Command(depPath, "ensure", "-update", pkgName)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
utils.Logger.Info("No vendor folder detected, not using dependency manager to import package", "package", pkgName)
|
utils.Logger.Info("No vendor folder detected, not using dependency manager to import package", "package", pkgName)
|
||||||
getCmd = exec.Command(c.GoCmd, "get", pkgName)
|
getCmd = exec.Command(c.GoCmd, "get", "-u", pkgName)
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.CmdInit(getCmd, c.AppPath)
|
utils.CmdInit(getCmd, c.AppPath)
|
||||||
@@ -201,6 +238,7 @@ func (c *CommandConfig) InitPackageResolver() {
|
|||||||
|
|
||||||
// lookup and set Go related variables
|
// lookup and set Go related variables
|
||||||
func (c *CommandConfig) InitGoPaths() {
|
func (c *CommandConfig) InitGoPaths() {
|
||||||
|
utils.Logger.Info("InitGoPaths")
|
||||||
// lookup go path
|
// lookup go path
|
||||||
c.GoPath = build.Default.GOPATH
|
c.GoPath = build.Default.GOPATH
|
||||||
if c.GoPath == "" {
|
if c.GoPath == "" {
|
||||||
@@ -237,10 +275,10 @@ func (c *CommandConfig) InitGoPaths() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(c.SrcRoot)==0 && len(bestpath) > 0 {
|
utils.Logger.Info("Source root", "path", c.SrcRoot, "cwd", workingDir, "gopath", c.GoPath, "bestpath",bestpath)
|
||||||
|
if len(c.SrcRoot) == 0 && len(bestpath) > 0 {
|
||||||
c.SrcRoot = bestpath
|
c.SrcRoot = bestpath
|
||||||
}
|
}
|
||||||
utils.Logger.Info("Source root", "path", c.SrcRoot, "cwd", workingDir, "gopath", c.GoPath)
|
|
||||||
|
|
||||||
// If source root is empty and this isn't a version then skip it
|
// If source root is empty and this isn't a version then skip it
|
||||||
if len(c.SrcRoot) == 0 {
|
if len(c.SrcRoot) == 0 {
|
||||||
@@ -256,3 +294,48 @@ func (c *CommandConfig) InitGoPaths() {
|
|||||||
c.AppPath = filepath.Join(c.SrcRoot, filepath.FromSlash(c.ImportPath))
|
c.AppPath = filepath.Join(c.SrcRoot, filepath.FromSlash(c.ImportPath))
|
||||||
utils.Logger.Info("Set application path", "path", c.AppPath)
|
utils.Logger.Info("Set application path", "path", c.AppPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sets the versions on the command config
|
||||||
|
func (c *CommandConfig) SetVersions() (err error) {
|
||||||
|
c.CommandVersion, _ = ParseVersion(cmd.Version)
|
||||||
|
_, revelPath, err := utils.FindSrcPaths(c.ImportPath, RevelImportPath, c.PackageResolver)
|
||||||
|
if err == nil {
|
||||||
|
utils.Logger.Info("Fullpath to revel", "dir", revelPath)
|
||||||
|
fset := token.NewFileSet() // positions are relative to fset
|
||||||
|
|
||||||
|
versionData, err := ioutil.ReadFile(filepath.Join(revelPath, RevelImportPath, "version.go"))
|
||||||
|
if err != nil {
|
||||||
|
utils.Logger.Error("Failed to find Revel version:", "error", err, "path", revelPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse src but stop after processing the imports.
|
||||||
|
f, err := parser.ParseFile(fset, "", versionData, parser.ParseComments)
|
||||||
|
if err != nil {
|
||||||
|
return utils.NewBuildError("Failed to parse Revel version error:", "error", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print the imports from the file's AST.
|
||||||
|
for _, s := range f.Decls {
|
||||||
|
genDecl, ok := s.(*ast.GenDecl)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if genDecl.Tok != token.CONST {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, a := range genDecl.Specs {
|
||||||
|
spec := a.(*ast.ValueSpec)
|
||||||
|
r := spec.Values[0].(*ast.BasicLit)
|
||||||
|
if spec.Names[0].Name == "Version" {
|
||||||
|
c.FrameworkVersion, err = ParseVersion(strings.Replace(r.Value, `"`, ``, -1))
|
||||||
|
if err != nil {
|
||||||
|
utils.Logger.Errorf("Failed to parse version")
|
||||||
|
} else {
|
||||||
|
utils.Logger.Info("Parsed revel version", "version", c.FrameworkVersion.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,15 +10,15 @@ import (
|
|||||||
func TestEventHandler(t *testing.T) {
|
func TestEventHandler(t *testing.T) {
|
||||||
counter := 0
|
counter := 0
|
||||||
newListener := func(typeOf revel.Event, value interface{}) (responseOf revel.EventResponse) {
|
newListener := func(typeOf revel.Event, value interface{}) (responseOf revel.EventResponse) {
|
||||||
if typeOf == revel.REVEL_FAILURE {
|
if typeOf == revel.ENGINE_SHUTDOWN_REQUEST {
|
||||||
counter++
|
counter++
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Attach the same handlder twice so we expect to see the response twice as well
|
// Attach the same handler twice so we expect to see the response twice as well
|
||||||
revel.AddInitEventHandler(newListener)
|
revel.AddInitEventHandler(newListener)
|
||||||
revel.AddInitEventHandler(newListener)
|
revel.AddInitEventHandler(newListener)
|
||||||
revel.RaiseEvent(revel.REVEL_AFTER_MODULES_LOADED, nil)
|
revel.StopServer(1)
|
||||||
revel.RaiseEvent(revel.REVEL_FAILURE, nil)
|
|
||||||
assert.Equal(t, counter, 2, "Expected event handler to have been called")
|
assert.Equal(t, counter, 2, "Expected event handler to have been called")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -92,6 +92,7 @@ func (w *WrappedRevelCallback) PackageResolver(pkgName string) error {
|
|||||||
|
|
||||||
// RevelImportPath Revel framework import path
|
// RevelImportPath Revel framework import path
|
||||||
var RevelImportPath = "github.com/revel/revel"
|
var RevelImportPath = "github.com/revel/revel"
|
||||||
|
var RevelModulesImportPath = "github.com/revel/modules"
|
||||||
|
|
||||||
// This function returns a container object describing the revel application
|
// This function returns a container object describing the revel application
|
||||||
// eventually this type of function will replace the global variables.
|
// eventually this type of function will replace the global variables.
|
||||||
@@ -120,8 +121,16 @@ func NewRevelPaths(mode, importPath, srcPath string, callback RevelCallback) (rp
|
|||||||
rp.BasePath = filepath.Join(rp.SourcePath, filepath.FromSlash(importPath))
|
rp.BasePath = filepath.Join(rp.SourcePath, filepath.FromSlash(importPath))
|
||||||
rp.PackageInfo.Vendor = utils.Exists(filepath.Join(rp.BasePath, "vendor"))
|
rp.PackageInfo.Vendor = utils.Exists(filepath.Join(rp.BasePath, "vendor"))
|
||||||
rp.AppPath = filepath.Join(rp.BasePath, "app")
|
rp.AppPath = filepath.Join(rp.BasePath, "app")
|
||||||
rp.ViewsPath = filepath.Join(rp.AppPath, "views")
|
|
||||||
|
|
||||||
|
// Sanity check , ensure app and conf paths exist
|
||||||
|
if !utils.DirExists(rp.AppPath) {
|
||||||
|
return rp, fmt.Errorf("No application found at path %s", rp.AppPath)
|
||||||
|
}
|
||||||
|
if !utils.DirExists(filepath.Join(rp.BasePath, "conf")) {
|
||||||
|
return rp, fmt.Errorf("No configuration found at path %s", filepath.Join(rp.BasePath, "conf"))
|
||||||
|
}
|
||||||
|
|
||||||
|
rp.ViewsPath = filepath.Join(rp.AppPath, "views")
|
||||||
rp.CodePaths = []string{rp.AppPath}
|
rp.CodePaths = []string{rp.AppPath}
|
||||||
rp.TemplatePaths = []string{}
|
rp.TemplatePaths = []string{}
|
||||||
|
|
||||||
@@ -218,7 +227,7 @@ func (rp *RevelContainer) loadModules(callback RevelCallback) (err error) {
|
|||||||
|
|
||||||
modulePath, err := rp.ResolveImportPath(moduleImportPath)
|
modulePath, err := rp.ResolveImportPath(moduleImportPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Logger.Info("Missing module ", "module", moduleImportPath, "error",err)
|
utils.Logger.Info("Missing module ", "module_import_path", moduleImportPath, "error",err)
|
||||||
callback.PackageResolver(moduleImportPath)
|
callback.PackageResolver(moduleImportPath)
|
||||||
modulePath, err = rp.ResolveImportPath(moduleImportPath)
|
modulePath, err = rp.ResolveImportPath(moduleImportPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
124
model/version.go
Normal file
124
model/version.go
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Version struct {
|
||||||
|
Prefix string
|
||||||
|
Major int
|
||||||
|
Minor int
|
||||||
|
Maintenance int
|
||||||
|
Suffix string
|
||||||
|
BuildDate string
|
||||||
|
MinGoVersion string
|
||||||
|
}
|
||||||
|
|
||||||
|
// The compatibility list
|
||||||
|
var frameworkCompatibleRangeList = [][]string{
|
||||||
|
{"0.0.0", "0.20.0"}, // minimum Revel version to use with this version of the tool
|
||||||
|
{"0.19.99", "0.30.0"}, // Compatible with Framework V 0.19.99 - 0.30.0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parses a version like v1.2.3a or 1.2
|
||||||
|
var versionRegExp = regexp.MustCompile(`([^\d]*)?([0-9]*)\.([0-9]*)(\.([0-9]*))?(.*)`)
|
||||||
|
|
||||||
|
// Parse the version and return it as a Version object
|
||||||
|
func ParseVersion(version string) (v *Version, err error) {
|
||||||
|
|
||||||
|
v = &Version{}
|
||||||
|
return v, v.ParseVersion(version)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the version and return it as a Version object
|
||||||
|
func (v *Version)ParseVersion(version string) (err error) {
|
||||||
|
|
||||||
|
parsedResult := versionRegExp.FindAllStringSubmatch(version, -1)
|
||||||
|
if len(parsedResult) != 1 {
|
||||||
|
err = errors.Errorf("Invalid version %s", version)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(parsedResult[0]) != 7 {
|
||||||
|
err = errors.Errorf("Invalid version %s", version)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
v.Prefix = parsedResult[0][1]
|
||||||
|
v.Major = v.intOrZero(parsedResult[0][2])
|
||||||
|
v.Minor = v.intOrZero(parsedResult[0][3])
|
||||||
|
v.Maintenance = v.intOrZero(parsedResult[0][5])
|
||||||
|
v.Suffix = parsedResult[0][6]
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Returns 0 or an int value for the string, errors are returned as 0
|
||||||
|
func (v *Version) intOrZero(input string) (value int) {
|
||||||
|
if input != "" {
|
||||||
|
value, _ = strconv.Atoi(input)
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if this major revision is compatible
|
||||||
|
func (v *Version) CompatibleFramework(c *CommandConfig) error {
|
||||||
|
for i, rv := range frameworkCompatibleRangeList {
|
||||||
|
start, _ := ParseVersion(rv[0])
|
||||||
|
end, _ := ParseVersion(rv[1])
|
||||||
|
if !v.Newer(start) || v.Newer(end) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Framework is older then 0.20, turn on historic mode
|
||||||
|
if i == 0 {
|
||||||
|
c.HistoricMode = true
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return errors.New("Tool out of date - do a 'go get -u github.com/revel/cmd/revel'")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if this major revision is newer then the passed in
|
||||||
|
func (v *Version) MajorNewer(o *Version) bool {
|
||||||
|
if v.Major != o.Major {
|
||||||
|
return v.Major > o.Major
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if this major or major and minor revision is newer then the value passed in
|
||||||
|
func (v *Version) MinorNewer(o *Version) bool {
|
||||||
|
if v.Major != o.Major {
|
||||||
|
return v.Major > o.Major
|
||||||
|
}
|
||||||
|
if v.Minor != o.Minor {
|
||||||
|
return v.Minor > o.Minor
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if the version is newer then the current on
|
||||||
|
func (v *Version) Newer(o *Version) bool {
|
||||||
|
if v.Major != o.Major {
|
||||||
|
return v.Major > o.Major
|
||||||
|
}
|
||||||
|
if v.Minor != o.Minor {
|
||||||
|
return v.Minor > o.Minor
|
||||||
|
}
|
||||||
|
if v.Maintenance != o.Maintenance {
|
||||||
|
return v.Maintenance > o.Maintenance
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert the version to a string
|
||||||
|
func (v *Version) VersionString() string {
|
||||||
|
return fmt.Sprintf("%s%d.%d.%d%s", v.Prefix, v.Major, v.Minor, v.Maintenance, v.Suffix)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert the version build date and go version to a string
|
||||||
|
func (v *Version) String() string {
|
||||||
|
return fmt.Sprintf("Version: %s%d.%d.%d%s\nBuild Date: %s\n Minimium Go Version: %s",
|
||||||
|
v.Prefix, v.Major, v.Minor, v.Maintenance, v.Suffix, v.BuildDate, v.MinGoVersion)
|
||||||
|
}
|
||||||
33
model/version_test.go
Normal file
33
model/version_test.go
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
package model_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"testing"
|
||||||
|
"github.com/revel/cmd/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
var versionTests = [][]string{
|
||||||
|
{"v0.20.0-dev", "v0.20.0-dev"},
|
||||||
|
{"v0.20-dev", "v0.20.0-dev"},
|
||||||
|
{"v0.20.", "v0.20.0"},
|
||||||
|
{"2.0", "2.0.0"},
|
||||||
|
}
|
||||||
|
// Test that the event handler can be attached and it dispatches the event received
|
||||||
|
func TestVersion(t *testing.T) {
|
||||||
|
for _, v:= range versionTests {
|
||||||
|
p,e:=model.ParseVersion(v[0])
|
||||||
|
assert.Nil(t,e,"Should have parsed %s",v)
|
||||||
|
assert.Equal(t,p.String(),v[1], "Should be equal %s==%s",p.String(),v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// test the ranges
|
||||||
|
func TestVersionRange(t *testing.T) {
|
||||||
|
a,_ := model.ParseVersion("0.1.2")
|
||||||
|
b,_ := model.ParseVersion("0.2.1")
|
||||||
|
c,_ := model.ParseVersion("1.0.1")
|
||||||
|
assert.True(t, b.MinorNewer(a), "B is newer then A")
|
||||||
|
assert.False(t, b.MajorNewer(a), "B is not major newer then A")
|
||||||
|
assert.False(t, b.MajorNewer(c), "B is not major newer then A")
|
||||||
|
assert.True(t, c.MajorNewer(b), "C is major newer then b")
|
||||||
|
}
|
||||||
@@ -46,6 +46,9 @@ func ProcessSource(paths *model.RevelContainer) (_ *model.SourceInfo, compileErr
|
|||||||
|
|
||||||
// Start walking the directory tree.
|
// Start walking the directory tree.
|
||||||
compileError = utils.Walk(root, pc.processPath)
|
compileError = utils.Walk(root, pc.processPath)
|
||||||
|
if compileError != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return pc.srcInfo, compileError
|
return pc.srcInfo, compileError
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ func buildApp(c *model.CommandConfig) (err error) {
|
|||||||
// Convert target to absolute path
|
// Convert target to absolute path
|
||||||
c.Build.TargetPath, _ = filepath.Abs(destPath)
|
c.Build.TargetPath, _ = filepath.Abs(destPath)
|
||||||
c.Build.Mode = mode
|
c.Build.Mode = mode
|
||||||
|
c.Build.ImportPath = appImportPath
|
||||||
|
|
||||||
revel_paths, err := model.NewRevelPaths(mode, appImportPath, "", model.NewWrappedRevelCallback(nil, c.PackageResolver))
|
revel_paths, err := model.NewRevelPaths(mode, appImportPath, "", model.NewWrappedRevelCallback(nil, c.PackageResolver))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ func newApp(name string, command model.COMMAND, precall func(c *model.CommandCon
|
|||||||
if precall != nil {
|
if precall != nil {
|
||||||
precall(c)
|
precall(c)
|
||||||
}
|
}
|
||||||
if !c.UpdateImportPath() {
|
if c.UpdateImportPath()!=nil {
|
||||||
a.Fail("Unable to update import path")
|
a.Fail("Unable to update import path")
|
||||||
}
|
}
|
||||||
c.InitGoPaths()
|
c.InitGoPaths()
|
||||||
|
|||||||
62
revel/new.go
62
revel/new.go
@@ -65,6 +65,13 @@ func newApp(c *model.CommandConfig) (err error) {
|
|||||||
if err == nil || !utils.Empty(c.AppPath) {
|
if err == nil || !utils.Empty(c.AppPath) {
|
||||||
return utils.NewBuildError("Abort: Import path already exists.", "path", c.ImportPath)
|
return utils.NewBuildError("Abort: Import path already exists.", "path", c.ImportPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// checking and setting skeleton
|
||||||
|
if err=setSkeletonPath(c);err!=nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create application path
|
||||||
if err := os.MkdirAll(c.AppPath, os.ModePerm); err != nil {
|
if err := os.MkdirAll(c.AppPath, os.ModePerm); err != nil {
|
||||||
return utils.NewBuildError("Abort: Unable to create app path.", "path", c.AppPath)
|
return utils.NewBuildError("Abort: Unable to create app path.", "path", c.AppPath)
|
||||||
}
|
}
|
||||||
@@ -107,7 +114,8 @@ func newApp(c *model.CommandConfig) (err error) {
|
|||||||
|
|
||||||
getCmd := exec.Command("dep", "ensure", "-v")
|
getCmd := exec.Command("dep", "ensure", "-v")
|
||||||
utils.CmdInit(getCmd, c.AppPath)
|
utils.CmdInit(getCmd, c.AppPath)
|
||||||
utils.Logger.Info("Exec:", "args", getCmd.Args)
|
|
||||||
|
utils.Logger.Info("Exec:", "args", getCmd.Args, "env", getCmd.Env, "workingdir",getCmd.Dir)
|
||||||
getOutput, err := getCmd.CombinedOutput()
|
getOutput, err := getCmd.CombinedOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return utils.NewBuildIfError(err, string(getOutput))
|
return utils.NewBuildIfError(err, string(getOutput))
|
||||||
@@ -115,15 +123,11 @@ func newApp(c *model.CommandConfig) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// checking and setting application
|
// checking and setting application
|
||||||
|
|
||||||
if err = setApplicationPath(c); err != nil {
|
if err = setApplicationPath(c); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
// At this point the versions can be set
|
||||||
// checking and setting skeleton
|
c.SetVersions()
|
||||||
if err=setSkeletonPath(c);err!=nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// copy files to new app directory
|
// copy files to new app directory
|
||||||
if err = copyNewAppFiles(c);err != nil {
|
if err = copyNewAppFiles(c);err != nil {
|
||||||
@@ -189,40 +193,46 @@ func setApplicationPath(c *model.CommandConfig) (err error) {
|
|||||||
|
|
||||||
c.AppName = filepath.Base(c.AppPath)
|
c.AppName = filepath.Base(c.AppPath)
|
||||||
|
|
||||||
//if c.BasePath == "." {
|
|
||||||
// // we need to remove the a single '.' when
|
|
||||||
// // the app is in the $GOROOT/src directory
|
|
||||||
// c.BasePath = ""
|
|
||||||
//} else {
|
|
||||||
// // we need to append a '/' when the app is
|
|
||||||
// // is a subdirectory such as $GOROOT/src/path/to/revelapp
|
|
||||||
// c.BasePath += "/"
|
|
||||||
//}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the skeleton path
|
// Set the skeleton path
|
||||||
func setSkeletonPath(c *model.CommandConfig) (err error) {
|
func setSkeletonPath(c *model.CommandConfig) (err error) {
|
||||||
if len(c.New.SkeletonPath) == 0 {
|
if len(c.New.SkeletonPath) == 0 {
|
||||||
c.New.SkeletonPath = "git://" + RevelSkeletonsImportPath + ":basic/bootstrap4"
|
c.New.SkeletonPath = "https://" + RevelSkeletonsImportPath + ":basic/bootstrap4"
|
||||||
}
|
}
|
||||||
|
|
||||||
// First check to see the protocol of the string
|
// First check to see the protocol of the string
|
||||||
if sp, err := url.Parse(c.New.SkeletonPath); err == nil {
|
sp, err := url.Parse(c.New.SkeletonPath)
|
||||||
|
if err == nil {
|
||||||
utils.Logger.Info("Detected skeleton path", "path", sp)
|
utils.Logger.Info("Detected skeleton path", "path", sp)
|
||||||
|
|
||||||
switch strings.ToLower(sp.Scheme) {
|
switch strings.ToLower(sp.Scheme) {
|
||||||
// TODO Add support for https, http, ftp, file
|
// TODO Add support for ftp, sftp, scp ??
|
||||||
|
case "" :
|
||||||
|
sp.Scheme="file"
|
||||||
|
fallthrough
|
||||||
|
case "file" :
|
||||||
|
fullpath := sp.String()[7:]
|
||||||
|
if !filepath.IsAbs(fullpath) {
|
||||||
|
fullpath, err = filepath.Abs(fullpath)
|
||||||
|
if err!=nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.New.SkeletonPath = fullpath
|
||||||
|
utils.Logger.Info("Set skeleton path to ", fullpath)
|
||||||
|
if !utils.DirExists(fullpath) {
|
||||||
|
return fmt.Errorf("Failed to find skeleton in filepath %s %s", fullpath, sp.String())
|
||||||
|
}
|
||||||
case "git":
|
case "git":
|
||||||
|
fallthrough
|
||||||
|
case "http":
|
||||||
|
fallthrough
|
||||||
|
case "https":
|
||||||
if err := newLoadFromGit(c, sp); err != nil {
|
if err := newLoadFromGit(c, sp); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
//case "":
|
|
||||||
|
|
||||||
//if err := newLoadFromGo(c, sp); err != nil {
|
|
||||||
// return err
|
|
||||||
//}
|
|
||||||
default:
|
default:
|
||||||
utils.Logger.Fatal("Unsupported skeleton schema ", "path", c.New.SkeletonPath)
|
utils.Logger.Fatal("Unsupported skeleton schema ", "path", c.New.SkeletonPath)
|
||||||
|
|
||||||
@@ -245,7 +255,7 @@ func newLoadFromGit(c *model.CommandConfig, sp *url.URL) (err error) {
|
|||||||
utils.Logger.Info("Exec:", "args", getCmd.Args)
|
utils.Logger.Info("Exec:", "args", getCmd.Args)
|
||||||
getOutput, err := getCmd.CombinedOutput()
|
getOutput, err := getCmd.CombinedOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Logger.Fatalf("Abort: could not clone the Skeleton source code: \n%s\n%s\n", getOutput, c.New.SkeletonPath)
|
utils.Logger.Fatal("Abort: could not clone the Skeleton source code: ","output", string(getOutput), "path", c.New.SkeletonPath)
|
||||||
}
|
}
|
||||||
outputPath := targetPath
|
outputPath := targetPath
|
||||||
if len(pathpart) > 1 {
|
if len(pathpart) > 1 {
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import (
|
|||||||
"github.com/revel/cmd/logger"
|
"github.com/revel/cmd/logger"
|
||||||
"github.com/revel/cmd/model"
|
"github.com/revel/cmd/model"
|
||||||
"github.com/revel/cmd/utils"
|
"github.com/revel/cmd/utils"
|
||||||
|
"bytes"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -70,14 +71,17 @@ func main() {
|
|||||||
wd, _ := os.Getwd()
|
wd, _ := os.Getwd()
|
||||||
|
|
||||||
utils.InitLogger(wd, logger.LvlError)
|
utils.InitLogger(wd, logger.LvlError)
|
||||||
|
|
||||||
parser := flags.NewParser(c, flags.HelpFlag|flags.PassDoubleDash)
|
parser := flags.NewParser(c, flags.HelpFlag|flags.PassDoubleDash)
|
||||||
if err := ParseArgs(c, parser, os.Args[1:]); err != nil {
|
if len(os.Args)<2 {
|
||||||
fmt.Fprint(os.Stderr, err.Error())
|
|
||||||
parser.WriteHelp(os.Stdout)
|
parser.WriteHelp(os.Stdout)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := ParseArgs(c, parser, os.Args[1:]); err != nil {
|
||||||
|
fmt.Fprint(os.Stderr, err.Error() +"\n")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
// Switch based on the verbose flag
|
// Switch based on the verbose flag
|
||||||
if len(c.Verbose)>1 {
|
if len(c.Verbose)>1 {
|
||||||
utils.InitLogger(wd, logger.LvlDebug)
|
utils.InitLogger(wd, logger.LvlDebug)
|
||||||
@@ -87,8 +91,10 @@ func main() {
|
|||||||
utils.InitLogger(wd, logger.LvlWarn)
|
utils.InitLogger(wd, logger.LvlWarn)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !c.UpdateImportPath() {
|
if err := c.UpdateImportPath();err!=nil {
|
||||||
utils.Logger.Fatal("Unable to determine application path")
|
utils.Logger.Error(err.Error())
|
||||||
|
parser.WriteHelp(os.Stdout)
|
||||||
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
command := Commands[c.Index]
|
command := Commands[c.Index]
|
||||||
@@ -136,12 +142,12 @@ func ParseArgs(c *model.CommandConfig, parser *flags.Parser, args []string) (err
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.Index == 0 {
|
if len(extraArgs) > 0 {
|
||||||
err = fmt.Errorf("Unknown command %v", extraArgs)
|
|
||||||
} else if len(extraArgs) > 0 {
|
|
||||||
utils.Logger.Info("Found additional arguements, setting them")
|
utils.Logger.Info("Found additional arguements, setting them")
|
||||||
if !Commands[c.Index].UpdateConfig(c, extraArgs) {
|
if !Commands[c.Index].UpdateConfig(c, extraArgs) {
|
||||||
err = fmt.Errorf("Invalid command line arguements %v", extraArgs)
|
buffer := &bytes.Buffer{}
|
||||||
|
parser.WriteHelp(buffer)
|
||||||
|
err = fmt.Errorf("Invalid command line arguements %v\n%s", extraArgs, buffer.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
22
revel/run.go
22
revel/run.go
@@ -12,6 +12,8 @@ import (
|
|||||||
"github.com/revel/cmd/model"
|
"github.com/revel/cmd/model"
|
||||||
"github.com/revel/cmd/utils"
|
"github.com/revel/cmd/utils"
|
||||||
"go/build"
|
"go/build"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
)
|
)
|
||||||
|
|
||||||
var cmdRun = &Command{
|
var cmdRun = &Command{
|
||||||
@@ -71,7 +73,7 @@ func updateRunConfig(c *model.CommandConfig, args []string) bool {
|
|||||||
// 3. revel run [run-mode] [port]
|
// 3. revel run [run-mode] [port]
|
||||||
|
|
||||||
// Check to see if the import path evaluates out to something that may be on a gopath
|
// Check to see if the import path evaluates out to something that may be on a gopath
|
||||||
if _, err := build.Import(args[0], "", build.FindOnly); err == nil {
|
if runIsImportPath(args[0]) {
|
||||||
// 1st arg is the import path
|
// 1st arg is the import path
|
||||||
c.Run.ImportPath = args[0]
|
c.Run.ImportPath = args[0]
|
||||||
|
|
||||||
@@ -92,12 +94,7 @@ func updateRunConfig(c *model.CommandConfig, args []string) bool {
|
|||||||
// 1. revel run [import-path]
|
// 1. revel run [import-path]
|
||||||
// 2. revel run [port]
|
// 2. revel run [port]
|
||||||
// 3. revel run [run-mode]
|
// 3. revel run [run-mode]
|
||||||
_, err := build.Import(args[0], "", build.FindOnly)
|
if runIsImportPath(args[0]) {
|
||||||
if err != nil {
|
|
||||||
utils.Logger.Warn("Unable to run using an import path, assuming import path is working directory %s %s", "Argument", args[0], "error", err.Error())
|
|
||||||
}
|
|
||||||
utils.Logger.Info("Trying to build with", args[0], err)
|
|
||||||
if err == nil {
|
|
||||||
// 1st arg is the import path
|
// 1st arg is the import path
|
||||||
c.Run.ImportPath = args[0]
|
c.Run.ImportPath = args[0]
|
||||||
} else if _, err := strconv.Atoi(args[0]); err == nil {
|
} else if _, err := strconv.Atoi(args[0]); err == nil {
|
||||||
@@ -108,12 +105,21 @@ func updateRunConfig(c *model.CommandConfig, args []string) bool {
|
|||||||
c.Run.Mode = args[0]
|
c.Run.Mode = args[0]
|
||||||
}
|
}
|
||||||
case 0:
|
case 0:
|
||||||
return false
|
// Attempt to set the import path to the current working director.
|
||||||
|
c.Run.ImportPath,_ = os.Getwd()
|
||||||
}
|
}
|
||||||
c.Index = model.RUN
|
c.Index = model.RUN
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns true if this is an absolute path or a relative gopath
|
||||||
|
func runIsImportPath(pathToCheck string) bool {
|
||||||
|
if _, err := build.Import(pathToCheck, "", build.FindOnly);err==nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return filepath.IsAbs(pathToCheck)
|
||||||
|
}
|
||||||
|
|
||||||
// Called to run the app
|
// Called to run the app
|
||||||
func runApp(c *model.CommandConfig) (err error) {
|
func runApp(c *model.CommandConfig) (err error) {
|
||||||
if c.Run.Mode == "" {
|
if c.Run.Mode == "" {
|
||||||
|
|||||||
268
revel/version.go
268
revel/version.go
@@ -10,19 +10,30 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"runtime"
|
|
||||||
|
|
||||||
"github.com/revel/cmd"
|
"github.com/revel/cmd"
|
||||||
"github.com/revel/cmd/model"
|
"github.com/revel/cmd/model"
|
||||||
"github.com/revel/cmd/utils"
|
"github.com/revel/cmd/utils"
|
||||||
"go/ast"
|
"go/ast"
|
||||||
"go/build"
|
|
||||||
"go/parser"
|
"go/parser"
|
||||||
"go/token"
|
"go/token"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"bytes"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
// The version container
|
||||||
|
VersionCommand struct {
|
||||||
|
Command *model.CommandConfig // The command
|
||||||
|
revelVersion *model.Version // The Revel framework version
|
||||||
|
modulesVersion *model.Version // The Revel modules version
|
||||||
|
cmdVersion *model.Version // The tool version
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
var cmdVersion = &Command{
|
var cmdVersion = &Command{
|
||||||
@@ -38,12 +49,13 @@ For example:
|
|||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
cmdVersion.RunWith = versionApp
|
v := &VersionCommand{}
|
||||||
cmdVersion.UpdateConfig = updateVersionConfig
|
cmdVersion.UpdateConfig = v.UpdateConfig
|
||||||
|
cmdVersion.RunWith = v.RunWith
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the version
|
// Update the version
|
||||||
func updateVersionConfig(c *model.CommandConfig, args []string) bool {
|
func (v *VersionCommand) UpdateConfig(c *model.CommandConfig, args []string) bool {
|
||||||
if len(args) > 0 {
|
if len(args) > 0 {
|
||||||
c.Version.ImportPath = args[0]
|
c.Version.ImportPath = args[0]
|
||||||
}
|
}
|
||||||
@@ -51,69 +63,199 @@ func updateVersionConfig(c *model.CommandConfig, args []string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Displays the version of go and Revel
|
// Displays the version of go and Revel
|
||||||
func versionApp(c *model.CommandConfig) (err error) {
|
func (v *VersionCommand) RunWith(c *model.CommandConfig) (err error) {
|
||||||
|
utils.Logger.Info("Requesting version information", "config", c)
|
||||||
|
v.Command = c
|
||||||
|
|
||||||
var revelPath, appPath string
|
// Update the versions with the local values
|
||||||
|
v.updateLocalVersions()
|
||||||
|
|
||||||
|
needsUpdates := true
|
||||||
appPath, revelPath, err = utils.FindSrcPaths(c.ImportPath, model.RevelImportPath, c.PackageResolver)
|
versionInfo := ""
|
||||||
if err != nil {
|
for x := 0; x < 2 && needsUpdates; x++ {
|
||||||
return utils.NewBuildError("Failed to import "+c.ImportPath+" with error:", "error", err)
|
needsUpdates = false
|
||||||
|
versionInfo, needsUpdates = v.doRepoCheck(x==0)
|
||||||
}
|
}
|
||||||
revelPath = revelPath + model.RevelImportPath
|
|
||||||
|
|
||||||
fmt.Println("\nRevel Framework",revelPath, appPath )
|
fmt.Println(versionInfo)
|
||||||
if err != nil {
|
cmd := exec.Command(c.GoCmd, "version")
|
||||||
utils.Logger.Info("Failed to find Revel in GOPATH with error:", "error", err, "gopath", build.Default.GOPATH)
|
cmd.Stdout = os.Stdout
|
||||||
fmt.Println("Information not available (not on GOPATH)")
|
if e := cmd.Start(); e != nil {
|
||||||
|
fmt.Println("Go command error ", e)
|
||||||
} else {
|
} else {
|
||||||
utils.Logger.Info("Fullpath to revel", "dir", revelPath)
|
cmd.Wait()
|
||||||
fset := token.NewFileSet() // positions are relative to fset
|
}
|
||||||
|
|
||||||
version, err := ioutil.ReadFile(filepath.Join(revelPath, "version.go"))
|
return
|
||||||
if err != nil {
|
}
|
||||||
utils.Logger.Error("Failed to find Revel version:", "error", err, "path", revelPath)
|
|
||||||
}
|
// Checks the Revel repos for the latest version
|
||||||
|
func (v *VersionCommand) doRepoCheck(updateLibs bool) (versionInfo string, needsUpdate bool) {
|
||||||
// Parse src but stop after processing the imports.
|
var (
|
||||||
f, err := parser.ParseFile(fset, "", version, parser.ParseComments)
|
title string
|
||||||
if err != nil {
|
localVersion *model.Version
|
||||||
return utils.NewBuildError("Failed to parse Revel version error:", "error", err)
|
)
|
||||||
}
|
for _, repo := range []string{"revel", "cmd", "modules"} {
|
||||||
|
versonFromRepo, err := v.versionFromRepo(repo, "", "version.go")
|
||||||
// Print the imports from the file's AST.
|
if err != nil {
|
||||||
for _, s := range f.Decls {
|
utils.Logger.Info("Failed to get version from repo", "repo", repo, "error", err)
|
||||||
genDecl, ok := s.(*ast.GenDecl)
|
}
|
||||||
if !ok {
|
switch repo {
|
||||||
continue
|
case "revel":
|
||||||
}
|
title, repo, localVersion = "Revel Framework", "github.com/revel/revel", v.revelVersion
|
||||||
if genDecl.Tok != token.CONST {
|
case "cmd":
|
||||||
continue
|
title, repo, localVersion = "Revel Cmd", "github.com/revel/cmd/revel", v.cmdVersion
|
||||||
}
|
case "modules":
|
||||||
for _, a := range genDecl.Specs {
|
title, repo, localVersion = "Revel Modules", "github.com/revel/modules", v.modulesVersion
|
||||||
spec := a.(*ast.ValueSpec)
|
}
|
||||||
r := spec.Values[0].(*ast.BasicLit)
|
|
||||||
fmt.Printf("Revel %s = %s\n", spec.Names[0].Name, r.Value)
|
// Only do an update on the first loop, and if specified to update
|
||||||
}
|
shouldUpdate := updateLibs && v.Command.Version.Update
|
||||||
}
|
if v.Command.Version.Update {
|
||||||
}
|
if localVersion == nil || (versonFromRepo != nil && versonFromRepo.Newer(localVersion)) {
|
||||||
fmt.Println("\nRevel Command Utility Tool")
|
needsUpdate = true
|
||||||
fmt.Println("Version", cmd.Version)
|
if shouldUpdate {
|
||||||
fmt.Println("Build Date", cmd.BuildDate)
|
v.doUpdate(title, repo, localVersion, versonFromRepo)
|
||||||
fmt.Println("Minimum Go Version", cmd.MinimumGoVersion)
|
v.updateLocalVersions()
|
||||||
|
}
|
||||||
fmt.Printf("Compiled By %s %s/%s\n\n", runtime.Version(), runtime.GOOS, runtime.GOARCH)
|
}
|
||||||
// Extract the goversion detected
|
}
|
||||||
if len(c.GoCmd) > 0 {
|
versionInfo = versionInfo + v.outputVersion(title, repo, localVersion, versonFromRepo)
|
||||||
cmd := exec.Command(c.GoCmd, "version")
|
}
|
||||||
cmd.Stdout = os.Stdout
|
return
|
||||||
if e := cmd.Start(); e != nil {
|
}
|
||||||
fmt.Println("Go command error ", e)
|
|
||||||
} else {
|
// Checks for updates if needed
|
||||||
cmd.Wait()
|
func (v *VersionCommand) doUpdate(title, repo string, local, remote *model.Version) {
|
||||||
}
|
utils.Logger.Info("Updating package", "package", title, "repo",repo)
|
||||||
} else {
|
fmt.Println("Attempting to update package", title)
|
||||||
fmt.Println("Go command not found ")
|
if err := v.Command.PackageResolver(repo); err != nil {
|
||||||
|
utils.Logger.Error("Unable to update repo", "repo", repo, "error", err)
|
||||||
|
} else if repo == "github.com/revel/cmd/revel" {
|
||||||
|
// One extra step required here to run the install for the command
|
||||||
|
utils.Logger.Fatal("Revel command tool was updated, you must manually run the following command before continuing\ngo install github.com/revel/cmd/revel")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prints out the local and remote versions, calls update if needed
|
||||||
|
func (v *VersionCommand) outputVersion(title, repo string, local, remote *model.Version) (output string) {
|
||||||
|
buffer := &bytes.Buffer{}
|
||||||
|
remoteVersion := "Unknown"
|
||||||
|
if remote != nil {
|
||||||
|
remoteVersion = remote.VersionString()
|
||||||
|
}
|
||||||
|
localVersion := "Unknown"
|
||||||
|
if local != nil {
|
||||||
|
localVersion = local.VersionString()
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(buffer, "%s\t:\t%s\t(%s remote master branch)\n", title, localVersion, remoteVersion)
|
||||||
|
return buffer.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the version from the repository
|
||||||
|
func (v *VersionCommand) versionFromRepo(repoName, branchName, fileName string) (version *model.Version, err error) {
|
||||||
|
if branchName == "" {
|
||||||
|
branchName = "master"
|
||||||
|
}
|
||||||
|
// Try to download the version of file from the repo, just use an http connection to retrieve the source
|
||||||
|
// Assuming that the repo is github
|
||||||
|
fullurl := "https://raw.githubusercontent.com/revel/" + repoName + "/" + branchName + "/" + fileName
|
||||||
|
resp, err := http.Get(fullurl)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
body, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
utils.Logger.Info("Got version file", "from", fullurl, "content", string(body))
|
||||||
|
|
||||||
|
return v.versionFromBytes(body)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns version information from a file called version on the gopath
|
||||||
|
func (v *VersionCommand) compareAndUpdateVersion(remoteVersion *model.Version, localVersion *model.Version) (err error) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (v *VersionCommand) versionFromFilepath(sourcePath string) (version *model.Version, err error) {
|
||||||
|
utils.Logger.Info("Fullpath to revel", "dir", sourcePath)
|
||||||
|
|
||||||
|
sourceStream, err := ioutil.ReadFile(filepath.Join(sourcePath, "version.go"))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return v.versionFromBytes(sourceStream)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns version information from a file called version on the gopath
|
||||||
|
func (v *VersionCommand) versionFromBytes(sourceStream []byte) (version *model.Version, err error) {
|
||||||
|
fset := token.NewFileSet() // positions are relative to fset
|
||||||
|
|
||||||
|
// Parse src but stop after processing the imports.
|
||||||
|
f, err := parser.ParseFile(fset, "", sourceStream, parser.ParseComments)
|
||||||
|
if err != nil {
|
||||||
|
err = utils.NewBuildError("Failed to parse Revel version error:", "error", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
version = &model.Version{}
|
||||||
|
|
||||||
|
// Print the imports from the file's AST.
|
||||||
|
for _, s := range f.Decls {
|
||||||
|
genDecl, ok := s.(*ast.GenDecl)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if genDecl.Tok != token.CONST {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, a := range genDecl.Specs {
|
||||||
|
spec := a.(*ast.ValueSpec)
|
||||||
|
r := spec.Values[0].(*ast.BasicLit)
|
||||||
|
switch spec.Names[0].Name {
|
||||||
|
case "Version":
|
||||||
|
version.ParseVersion(strings.Replace(r.Value, `"`, "", -1))
|
||||||
|
case "BuildDate":
|
||||||
|
version.BuildDate = r.Value
|
||||||
|
case "MinimumGoVersion":
|
||||||
|
version.MinGoVersion = r.Value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch the local version of revel from the file system
|
||||||
|
func (v *VersionCommand) updateLocalVersions() {
|
||||||
|
v.cmdVersion = &model.Version{}
|
||||||
|
v.cmdVersion.ParseVersion(cmd.Version)
|
||||||
|
v.cmdVersion.BuildDate = cmd.BuildDate
|
||||||
|
v.cmdVersion.MinGoVersion = cmd.MinimumGoVersion
|
||||||
|
|
||||||
|
var modulePath, revelPath string
|
||||||
|
_, revelPath, err := utils.FindSrcPaths(v.Command.ImportPath, model.RevelImportPath, v.Command.PackageResolver)
|
||||||
|
if err != nil {
|
||||||
|
utils.Logger.Warn("Unable to extract version information from Revel library", "error,err")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
revelPath = revelPath + model.RevelImportPath
|
||||||
|
utils.Logger.Info("Fullpath to revel", "dir", revelPath)
|
||||||
|
v.revelVersion, err = v.versionFromFilepath(revelPath)
|
||||||
|
if err != nil {
|
||||||
|
utils.Logger.Warn("Unable to extract version information from Revel", "error,err")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, modulePath, err = utils.FindSrcPaths(v.Command.ImportPath, model.RevelModulesImportPath, v.Command.PackageResolver)
|
||||||
|
if err != nil {
|
||||||
|
utils.Logger.Warn("Unable to extract version information from Revel library", "error,err")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
modulePath = modulePath + model.RevelModulesImportPath
|
||||||
|
v.modulesVersion, err = v.versionFromFilepath(modulePath)
|
||||||
|
if err != nil {
|
||||||
|
utils.Logger.Warn("Unable to extract version information from Revel Modules", "error", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -5,30 +5,30 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
|
"bytes"
|
||||||
|
"path/filepath"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Initialize the command based on the GO environment
|
// Initialize the command based on the GO environment
|
||||||
func CmdInit(c *exec.Cmd, basePath string) {
|
func CmdInit(c *exec.Cmd, basePath string) {
|
||||||
c.Dir = basePath
|
c.Dir = basePath
|
||||||
|
// Dep does not like paths that are not real, convert all paths in go to real paths
|
||||||
|
realPath := &bytes.Buffer{}
|
||||||
|
for _, p := range filepath.SplitList(build.Default.GOPATH) {
|
||||||
|
rp,_ := filepath.EvalSymlinks(p)
|
||||||
|
if realPath.Len() > 0 {
|
||||||
|
realPath.WriteString(string(filepath.ListSeparator))
|
||||||
|
}
|
||||||
|
realPath.WriteString(rp)
|
||||||
|
}
|
||||||
// Go 1.8 fails if we do not include the GOROOT
|
// Go 1.8 fails if we do not include the GOROOT
|
||||||
c.Env = []string{"GOPATH=" + build.Default.GOPATH, "PATH=" + GetEnv("PATH"), "GOROOT="+ GetEnv("GOROOT")}
|
c.Env = []string{"GOPATH=" + realPath.String(), "GOROOT="+ os.Getenv("GOROOT")}
|
||||||
// Fetch the rest of the env variables
|
// Fetch the rest of the env variables
|
||||||
for _, e := range os.Environ() {
|
for _, e := range os.Environ() {
|
||||||
pair := strings.Split(e, "=")
|
pair := strings.Split(e, "=")
|
||||||
if pair[0]=="GOPATH" {
|
if pair[0]=="GOPATH" || pair[0]=="GOROOT" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
c.Env = append(c.Env,e)
|
c.Env = append(c.Env,e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns an environment variable
|
|
||||||
func GetEnv(name string) string {
|
|
||||||
for _, v := range os.Environ() {
|
|
||||||
split := strings.Split(v, "=")
|
|
||||||
if split[0] == name {
|
|
||||||
return strings.Join(split[1:], "")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
@@ -23,6 +23,15 @@ type (
|
|||||||
IsError bool
|
IsError bool
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
// Return a new error object
|
||||||
|
func NewError(source, title,path,description string) *Error {
|
||||||
|
return &Error {
|
||||||
|
SourceType:source,
|
||||||
|
Title:title,
|
||||||
|
Path:path,
|
||||||
|
Description:description,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Creates a link based on the configuration setting "errors.link"
|
// Creates a link based on the configuration setting "errors.link"
|
||||||
func (e *Error) SetLink(errorLink string) {
|
func (e *Error) SetLink(errorLink string) {
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ func MustChmod(filename string, mode os.FileMode) {
|
|||||||
// Called if panic
|
// Called if panic
|
||||||
func PanicOnError(err error, msg string) {
|
func PanicOnError(err error, msg string) {
|
||||||
if revErr, ok := err.(*Error); (ok && revErr != nil) || (!ok && err != nil) {
|
if revErr, ok := err.(*Error); (ok && revErr != nil) || (!ok && err != nil) {
|
||||||
Logger.Panicf("Abort: %s: %s %s\n", msg, revErr, err)
|
Logger.Panicf("Abort: %s: %s %s", msg, revErr, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,6 +160,9 @@ func PanicOnError(err error, msg string) {
|
|||||||
// Additionally, the trailing ".template" is stripped from the file name.
|
// Additionally, the trailing ".template" is stripped from the file name.
|
||||||
// Also, dot files and dot directories are skipped.
|
// Also, dot files and dot directories are skipped.
|
||||||
func CopyDir(destDir, srcDir string, data map[string]interface{}) error {
|
func CopyDir(destDir, srcDir string, data map[string]interface{}) error {
|
||||||
|
if !DirExists(srcDir) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
return fsWalk(srcDir, srcDir, func(srcPath string, info os.FileInfo, err error) error {
|
return fsWalk(srcDir, srcDir, func(srcPath string, info os.FileInfo, err error) error {
|
||||||
// Get the relative path from the source base, and the corresponding path in
|
// Get the relative path from the source base, and the corresponding path in
|
||||||
// the dest directory.
|
// the dest directory.
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
// Copyright (c) 2012-2017 The Revel Framework Authors, All rights reserved.
|
// Copyright (c) 2012-2018 The Revel Framework Authors, All rights reserved.
|
||||||
// Revel Framework source code and usage is governed by a MIT style
|
// Revel Framework source code and usage is governed by a MIT style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// Version current Revel Command version
|
// Version current Revel version
|
||||||
Version = "0.20.2"
|
Version = "0.21.0"
|
||||||
|
|
||||||
// BuildDate latest commit/release date
|
// BuildDate latest commit/release date
|
||||||
BuildDate = "2018-10-02"
|
BuildDate = "2018-10-30"
|
||||||
|
|
||||||
// MinimumGoVersion minimum required Go version for Revel
|
// MinimumGoVersion minimum required Go version for Revel
|
||||||
MinimumGoVersion = ">= go1.8"
|
MinimumGoVersion = ">= go1.8"
|
||||||
|
|||||||
Reference in New Issue
Block a user