forked from TrueCloudLab/distribution
Feature: Web Panic Reporting via hooks
This PR is for issue of "email after registry webapp panic" #41, improving my previous design (closed). It use self setting up hooks, to catch panic in web application. And, send email in hooks handle directly, to no use new http server and handler. Signed-off-by: xiekeyang <keyangxie@126.com>
This commit is contained in:
parent
5f553b3cfc
commit
9d7c6923c1
3 changed files with 125 additions and 0 deletions
|
@ -9,6 +9,7 @@ import (
|
|||
"os"
|
||||
"time"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/docker/distribution"
|
||||
"github.com/docker/distribution/configuration"
|
||||
ctxu "github.com/docker/distribution/context"
|
||||
|
@ -101,6 +102,7 @@ func NewApp(ctx context.Context, configuration configuration.Configuration) *App
|
|||
|
||||
app.configureEvents(&configuration)
|
||||
app.configureRedis(&configuration)
|
||||
app.configureLogHook(&configuration)
|
||||
|
||||
// configure storage caches
|
||||
if cc, ok := configuration.Storage["cache"]; ok {
|
||||
|
@ -291,6 +293,31 @@ func (app *App) configureRedis(configuration *configuration.Configuration) {
|
|||
}))
|
||||
}
|
||||
|
||||
// configureLogHook prepares logging hook parameters.
|
||||
func (app *App) configureLogHook(configuration *configuration.Configuration) {
|
||||
logger := ctxu.GetLogger(app).(*log.Entry).Logger
|
||||
for _, configHook := range configuration.Log.Hooks {
|
||||
if !configHook.Disabled {
|
||||
switch configHook.Type {
|
||||
case "mail":
|
||||
hook := &logHook{}
|
||||
hook.LevelsParam = configHook.Levels
|
||||
hook.Mail = &mailer{
|
||||
Addr: configHook.MailOptions.SMTP.Addr,
|
||||
Username: configHook.MailOptions.SMTP.Username,
|
||||
Password: configHook.MailOptions.SMTP.Password,
|
||||
Insecure: configHook.MailOptions.SMTP.Insecure,
|
||||
From: configHook.MailOptions.From,
|
||||
To: configHook.MailOptions.To,
|
||||
}
|
||||
logger.Hooks.Add(hook)
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
app.Context = ctxu.WithLogger(app.Context, logger)
|
||||
}
|
||||
|
||||
func (app *App) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close() // ensure that request body is always closed.
|
||||
|
||||
|
|
53
docs/handlers/hooks.go
Normal file
53
docs/handlers/hooks.go
Normal file
|
@ -0,0 +1,53 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
)
|
||||
|
||||
// logHook is for hooking Panic in web application
|
||||
type logHook struct {
|
||||
LevelsParam []string
|
||||
Mail *mailer
|
||||
}
|
||||
|
||||
// Fire forwards an error to LogHook
|
||||
func (hook *logHook) Fire(entry *logrus.Entry) error {
|
||||
addr := strings.Split(hook.Mail.Addr, ":")
|
||||
if len(addr) != 2 {
|
||||
return errors.New("Invalid Mail Address")
|
||||
}
|
||||
host := addr[0]
|
||||
subject := fmt.Sprintf("[%s] %s: %s", entry.Level, host, entry.Message)
|
||||
|
||||
html := `
|
||||
{{.Message}}
|
||||
|
||||
{{range $key, $value := .Data}}
|
||||
{{$key}}: {{$value}}
|
||||
{{end}}
|
||||
`
|
||||
b := bytes.NewBuffer(make([]byte, 0))
|
||||
t := template.Must(template.New("mail body").Parse(html))
|
||||
if err := t.Execute(b, entry); err != nil {
|
||||
return err
|
||||
}
|
||||
body := fmt.Sprintf("%s", b)
|
||||
|
||||
return hook.Mail.sendMail(subject, body)
|
||||
}
|
||||
|
||||
// Levels contains hook levels to be catched
|
||||
func (hook *logHook) Levels() []logrus.Level {
|
||||
levels := []logrus.Level{}
|
||||
for _, v := range hook.LevelsParam {
|
||||
lv, _ := logrus.ParseLevel(v)
|
||||
levels = append(levels, lv)
|
||||
}
|
||||
return levels
|
||||
}
|
45
docs/handlers/mail.go
Normal file
45
docs/handlers/mail.go
Normal file
|
@ -0,0 +1,45 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/smtp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// mailer provides fields of email configuration for sending.
|
||||
type mailer struct {
|
||||
Addr, Username, Password, From string
|
||||
Insecure bool
|
||||
To []string
|
||||
}
|
||||
|
||||
// sendMail allows users to send email, only if mail parameters is configured correctly.
|
||||
func (mail *mailer) sendMail(subject, message string) error {
|
||||
addr := strings.Split(mail.Addr, ":")
|
||||
if len(addr) != 2 {
|
||||
return errors.New("Invalid Mail Address")
|
||||
}
|
||||
host := addr[0]
|
||||
msg := []byte("To:" + strings.Join(mail.To, ";") +
|
||||
"\r\nFrom: " + mail.From +
|
||||
"\r\nSubject: " + subject +
|
||||
"\r\nContent-Type: text/plain\r\n\r\n" +
|
||||
message)
|
||||
auth := smtp.PlainAuth(
|
||||
"",
|
||||
mail.Username,
|
||||
mail.Password,
|
||||
host,
|
||||
)
|
||||
err := smtp.SendMail(
|
||||
mail.Addr,
|
||||
auth,
|
||||
mail.From,
|
||||
mail.To,
|
||||
[]byte(msg),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
Loading…
Reference in a new issue