Show last cron messages on monitor page ()

As discussed on  we should store the results of the last task message on the
crontask and show them on the monitor page.

Signed-off-by: Andrew Thornton <art27@cantab.net>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
zeripath 2022-03-29 02:31:07 +01:00 committed by GitHub
parent e69b7a92ed
commit 90e0a402c1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 109 additions and 67 deletions
options/locale
services/cron
templates/admin

View file

@ -2816,6 +2816,7 @@ monitor.process = Running Processes
monitor.desc = Description
monitor.start = Start Time
monitor.execute_time = Execution Time
monitor.last_execution_result = Result
monitor.process.cancel = Cancel process
monitor.process.cancel_desc = Cancelling a process may cause data loss
monitor.process.cancel_notices = Cancel: <strong>%s</strong>?

View file

@ -51,7 +51,19 @@ type TaskTableRow struct {
Spec string
Next time.Time
Prev time.Time
Status string
LastMessage string
LastDoer string
ExecTimes int64
task *Task
}
func (t *TaskTableRow) FormatLastMessage(locale string) string {
if t.Status == "finished" {
return t.task.GetConfig().FormatMessage(locale, t.Name, t.Status, t.LastDoer)
}
return t.task.GetConfig().FormatMessage(locale, t.Name, t.Status, t.LastDoer, t.LastMessage)
}
// TaskTable represents a table of tasks
@ -85,6 +97,10 @@ func ListTasks() TaskTable {
Next: next,
Prev: prev,
ExecTimes: task.ExecTimes,
LastMessage: task.LastMessage,
Status: task.Status,
LastDoer: task.LastDoer,
task: task,
})
task.lock.Unlock()
}

View file

