예제 #1
0
파일: tasks.go 프로젝트: rainycape/gondola
func afterTask(ctx *app.Context, task *Task, started time.Time, terr *error) {
	name := task.Name()
	if err := recover(); err != nil {
		skip, stackSkip, _, _ := runtimeutil.GetPanic()
		var buf bytes.Buffer
		fmt.Fprintf(&buf, "Panic executing task %s: %v\n", name, err)
		stack := runtimeutil.FormatStack(stackSkip)
		location, code := runtimeutil.FormatCaller(skip, 5, true, true)
		if location != "" {
			buf.WriteString("\n At ")
			buf.WriteString(location)
			if code != "" {
				buf.WriteByte('\n')
				buf.WriteString(code)
				buf.WriteByte('\n')
			}
		}
		if stack != "" {
			buf.WriteString("\nStack:\n")
			buf.WriteString(stack)
		}
		*terr = errors.New(buf.String())
	}
	end := time.Now()
	running.Lock()
	defer running.Unlock()
	c := running.tasks[task] - 1
	if c > 0 {
		running.tasks[task] = c
	} else {
		delete(running.tasks, task)
	}
	ctx.Logger().Infof("Finished task %s (%d instances now running) at %v (took %v)", name, c, end, end.Sub(started))
}
예제 #2
0
파일: app.go 프로젝트: rainycape/gondola
func (app *App) logError(ctx *Context, err interface{}) {
	skip, stackSkip, _, _ := runtimeutil.GetPanic()
	var buf bytes.Buffer
	if ctx.R != nil {
		buf.WriteString("Panic serving ")
		buf.WriteString(ctx.R.Method)
		buf.WriteByte(' ')
		buf.WriteString(ctx.R.Host)
		buf.WriteString(ctx.R.URL.Path)
		if rq := ctx.R.URL.RawQuery; rq != "" {
			buf.WriteByte('?')
			buf.WriteString(rq)
		}
		if rf := ctx.R.URL.Fragment; rf != "" {
			buf.WriteByte('#')
			buf.WriteString(rf)
		}
		buf.WriteByte(' ')
		buf.WriteString(ctx.RemoteAddress())
	} else {
		buf.WriteString("Panic")
	}
	elapsed := ctx.Elapsed()
	fmt.Fprintf(&buf, " (after %s): %v\n", elapsed, err)
	stack := runtimeutil.FormatStack(stackSkip)
	location, code := runtimeutil.FormatCaller(skip, 5, true, true)
	if location != "" {
		buf.WriteString("\n At ")
		buf.WriteString(location)
		if code != "" {
			buf.WriteByte('\n')
			buf.WriteString(code)
			buf.WriteByte('\n')
		}
	}
	if stack != "" {
		buf.WriteString("\nStack:\n")
		buf.WriteString(stack)
	}
	req := ""
	if ctx.R != nil {
		dump, derr := httputil.DumpRequest(ctx.R, true)
		if derr == nil {
			// This cleans up empty lines and replaces \r\n with \n
			req = stringutil.Lines(string(dump), 0, 10000, true)
			buf.WriteString("\nRequest:\n")
			buf.WriteString(req)
		}
		// Check if there are any attached files that we might
		// want to send in an email
		if !app.cfg.Debug && mail.AdminEmail() != "" {
			ctx.R.ParseMultipartForm(32 << 20) // 32 MiB, as stdlib
			if form := ctx.R.MultipartForm; form != nil {
				var count int
				var attachments []*mail.Attachment
				var message bytes.Buffer
				if len(form.File) > 0 {
					for k, v := range form.File {
						for _, file := range v {
							f, err := file.Open()
							if err != nil {
								fmt.Fprintf(&message, "%s => error %s", k, err)
								continue
							}
							attachment, err := mail.NewAttachment(file.Filename, f)
							attachment.ContentType = file.Header.Get("Content-Type")
							f.Close()
							if err != nil {
								fmt.Fprintf(&message, "%s => error %s", k, err)
								continue
							}
							count++
							fmt.Fprintf(&message, "%s => %s (%s)", k, attachment.Name, attachment.ContentType)
							attachments = append(attachments, attachment)
						}
					}
					fmt.Fprintf(&message, "\nError:\n%s", buf.String())
					host, _ := os.Hostname()
					from := mail.DefaultFrom()
					if from == "" {
						from = fmt.Sprintf("errors@%s", host)
					}
					msg := &mail.Message{
						From:        from,
						To:          mail.Admin,
						Subject:     fmt.Sprintf("Panic with %d attached files on %s", count, host),
						TextBody:    message.String(),
						Attachments: attachments,
					}
					ctx.SendMail("", nil, msg)
				}
			}
		}
	}
	ctx.Logger().Error(buf.String())
	if app.cfg.Debug {
		app.errorPage(ctx, elapsed, skip, stackSkip, req, err)
	} else {
		app.handleHTTPError(ctx, "Internal Server Error", http.StatusInternalServerError)
	}
}