X-Git-Url: https://gerrit.akraino.org/r/gitweb?a=blobdiff_plain;f=src%2Ffoundation%2Fapi%2Frevel%2Ferrors.go;fp=src%2Ffoundation%2Fapi%2Frevel%2Ferrors.go;h=dc3807a060df26078a20729a1b15a80777e11ac2;hb=1d1ee6961c93781e1187d8c7faa868da6b2f01f4;hp=0000000000000000000000000000000000000000;hpb=56dd5e0f2164b37b40ac1daa188ccc618b4cbd19;p=iec.git diff --git a/src/foundation/api/revel/errors.go b/src/foundation/api/revel/errors.go new file mode 100644 index 0000000..dc3807a --- /dev/null +++ b/src/foundation/api/revel/errors.go @@ -0,0 +1,155 @@ +// Copyright (c) 2012-2016 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 revel + +import ( + "fmt" + "path/filepath" + "runtime/debug" + "strconv" + "strings" +) + +// Error description, used as an argument to the error template. +type Error struct { + SourceType string // The type of source that failed to build. + Title, Path, Description string // Description of the error, as presented to the user. + Line, Column int // Where the error was encountered. + SourceLines []string // The entire source file, split into lines. + Stack string // The raw stack trace string from debug.Stack(). + MetaError string // Error that occurred producing the error page. + Link string // A configurable link to wrap the error source in +} + +// SourceLine structure to hold the per-source-line details. +type SourceLine struct { + Source string + Line int + IsError bool +} + +// NewErrorFromPanic method finds the deepest stack from in user code and +// provide a code listing of that, on the line that eventually triggered +// the panic. Returns nil if no relevant stack frame can be found. +func NewErrorFromPanic(err interface{}) *Error { + + // Parse the filename and line from the originating line of app code. + // /Users/robfig/code/gocode/src/revel/examples/booking/app/controllers/hotels.go:191 (0x44735) + stack := string(debug.Stack()) + frame, basePath := findRelevantStackFrame(stack) + if frame == -1 { + return nil + } + + stack = stack[frame:] + stackElement := stack[:strings.Index(stack, "\n")] + colonIndex := strings.LastIndex(stackElement, ":") + filename := stackElement[:colonIndex] + var line int + fmt.Sscan(stackElement[colonIndex+1:], &line) + + // Show an error page. + description := "Unspecified error" + if err != nil { + description = fmt.Sprint(err) + } + lines, readErr := ReadLines(filename) + if readErr != nil { + utilLog.Error("Unable to read file", "file", filename, "error", readErr) + } + return &Error{ + Title: "Runtime Panic", + Path: filename[len(basePath):], + Line: line, + Description: description, + SourceLines: lines, + Stack: stack, + } +} + +// Error method constructs a plaintext version of the error, taking +// account that fields are optionally set. Returns e.g. Compilation Error +// (in views/header.html:51): expected right delim in end; got "}" +func (e *Error) Error() string { + loc := "" + if e.Path != "" { + line := "" + if e.Line != 0 { + line = fmt.Sprintf(":%d", e.Line) + } + loc = fmt.Sprintf("(in %s%s)", e.Path, line) + } + header := loc + if e.Title != "" { + if loc != "" { + header = fmt.Sprintf("%s %s: ", e.Title, loc) + } else { + header = fmt.Sprintf("%s: ", e.Title) + } + } + return fmt.Sprintf("%s%s Stack: %s", header, e.Description, e.Stack) +} + +// ContextSource method returns a snippet of the source around +// where the error occurred. +func (e *Error) ContextSource() []SourceLine { + if e.SourceLines == nil { + return nil + } + start := (e.Line - 1) - 5 + if start < 0 { + start = 0 + } + end := (e.Line - 1) + 5 + if end > len(e.SourceLines) { + end = len(e.SourceLines) + } + + lines := make([]SourceLine, end-start) + for i, src := range e.SourceLines[start:end] { + fileLine := start + i + 1 + lines[i] = SourceLine{src, fileLine, fileLine == e.Line} + } + return lines +} + +// SetLink method prepares a link and assign to Error.Link attribute +func (e *Error) SetLink(errorLink string) { + errorLink = strings.Replace(errorLink, "{{Path}}", e.Path, -1) + errorLink = strings.Replace(errorLink, "{{Line}}", strconv.Itoa(e.Line), -1) + + e.Link = "" + e.Path + ":" + strconv.Itoa(e.Line) + "" +} + +// Return the character index of the first relevant stack frame, or -1 if none were found. +// Additionally it returns the base path of the tree in which the identified code resides. +func findRelevantStackFrame(stack string) (int, string) { + // Find first item in SourcePath that isn't in RevelPath. + // If first item is in RevelPath, keep track of position, trim and check again. + partialStack := stack + sourcePath := filepath.ToSlash(SourcePath) + revelPath := filepath.ToSlash(RevelPath) + sumFrame := 0 + for { + frame := strings.Index(partialStack, sourcePath) + revelFrame := strings.Index(partialStack, revelPath) + + if frame == -1 { + break + } else if frame != revelFrame { + return sumFrame + frame, SourcePath + } else { + // Need to at least trim off the first character so this frame isn't caught again. + partialStack = partialStack[frame+1:] + sumFrame += frame + 1 + } + } + for _, module := range Modules { + if frame := strings.Index(stack, filepath.ToSlash(module.Path)); frame != -1 { + return frame, module.Path + } + } + return -1, "" +}