package main
import (
"fmt"
"html/template"
"log"
"os"
"os/exec"
"path/filepath"
"runtime"
"runtime/debug"
"strings"
"time"
"github.com/robert-nix/ansihtml"
)
// https://gist.github.com/swdunlop/9629168
func identifyPanic ( ) string {
var name , file string
var line int
var pc [ 16 ] uintptr
n := runtime . Callers ( 4 , pc [ : ] )
for _ , pc := range pc [ : n ] {
fn := runtime . FuncForPC ( pc )
if fn == nil {
continue
}
file , line = fn . FileLine ( pc )
name = fn . Name ( )
if ! strings . HasPrefix ( name , "runtime." ) {
break
}
}
switch {
case name != "" :
return fmt . Sprintf ( "%v:%v" , name , line )
case file != "" :
return fmt . Sprintf ( "%v:%v" , file , line )
}
return fmt . Sprintf ( "pc:%x" , pc )
}
// OpenFile attempts to open a given file in the appropriate GUI application.
func OpenFile ( fpath string ) ( err error ) {
switch PLATFORM {
case "linux" :
err = exec . Command ( "xdg-open" , fpath ) . Start ( )
case "windows" :
err = exec . Command ( "rundll32" , "url.dll,FileProtocolHandler" , fpath ) . Start ( )
case "darwin" :
err = exec . Command ( "open" , fpath ) . Start ( )
default :
err = fmt . Errorf ( "unknown os" )
}
return
}
// Exit dumps the last 100 lines of output to a crash file in /tmp (or equivalent), and generates a prettier HTML file containing it that is opened in the browser if possible.
func Exit ( err interface { } ) {
tmpl , err2 := template . ParseFS ( localFS , "html/crash.html" , "html/header.html" )
if err2 != nil {
log . Fatalf ( "Failed to load template: %v" , err )
}
logCache := lineCache . String ( )
if err != nil {
fmt . Println ( err )
logCache += "\n" + fmt . Sprint ( err )
}
logCache += "\n" + string ( debug . Stack ( ) )
sanitized := sanitizeLog ( logCache )
data := map [ string ] interface { } {
"Log" : logCache ,
"SanitizedLog" : sanitized ,
}
if err != nil {
data [ "Err" ] = fmt . Sprintf ( "%s %v" , identifyPanic ( ) , err )
}
// Use dashes for time rather than colons for Windows
fpath := filepath . Join ( temp , "jfa-go-crash-" + time . Now ( ) . Local ( ) . Format ( "2006-01-02T15-04-05" ) )
err2 = os . WriteFile ( fpath + ".txt" , [ ] byte ( logCache ) , 0666 )
if err2 != nil {
log . Fatalf ( "Failed to write crash dump file: %v" , err2 )
}
log . Printf ( "\n------\nA crash report has been saved to \"%s\".\n------" , fpath + ".txt" )
// Render ANSI colors to HTML
data [ "Log" ] = template . HTML ( string ( ansihtml . ConvertToHTML ( [ ] byte ( data [ "Log" ] . ( string ) ) ) ) )
data [ "SanitizedLog" ] = template . HTML ( string ( ansihtml . ConvertToHTML ( [ ] byte ( data [ "SanitizedLog" ] . ( string ) ) ) ) )
data [ "Err" ] = template . HTML ( string ( ansihtml . ConvertToHTML ( [ ] byte ( data [ "Err" ] . ( string ) ) ) ) )
f , err2 := os . OpenFile ( fpath + ".html" , os . O_RDWR | os . O_CREATE | os . O_TRUNC , 0666 )
if err2 != nil {
log . Fatalf ( "Failed to open crash dump file: %v" , err2 )
}
defer f . Close ( )
err2 = tmpl . Execute ( f , data )
if err2 != nil {
log . Fatalf ( "Failed to execute template: %v" , err2 )
}
if err := OpenFile ( fpath + ".html" ) ; err != nil {
log . Printf ( "Failed to open browser, trying text file..." )
OpenFile ( fpath + ".txt" )
}
if TRAY {
QuitTray ( )
} else {
os . Exit ( 1 )
}
}