mirror of
				https://codeberg.org/forgejo/forgejo.git
				synced 2025-10-30 22:11:07 +00:00 
			
		
		
		
	* When route cannot be found on chi, go to macaron * Stick chi version to 1.5.0 * Follow router log setting
		
			
				
	
	
		
			192 lines
		
	
	
	
		
			4.4 KiB
		
	
	
	
		
			Go
		
	
	
	
		
			Vendored
		
	
	
	
			
		
		
	
	
			192 lines
		
	
	
	
		
			4.4 KiB
		
	
	
	
		
			Go
		
	
	
	
		
			Vendored
		
	
	
	
| package middleware
 | |
| 
 | |
| // The original work was derived from Goji's middleware, source:
 | |
| // https://github.com/zenazn/goji/tree/master/web/middleware
 | |
| 
 | |
| import (
 | |
| 	"bytes"
 | |
| 	"errors"
 | |
| 	"fmt"
 | |
| 	"net/http"
 | |
| 	"os"
 | |
| 	"runtime/debug"
 | |
| 	"strings"
 | |
| )
 | |
| 
 | |
| // Recoverer is a middleware that recovers from panics, logs the panic (and a
 | |
| // backtrace), and returns a HTTP 500 (Internal Server Error) status if
 | |
| // possible. Recoverer prints a request ID if one is provided.
 | |
| //
 | |
| // Alternatively, look at https://github.com/pressly/lg middleware pkgs.
 | |
| func Recoverer(next http.Handler) http.Handler {
 | |
| 	fn := func(w http.ResponseWriter, r *http.Request) {
 | |
| 		defer func() {
 | |
| 			if rvr := recover(); rvr != nil && rvr != http.ErrAbortHandler {
 | |
| 
 | |
| 				logEntry := GetLogEntry(r)
 | |
| 				if logEntry != nil {
 | |
| 					logEntry.Panic(rvr, debug.Stack())
 | |
| 				} else {
 | |
| 					PrintPrettyStack(rvr)
 | |
| 				}
 | |
| 
 | |
| 				w.WriteHeader(http.StatusInternalServerError)
 | |
| 			}
 | |
| 		}()
 | |
| 
 | |
| 		next.ServeHTTP(w, r)
 | |
| 	}
 | |
| 
 | |
| 	return http.HandlerFunc(fn)
 | |
| }
 | |
| 
 | |
