feature: Logs table creates an entry as soon as the execution is started (#464)

This commit is contained in:
James Read 2024-11-01 21:23:22 +00:00 committed by GitHub
parent 964dc7b48a
commit 1bfb7c4b28
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 79 additions and 48 deletions

View File

@ -199,6 +199,10 @@ message EventExecutionFinished {
LogEntry log_entry = 1;
}
message EventExecutionStarted {
LogEntry log_entry = 1;
}
message KillActionRequest {
string execution_tracking_id = 1;
}

View File

@ -125,7 +125,7 @@ func DefaultExecutor(cfg *config.Config) *Executor {
}
type listener interface {
OnExecutionStarted(actionTitle string)
OnExecutionStarted(logEntry *InternalLogEntry)
OnExecutionFinished(logEntry *InternalLogEntry)
OnOutputChunk(o []byte, executionTrackingId string)
OnActionMapRebuilt()
@ -233,7 +233,7 @@ func (e *Executor) execChain(req *ExecutionRequest) {
// This isn't a step, because we want to notify all listeners, irrespective
// of how many steps were actually executed.
notifyListeners(req)
notifyListenersFinished(req)
}
func getConcurrentCount(req *ExecutionRequest) int {
@ -400,6 +400,8 @@ func stepRequestAction(req *ExecutionRequest) bool {
"tags": req.Tags,
}).Infof("Action requested")
notifyListenersStarted(req)
return true
}
@ -425,12 +427,18 @@ func stepLogFinish(req *ExecutionRequest) bool {
return true
}
func notifyListeners(req *ExecutionRequest) {
func notifyListenersFinished(req *ExecutionRequest) {
for _, listener := range req.executor.listeners {
listener.OnExecutionFinished(req.logEntry)
}
}
func notifyListenersStarted(req *ExecutionRequest) {
for _, listener := range req.executor.listeners {
listener.OnExecutionStarted(req.logEntry)
}
}
func appendErrorToStderr(err error, logEntry *InternalLogEntry) {
if err != nil {
logEntry.Output = err.Error() + "\n\n" + logEntry.Output

View File

@ -35,13 +35,10 @@ var ExecutionListener WebsocketExecutionListener
type WebsocketExecutionListener struct{}
func (WebsocketExecutionListener) OnExecutionStarted(title string) {
/*
broadcast(ExecutionStarted{
Type: "ExecutionStarted",
Action: title,
func (WebsocketExecutionListener) OnExecutionStarted(ile *executor.InternalLogEntry) {
broadcast(&pb.EventExecutionStarted{
LogEntry: internalLogEntryToPb(ile),
});
*/
}
func OnEntityChanged() {
@ -82,22 +79,7 @@ func (WebsocketExecutionListener) OnOutputChunk(chunk []byte, executionTrackingI
func (WebsocketExecutionListener) OnExecutionFinished(logEntry *executor.InternalLogEntry) {
evt := &pb.EventExecutionFinished{
LogEntry: &pb.LogEntry{
ActionTitle: logEntry.ActionTitle,
ActionIcon: logEntry.ActionIcon,
ActionId: logEntry.ActionId,
DatetimeStarted: logEntry.DatetimeStarted.Format("2006-01-02 15:04:05"),
DatetimeFinished: logEntry.DatetimeFinished.Format("2006-01-02 15:04:05"),
Output: logEntry.Output,
TimedOut: logEntry.TimedOut,
Blocked: logEntry.Blocked,
ExitCode: logEntry.ExitCode,
Tags: logEntry.Tags,
ExecutionTrackingId: logEntry.ExecutionTrackingID,
ExecutionStarted: logEntry.ExecutionStarted,
ExecutionFinished: logEntry.ExecutionFinished,
User: logEntry.Username,
},
LogEntry: internalLogEntryToPb(logEntry),
}
log.Infof("Execution finished: %v+v", evt.LogEntry)
@ -178,3 +160,23 @@ func HandleWebsocket(w http.ResponseWriter, r *http.Request) bool {
return true
}
func internalLogEntryToPb(logEntry *executor.InternalLogEntry) *pb.LogEntry {
return &pb.LogEntry{
ActionTitle: logEntry.ActionTitle,
ActionIcon: logEntry.ActionIcon,
ActionId: logEntry.ActionId,
DatetimeStarted: logEntry.DatetimeStarted.Format("2006-01-02 15:04:05"),
DatetimeFinished: logEntry.DatetimeFinished.Format("2006-01-02 15:04:05"),
Output: logEntry.Output,
TimedOut: logEntry.TimedOut,
Blocked: logEntry.Blocked,
ExitCode: logEntry.ExitCode,
Tags: logEntry.Tags,
ExecutionTrackingId: logEntry.ExecutionTrackingID,
ExecutionStarted: logEntry.ExecutionStarted,
ExecutionFinished: logEntry.ExecutionFinished,
User: logEntry.Username,
}
}

View File

@ -54,12 +54,13 @@ export function initMarshaller () {
window.executionDialog = new ExecutionDialog()
window.logEntries = {}
window.logEntries = new Map()
window.registeredPaths = new Map()
window.breadcrumbNavigation = []
window.currentPath = ''
window.addEventListener('EventExecutionStarted', onExecutionStarted)
window.addEventListener('EventExecutionFinished', onExecutionFinished)
window.addEventListener('EventOutputChunk', onOutputChunk)
}
@ -125,6 +126,14 @@ function onOutputChunk (evt) {
}
}
function onExecutionStarted (evt) {
const logEntry = evt.payload.logEntry
marshalLogsJsonToHtml({
logs: [logEntry]
})
}
function onExecutionFinished (evt) {
const logEntry = evt.payload.logEntry
@ -136,7 +145,9 @@ function onExecutionFinished (evt) {
switch (actionButton.popupOnStart) {
case 'execution-button':
if (document.querySelector('execution-button#execution-' + logEntry.executionTrackingId) !== null) { // If the button was created in our instance
document.querySelector('execution-button#execution-' + logEntry.executionTrackingId).onExecutionFinished(logEntry)
}
break
case 'execution-dialog-stdout-only':
case 'execution-dialog':
@ -617,41 +628,46 @@ function marshalDirectory (item, section) {
export function marshalLogsJsonToHtml (json) {
for (const logEntry of json.logs) {
const existing = window.logEntries[logEntry.executionTrackingId]
let row
if (existing !== undefined) {
continue
if (window.logEntries.has(logEntry.executionTrackingId)) {
row = window.logEntries.get(logEntry.executionTrackingId).dom
} else {
const tpl = document.getElementById('tplLogRow')
row = tpl.content.querySelector('tr').cloneNode(true)
row.querySelector('.content').onclick = () => {
window.executionDialog.reset()
window.executionDialog.show()
window.executionDialog.renderExecutionResult({
logEntry: window.logEntries.get(logEntry.executionTrackingId)
})
pushNewNavigationPath('/logs/' + logEntry.executionTrackingId)
}
window.logEntries[logEntry.executionTrackingId] = logEntry
row.exitCodeDisplay = new ActionStatusDisplay(row.querySelector('.exit-code'))
const tpl = document.getElementById('tplLogRow')
const row = tpl.content.querySelector('tr').cloneNode(true)
logEntry.dom = row
window.logEntries.set(logEntry.executionTrackingId, logEntry)
document.querySelector('#logTableBody').prepend(row)
}
row.querySelector('.timestamp').innerText = logEntry.datetimeStarted
row.querySelector('.content').innerText = logEntry.actionTitle
row.querySelector('.icon').innerHTML = logEntry.actionIcon
row.setAttribute('title', logEntry.actionTitle)
const exitCodeDisplay = new ActionStatusDisplay(row.querySelector('.exit-code'))
exitCodeDisplay.update(logEntry)
row.exitCodeDisplay.update(logEntry)
row.querySelector('.content').onclick = () => {
window.executionDialog.reset()
window.executionDialog.show()
window.executionDialog.renderExecutionResult({
logEntry: window.logEntries[logEntry.executionTrackingId]
})
pushNewNavigationPath('/logs/' + logEntry.executionTrackingId)
}
row.querySelector('.tags').innerHTML = ''
for (const tag of logEntry.tags) {
row.querySelector('.tags').append(createTag(tag))
}
row.querySelector('.tags').append(createAnnotation('user', logEntry.user))
document.querySelector('#logTableBody').prepend(row)
}
}

View File

@ -54,6 +54,7 @@ function websocketOnMessage (msg) {
case 'EventOutputChunk':
case 'EventConfigChanged':
case 'EventExecutionFinished':
case 'EventExecutionStarted':
case 'EventEntityChanged':
window.dispatchEvent(e)
break