@ -7,8 +7,6 @@ package cron
import (
"time"
user_model "code.gitea.io/gitea/models/user"
"github.com/unknwon/i18n"
)
@ -17,7 +15,7 @@ type Config interface {
IsEnabled() bool
DoRunAtStart() bool
GetSchedule() string
FormatMessage(name, status string, doer *user_model.User, args ...interface{}) string
FormatMessage(locale, name, status, doer string, args ...interface{}) string
DoNoticeOnSuccess() bool
}
@ -70,19 +68,20 @@ func (b *BaseConfig) DoNoticeOnSuccess() bool {
}
// FormatMessage returns a message for the task
func (b *BaseConfig) FormatMessage(name, status string, doer *user_model.User, args ...interface{}) string {
// Please note the `status` string will be concatenated with `admin.dashboard.cron.` and `admin.dashboard.task.` to provide locale messages. Similarly `name` will be composed with `admin.dashboard.` to provide the locale name for the task.
func (b *BaseConfig) FormatMessage(locale, name, status, doer string, args ...interface{}) string {
realArgs := make([]interface{}, 0, len(args)+2)
realArgs = append(realArgs, i18n.Tr("en-US", "admin.dashboard."+name))
if doer == nil {
realArgs = append(realArgs, i18n.Tr(locale, "admin.dashboard."+name))
if doer == "" {
realArgs = append(realArgs, "(Cron)")
} else {
realArgs = append(realArgs, doer.Name)
realArgs = append(realArgs, doer)
}
if len(args) > 0 {
realArgs = append(realArgs, args...)
}
if doer == nil || (doer.ID == -1 && doer.Name == "(Cron)") {
return i18n.Tr("en-US", "admin.dashboard.cron."+status, realArgs...)
if doer == "" {
return i18n.Tr(locale, "admin.dashboard.cron."+status, realArgs...)
}
return i18n.Tr("en-US", "admin.dashboard.task."+status, realArgs...)
return i18n.Tr(locale, "admin.dashboard.task."+status, realArgs...)
}

View file

@ -33,6 +33,9 @@ type Task struct {
Name string
config Config
fun func(context.Context, *user_model.User, Config) error
Status string
LastMessage string
LastDoer string
ExecTimes int64
}
@ -86,24 +89,45 @@ func (t *Task) RunWithUser(doer *user_model.User, config Config) {
}()
graceful.GetManager().RunWithShutdownContext(func(baseCtx context.Context) {
pm := process.GetManager()
ctx, _, finished := pm.AddContext(baseCtx, config.FormatMessage(t.Name, "process", doer))
doerName := ""
if doer != nil && doer.ID != -1 {
doerName = doer.Name
}
ctx, _, finished := pm.AddContext(baseCtx, config.FormatMessage("en-US", t.Name, "process", doerName))
defer finished()
if err := t.fun(ctx, doer, config); err != nil {
var message string
var status string
if db.IsErrCancelled(err) {
message := err.(db.ErrCancelled).Message
if err := admin_model.CreateNotice(ctx, admin_model.NoticeTask, config.FormatMessage(t.Name, "aborted", doer, message)); err != nil {
log.Error("CreateNotice: %v", err)
}
return
}
if err := admin_model.CreateNotice(ctx, admin_model.NoticeTask, config.FormatMessage(t.Name, "error", doer, err)); err != nil {
status = "cancelled"
message = err.(db.ErrCancelled).Message
} else {
status = "error"
message = err.Error()
}
t.lock.Lock()
t.LastMessage = message
t.Status = status
t.LastDoer = doerName
t.lock.Unlock()
if err := admin_model.CreateNotice(ctx, admin_model.NoticeTask, config.FormatMessage("en-US", t.Name, "cancelled", doerName, message)); err != nil {
log.Error("CreateNotice: %v", err)
}
return
}
t.lock.Lock()
t.Status = "finished"
t.LastMessage = ""
t.LastDoer = doerName
t.lock.Unlock()
if config.DoNoticeOnSuccess() {
if err := admin_model.CreateNotice(ctx, admin_model.NoticeTask, config.FormatMessage(t.Name, "finished", doer)); err != nil {
if err := admin_model.CreateNotice(ctx, admin_model.NoticeTask, config.FormatMessage("en-US", t.Name, "finished", doerName)); err != nil {
log.Error("CreateNotice: %v", err)
}
}

35
templates/admin/cron.tmpl Normal file
View file

@ -0,0 +1,35 @@
<h4 class="ui top attached header">
{{.i18n.Tr "admin.monitor.cron"}}
</h4>
<div class="ui attached table segment">
<form method="post" action="{{AppSubUrl}}/admin">
<table class="ui very basic striped table">
<thead>
<tr>
<th></th>
<th>{{.i18n.Tr "admin.monitor.name"}}</th>
<th>{{.i18n.Tr "admin.monitor.schedule"}}</th>
<th>{{.i18n.Tr "admin.monitor.next"}}</th>
<th>{{.i18n.Tr "admin.monitor.previous"}}</th>
<th>{{.i18n.Tr "admin.monitor.execute_times"}}</th>
<th>{{.i18n.Tr "admin.monitor.last_execution_result"}}</th>
</tr>
</thead>
<tbody>
{{range .Entries}}
<tr>
<td><button type="submit" class="ui green button" name="op" value="{{.Name}}" title="{{$.i18n.Tr "admin.dashboard.operation_run"}}">{{svg "octicon-triangle-right"}}</button></td>
<td>{{$.i18n.Tr (printf "admin.dashboard.%s" .Name)}}</td>
<td>{{.Spec}}</td>
<td>{{DateFmtLong .Next}}</td>
<td>{{if gt .Prev.Year 1 }}{{DateFmtLong .Prev}}{{else}}N/A{{end}}</td>
<td>{{.ExecTimes}}</td>
<td {{if ne .Status ""}}class="tooltip" data-content="{{.FormatLastMessage $.i18n.Language}}"{{end}} >{{if eq .Status "" }}{{else if eq .Status "finished"}}{{svg "octicon-check" 16}}{{else}}{{svg "octicon-x" 16}}{{end}}</td>
</tr>
{{end}}
</tbody>
</table>
<input type="hidden" name="from" value="monitor"/>
{{.CsrfTokenHtml}}
</form>
</div>

View file

@ -3,40 +3,7 @@
{{template "admin/navbar" .}}
<div class="ui container">
{{template "base/alert" .}}
<h4 class="ui top attached header">
{{.i18n.Tr "admin.monitor.cron"}}
</h4>
<div class="ui attached table segment">
<form method="post" action="{{AppSubUrl}}/admin">
<table class="ui very basic striped table">
<thead>
<tr>
<th></th>
<th>{{.i18n.Tr "admin.monitor.name"}}</th>
<th>{{.i18n.Tr "admin.monitor.schedule"}}</th>
<th>{{.i18n.Tr "admin.monitor.next"}}</th>
<th>{{.i18n.Tr "admin.monitor.previous"}}</th>
<th>{{.i18n.Tr "admin.monitor.execute_times"}}</th>
</tr>
</thead>
<tbody>
{{range .Entries}}
<tr>
<td><button type="submit" class="ui green button" name="op" value="{{.Name}}" title="{{$.i18n.Tr "admin.dashboard.operation_run"}}">{{svg "octicon-triangle-right"}}</button></td>
<td>{{$.i18n.Tr (printf "admin.dashboard.%s" .Name)}}</td>
<td>{{.Spec}}</td>
<td>{{DateFmtLong .Next}}</td>
<td>{{if gt .Prev.Year 1 }}{{DateFmtLong .Prev}}{{else}}N/A{{end}}</td>
<td>{{.ExecTimes}}</td>
</tr>
{{end}}
</tbody>
</table>
<input type="hidden" name="from" value="monitor"/>
{{.CsrfTokenHtml}}
</form>
</div>
{{template "admin/cron" .}}
<h4 class="ui top attached header">
{{.i18n.Tr "admin.monitor.queues"}}
</h4>