| func PrintPrettyStack(rvr interface{}) {
 | |
| 	debugStack := debug.Stack()
 | |
| 	s := prettyStack{}
 | |
| 	out, err := s.parse(debugStack, rvr)
 | |
| 	if err == nil {
 | |
| 		os.Stderr.Write(out)
 | |
| 	} else {
 | |
| 		// print stdlib output as a fallback
 | |
| 		os.Stderr.Write(debugStack)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| type prettyStack struct {
 | |
| }
 | |
| 
 | |
| func (s prettyStack) parse(debugStack []byte, rvr interface{}) ([]byte, error) {
 | |
| 	var err error
 | |
| 	useColor := true
 | |
| 	buf := &bytes.Buffer{}
 | |
| 
 | |
| 	cW(buf, false, bRed, "\n")
 | |
| 	cW(buf, useColor, bCyan, " panic: ")
 | |
| 	cW(buf, useColor, bBlue, "%v", rvr)
 | |
| 	cW(buf, false, bWhite, "\n \n")
 | |
| 
 | |
| 	// process debug stack info
 | |
| 	stack := strings.Split(string(debugStack), "\n")
 | |
| 	lines := []string{}
 | |
| 
 | |
| 	// locate panic line, as we may have nested panics
 | |
| 	for i := len(stack) - 1; i > 0; i-- {
 | |
| 		lines = append(lines, stack[i])
 | |
| 		if strings.HasPrefix(stack[i], "panic(0x") {
 | |
| 			lines = lines[0 : len(lines)-2] // remove boilerplate
 | |
| 			break
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// reverse
 | |
| 	for i := len(lines)/2 - 1; i >= 0; i-- {
 | |
| 		opp := len(lines) - 1 - i
 | |
| 		lines[i], lines[opp] = lines[opp], lines[i]
 | |
| 	}
 | |
| 
 | |
| 	// decorate
 | |
| 	for i, line := range lines {
 | |
| 		lines[i], err = s.decorateLine(line, useColor, i)
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	for _, l := range lines {
 | |
| 		fmt.Fprintf(buf, "%s", l)
 | |
| 	}
 | |
| 	return buf.Bytes(), nil
 | |
| }
 | |
| 
 | |
| func (s prettyStack) decorateLine(line string, useColor bool, num int) (string, error) {
 | |
| 	line = strings.TrimSpace(line)
 | |
| 	if strings.HasPrefix(line, "\t") || strings.Contains(line, ".go:") {
 | |
| 		return s.decorateSourceLine(line, useColor, num)
 | |
| 	} else if strings.HasSuffix(line, ")") {
 | |
| 		return s.decorateFuncCallLine(line, useColor, num)
 | |
| 	} else {
 | |
| 		if strings.HasPrefix(line, "\t") {
 | |
| 			return strings.Replace(line, "\t", "      ", 1), nil
 | |
| 		} else {
 | |
| 			return fmt.Sprintf("    %s\n", line), nil
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (s prettyStack) decorateFuncCallLine(line string, useColor bool, num int) (string, error) {
 | |
| 	idx := strings.LastIndex(line, "(")
 | |
| 	if idx < 0 {
 | |
| 		return "", errors.New("not a func call line")
 | |
| 	}
 | |
| 
 | |
| 	buf := &bytes.Buffer{}
 | |
| 	pkg := line[0:idx]
 | |
| 	// addr := line[idx:]
 | |
| 	method := ""
 | |
| 
 | |
| 	idx = strings.LastIndex(pkg, string(os.PathSeparator))
 | |
| 	if idx < 0 {
 | |
| 		idx = strings.Index(pkg, ".")
 | |
| 		method = pkg[idx:]
 | |
| 		pkg = pkg[0:idx]
 | |
| 	} else {
 | |
| 		method = pkg[idx+1:]
 | |
| 		pkg = pkg[0 : idx+1]
 | |
| 		idx = strings.Index(method, ".")
 | |
| 		pkg += method[0:idx]
 | |
| 		method = method[idx:]
 | |
| 	}
 | |
| 	pkgColor := nYellow
 | |
| 	methodColor := bGreen
 | |
| 
 | |
| 	if num == 0 {
 | |
| 		cW(buf, useColor, bRed, " -> ")
 | |
| 		pkgColor = bMagenta
 | |
| 		methodColor = bRed
 | |
| 	} else {
 | |
| 		cW(buf, useColor, bWhite, "    ")
 | |
| 	}
 | |
| 	cW(buf, useColor, pkgColor, "%s", pkg)
 | |
| 	cW(buf, useColor, methodColor, "%s\n", method)
 | |
| 	// cW(buf, useColor, nBlack, "%s", addr)
 | |
| 	return buf.String(), nil
 | |
| }
 | |
| 
 | |
| func (s prettyStack) decorateSourceLine(line string, useColor bool, num int) (string, error) {
 | |
| 	idx := strings.LastIndex(line, ".go:")
 | |
| 	if idx < 0 {
 | |
| 		return "", errors.New("not a source line")
 | |
| 	}
 | |
| 
 | |
| 	buf := &bytes.Buffer{}
 | |
| 	path := line[0 : idx+3]
 | |
| 	lineno := line[idx+3:]
 | |
| 
 | |
| 	idx = strings.LastIndex(path, string(os.PathSeparator))
 | |
| 	dir := path[0 : idx+1]
 | |
| 	file := path[idx+1:]
 | |
| 
 | |
| 	idx = strings.Index(lineno, " ")
 | |
| 	if idx > 0 {
 | |
| 		lineno = lineno[0:idx]
 | |
| 	}
 | |
| 	fileColor := bCyan
 | |
| 	lineColor := bGreen
 | |
| 
 | |
| 	if num == 1 {
 | |
| 		cW(buf, useColor, bRed, " ->   ")
 | |
| 		fileColor = bRed
 | |
| 		lineColor = bMagenta
 | |
| 	} else {
 | |
| 		cW(buf, false, bWhite, "      ")
 | |
| 	}
 | |
| 	cW(buf, useColor, bWhite, "%s", dir)
 | |
| 	cW(buf, useColor, fileColor, "%s", file)
 | |
| 	cW(buf, useColor, lineColor, "%s", lineno)
 | |
| 	if num == 1 {
 | |
| 		cW(buf, false, bWhite, "\n")
 | |
| 	}
 | |
| 	cW(buf, false, bWhite, "\n")
 | |
| 
 | |
| 	return buf.String(), nil
 | |
| }
 |