Crashes are now saved in the database
This commit is contained in:
parent
604dc3c755
commit
2e9ad4bf6f
@ -14,7 +14,7 @@ type ClientErrorReport struct {
|
|||||||
hasCreator
|
hasCreator
|
||||||
}
|
}
|
||||||
|
|
||||||
// StreamClientErrorReports returns a stream of all characters.
|
// StreamClientErrorReports returns a stream of all client error reports.
|
||||||
func StreamClientErrorReports() <-chan *ClientErrorReport {
|
func StreamClientErrorReports() <-chan *ClientErrorReport {
|
||||||
channel := make(chan *ClientErrorReport, nano.ChannelBufferSize)
|
channel := make(chan *ClientErrorReport, nano.ChannelBufferSize)
|
||||||
|
|
||||||
@ -29,10 +29,9 @@ func StreamClientErrorReports() <-chan *ClientErrorReport {
|
|||||||
return channel
|
return channel
|
||||||
}
|
}
|
||||||
|
|
||||||
// AllClientErrorReports returns a slice of all characters.
|
// AllClientErrorReports returns a slice of all client error reports.
|
||||||
func AllClientErrorReports() []*ClientErrorReport {
|
func AllClientErrorReports() []*ClientErrorReport {
|
||||||
all := make([]*ClientErrorReport, 0, DB.Collection("ClientErrorReport").Count())
|
all := make([]*ClientErrorReport, 0, DB.Collection("ClientErrorReport").Count())
|
||||||
|
|
||||||
stream := StreamClientErrorReports()
|
stream := StreamClientErrorReports()
|
||||||
|
|
||||||
for obj := range stream {
|
for obj := range stream {
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"github.com/aerogo/api"
|
"github.com/aerogo/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Force interface implementations
|
||||||
var (
|
var (
|
||||||
_ api.Newable = (*ClientErrorReport)(nil)
|
_ api.Newable = (*ClientErrorReport)(nil)
|
||||||
)
|
)
|
||||||
|
39
arn/Crash.go
Normal file
39
arn/Crash.go
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
package arn
|
||||||
|
|
||||||
|
import "github.com/aerogo/nano"
|
||||||
|
|
||||||
|
// Crash contains data about server crashes.
|
||||||
|
type Crash struct {
|
||||||
|
Error string `json:"error"`
|
||||||
|
Stack string `json:"stack"`
|
||||||
|
|
||||||
|
hasID
|
||||||
|
hasCreator
|
||||||
|
}
|
||||||
|
|
||||||
|
// StreamCrashes returns a stream of all crashes.
|
||||||
|
func StreamCrashes() <-chan *Crash {
|
||||||
|
channel := make(chan *Crash, nano.ChannelBufferSize)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for obj := range DB.All("Crash") {
|
||||||
|
channel <- obj.(*Crash)
|
||||||
|
}
|
||||||
|
|
||||||
|
close(channel)
|
||||||
|
}()
|
||||||
|
|
||||||
|
return channel
|
||||||
|
}
|
||||||
|
|
||||||
|
// AllCrashes returns a slice of all crashes.
|
||||||
|
func AllCrashes() []*Crash {
|
||||||
|
all := make([]*Crash, 0, DB.Collection("Crash").Count())
|
||||||
|
stream := StreamCrashes()
|
||||||
|
|
||||||
|
for obj := range stream {
|
||||||
|
all = append(all, obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
return all
|
||||||
|
}
|
13
arn/CrashAPI.go
Normal file
13
arn/CrashAPI.go
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package arn
|
||||||
|
|
||||||
|
import "github.com/aerogo/api"
|
||||||
|
|
||||||
|
// Force interface implementations
|
||||||
|
var (
|
||||||
|
_ api.Savable = (*Crash)(nil)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Save saves the crash in the database.
|
||||||
|
func (crash *Crash) Save() {
|
||||||
|
DB.Set("Crash", crash.ID, crash)
|
||||||
|
}
|
@ -25,6 +25,7 @@ var DB = Node.Namespace("arn").RegisterTypes(
|
|||||||
(*AnimeCharacters)(nil),
|
(*AnimeCharacters)(nil),
|
||||||
(*AnimeRelations)(nil),
|
(*AnimeRelations)(nil),
|
||||||
(*AnimeList)(nil),
|
(*AnimeList)(nil),
|
||||||
|
(*Crash)(nil),
|
||||||
(*Character)(nil),
|
(*Character)(nil),
|
||||||
(*ClientErrorReport)(nil),
|
(*ClientErrorReport)(nil),
|
||||||
(*Company)(nil),
|
(*Company)(nil),
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/aerogo/aero"
|
"github.com/aerogo/aero"
|
||||||
|
"github.com/animenotifier/notify.moe/arn"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Recover recovers from panics and shows them as the response body.
|
// Recover recovers from panics and shows them as the response body.
|
||||||
@ -27,11 +28,28 @@ func Recover(next aero.Handler) aero.Handler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
stack := make([]byte, 4096)
|
stack := make([]byte, 4096)
|
||||||
length := runtime.Stack(stack, true)
|
length := runtime.Stack(stack, false)
|
||||||
stackString := string(stack[:length])
|
stackString := string(stack[:length])
|
||||||
fmt.Fprint(os.Stderr, stackString)
|
fmt.Fprint(os.Stderr, stackString)
|
||||||
|
|
||||||
message := err.Error() + "<br><br>" + strings.ReplaceAll(stackString, "\n", "<br>")
|
// Save crash in database
|
||||||
|
crash := &arn.Crash{
|
||||||
|
Error: err.Error(),
|
||||||
|
Stack: stackString,
|
||||||
|
}
|
||||||
|
|
||||||
|
crash.ID = arn.GenerateID("Crash")
|
||||||
|
crash.Created = arn.DateTimeUTC()
|
||||||
|
user := arn.GetUserFromContext(ctx)
|
||||||
|
|
||||||
|
if user != nil {
|
||||||
|
crash.CreatedBy = user.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
crash.Save()
|
||||||
|
|
||||||
|
// Send HTML
|
||||||
|
message := "<div class='crash'>" + err.Error() + "<br><br>" + strings.ReplaceAll(stackString, "\n", "<br>") + "</div>"
|
||||||
_ = ctx.Error(http.StatusInternalServerError, message)
|
_ = ctx.Error(http.StatusInternalServerError, message)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ component AdminTabs
|
|||||||
.tabs
|
.tabs
|
||||||
Tab("Server", "server", "/admin")
|
Tab("Server", "server", "/admin")
|
||||||
Tab("WebDev", "html5", "/admin/webdev")
|
Tab("WebDev", "html5", "/admin/webdev")
|
||||||
|
Tab("Crashes", "exclamation", "/admin/crashes")
|
||||||
Tab("Client errors", "exclamation", "/admin/errors/client")
|
Tab("Client errors", "exclamation", "/admin/errors/client")
|
||||||
Tab("Registrations", "user-plus", "/admin/registrations")
|
Tab("Registrations", "user-plus", "/admin/registrations")
|
||||||
Tab("Purchases", "shopping-cart", "/admin/purchases")
|
Tab("Purchases", "shopping-cart", "/admin/purchases")
|
||||||
|
26
pages/admin/crashes.go
Normal file
26
pages/admin/crashes.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package admin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
"github.com/aerogo/aero"
|
||||||
|
"github.com/animenotifier/notify.moe/arn"
|
||||||
|
"github.com/animenotifier/notify.moe/components"
|
||||||
|
)
|
||||||
|
|
||||||
|
const maxCrashes = 80
|
||||||
|
|
||||||
|
// Crashes shows client-side errors.
|
||||||
|
func Crashes(ctx aero.Context) error {
|
||||||
|
crashes := arn.AllCrashes()
|
||||||
|
|
||||||
|
sort.Slice(crashes, func(i, j int) bool {
|
||||||
|
return crashes[i].Created > crashes[j].Created
|
||||||
|
})
|
||||||
|
|
||||||
|
if len(crashes) > maxCrashes {
|
||||||
|
crashes = crashes[:maxCrashes]
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctx.HTML(components.Crashes(crashes))
|
||||||
|
}
|
19
pages/admin/crashes.pixy
Normal file
19
pages/admin/crashes.pixy
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
component Crashes(crashes []*arn.Crash)
|
||||||
|
AdminTabs
|
||||||
|
h1.mountable Server-side crashes
|
||||||
|
|
||||||
|
table
|
||||||
|
tbody
|
||||||
|
each crash in crashes
|
||||||
|
tr.mountable
|
||||||
|
td
|
||||||
|
a(href="/api/crash/" + crash.ID, target="_blank")= crash.Error
|
||||||
|
td
|
||||||
|
each line in strings.Split(crash.Stack, "\n")
|
||||||
|
p= line
|
||||||
|
td.utc-date(data-date=crash.Created)
|
||||||
|
td.edit-log-user
|
||||||
|
if crash.CreatedBy != ""
|
||||||
|
Avatar(crash.Creator())
|
||||||
|
else
|
||||||
|
span anonymous
|
@ -16,6 +16,8 @@ import (
|
|||||||
// privateTypes are types that are not available for download.
|
// privateTypes are types that are not available for download.
|
||||||
var privateTypes = []string{
|
var privateTypes = []string{
|
||||||
"Analytics",
|
"Analytics",
|
||||||
|
"Crash",
|
||||||
|
"ClientErrorReport",
|
||||||
"EditLogEntry",
|
"EditLogEntry",
|
||||||
"EmailToUser",
|
"EmailToUser",
|
||||||
"FacebookToUser",
|
"FacebookToUser",
|
||||||
|
@ -77,6 +77,7 @@ func Register(app *aero.Application) {
|
|||||||
page.Get(app, "/admin", admin.Get)
|
page.Get(app, "/admin", admin.Get)
|
||||||
page.Get(app, "/admin/webdev", admin.WebDev)
|
page.Get(app, "/admin/webdev", admin.WebDev)
|
||||||
page.Get(app, "/admin/registrations", admin.UserRegistrations)
|
page.Get(app, "/admin/registrations", admin.UserRegistrations)
|
||||||
|
page.Get(app, "/admin/crashes", admin.Crashes)
|
||||||
page.Get(app, "/admin/errors/client", admin.ClientErrors)
|
page.Get(app, "/admin/errors/client", admin.ClientErrors)
|
||||||
page.Get(app, "/admin/purchases", admin.PurchaseHistory)
|
page.Get(app, "/admin/purchases", admin.PurchaseHistory)
|
||||||
page.Get(app, "/admin/payments", admin.PaymentHistory)
|
page.Get(app, "/admin/payments", admin.PaymentHistory)
|
||||||
|
Loading…
Reference in New Issue
Block a user