Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
b984929
initial work, almost there
Mar 28, 2016
b062270
fix
Mar 28, 2016
4f16f0f
fix build
Mar 28, 2016
e0c9b0c
more fixes and improvements
Mar 28, 2016
0127a1e
fix scrolling + add log line buffer size
Mar 28, 2016
2083a77
fix humanize import
Mar 28, 2016
86e687d
misc cleanup
Mar 28, 2016
a241f2c
fix logger unmount
Mar 28, 2016
81f4d99
set proper path when adding new task group
Mar 28, 2016
9703fcd
implement scroll to top / scroll to bottom
Mar 28, 2016
f38417b
dont allow users to deselect all tasks
Mar 28, 2016
530ceee
label-ify task id placeholder
Mar 28, 2016
6dceecc
add link to instance
Mar 28, 2016
85f47d4
sort task loggers by instance number
Mar 28, 2016
678bf64
fix bug causing negative lengths to be requested
Mar 29, 2016
f400479
make sure we never request a negative length, ever!
Mar 29, 2016
32a955c
make paging work better + tweak mapDispatchToProps
Mar 30, 2016
b666f51
more work
Apr 3, 2016
f1ceb34
fix task group header
Apr 4, 2016
63e4b55
more work
Apr 8, 2016
2205730
fix
Apr 8, 2016
bd14778
add another timestamp regex
Apr 8, 2016
d1c813a
colors n shit
Apr 8, 2016
1f1031d
remove log
Apr 8, 2016
4ad1f7e
fix logline key + tweak timestamp regex
Apr 8, 2016
c48f25c
fix bug
Apr 10, 2016
f9f62cc
tweaks
Apr 10, 2016
17a355e
fix logline permalink
Apr 11, 2016
bbd374a
improve task status indicator
Apr 11, 2016
d5d5db0
remove waypoint dep, not used anymore
Apr 11, 2016
f58e4ab
more fixes
Apr 11, 2016
f91bace
more tweaks
Apr 11, 2016
3db49dd
delete some crap
Apr 12, 2016
d74df2d
fix tail end of file
Apr 12, 2016
d662035
remove unnecessary variable
Apr 12, 2016
79cee5d
update updatedAt when task group is reset
Apr 12, 2016
3a7046f
fix adding group during search + improve top / bottom labels
Apr 12, 2016
ac27d1f
fix task toggle + misc cleanup
Apr 12, 2016
3d62da0
force polling when a task group is ready
Apr 12, 2016
9236969
further cleanup
Apr 12, 2016
fe873a4
forgot one of the react-waypoints to remove
Apr 15, 2016
08f1a07
set NODE_ENV to production
Apr 15, 2016
926290f
right-align dropdowns in tailer
Apr 19, 2016
763ed69
strip carriage returns
Apr 25, 2016
2d46d4a
Merge branch 'master' into tailer2000
Apr 27, 2016
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions SingularityUI/app/actions/activeTasks.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
fetchTasksForRequest = (requestId, state='active') ->
params = {
property: 'taskId'
}
$.ajax
url: "#{ config.apiRoot }/history/request/#{ requestId }/tasks/#{ state }?#{ $.param(params) }"

updateActiveTasks = (requestId) ->
(dispatch) ->
fetchTasksForRequest(requestId).done (tasks) ->
dispatch({tasks, type: 'REQUEST_ACTIVE_TASKS'})

module.exports = { updateActiveTasks, fetchTasksForRequest }
338 changes: 338 additions & 0 deletions SingularityUI/app/actions/log.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,338 @@
Q = require 'q'

{ fetchTasksForRequest } = require './activeTasks'

fetchData = (taskId, path, offset=undefined, length=0) ->
length = Math.max(length, 0) # API breaks if you request a negative length
$.ajax
url: "#{ config.apiRoot }/sandbox/#{ taskId }/read?#{$.param({path, length, offset})}"

fetchTaskHistory = (taskId) ->
$.ajax
url: "#{ config.apiRoot }/history/task/#{ taskId }"

initializeUsingActiveTasks = (requestId, path, search) ->
(dispatch) ->
deferred = Q.defer()
fetchTasksForRequest(requestId).done (tasks) ->
taskIds = _.sortBy(_.pluck(tasks, 'taskId'), (taskId) -> taskId.instanceNo).map((taskId) -> taskId.id)
dispatch(initialize(requestId, path, search, taskIds)).then ->
deferred.resolve()
deferred.promise

initialize = (requestId, path, search, taskIds) ->
(dispatch, getState) ->
{ viewMode } = getState()

if viewMode is 'unified'
taskIdGroups = [taskIds]
else
taskIdGroups = taskIds.map (taskId) -> [taskId]

dispatch(init(requestId, taskIdGroups, path, search))

groupPromises = taskIdGroups.map (taskIds, taskGroupId) ->
taskInitPromises = taskIds.map (taskId) ->
taskInitDeferred = Q.defer()
resolvedPath = path.replace('$TASK_ID', taskId)
fetchData(taskId, resolvedPath).done ({offset}) ->
dispatch(initTask(taskId, offset, resolvedPath, true))
taskInitDeferred.resolve()
.error ({status}) ->
if status is 404
app.caughtError()
dispatch(taskFileDoesNotExist(taskGroupId, taskId))
taskInitDeferred.resolve()
else
taskInitDeferred.reject()
return taskInitDeferred.promise

taskStatusPromises = taskIds.map (taskId) ->
dispatch(updateTaskStatus(taskGroupId, taskId))

Promise.all(taskInitPromises, taskStatusPromises).then ->
dispatch(taskGroupFetchPrevious(taskGroupId)).then ->
dispatch(taskGroupReady(taskGroupId))

Promise.all(groupPromises)

init = (requestId, taskIdGroups, path, search) ->
{
requestId
taskIdGroups
path
search
type: 'LOG_INIT'
}

addTaskGroup = (taskIds, search) ->
{
taskIds
search
type: 'LOG_ADD_TASK_GROUP'
}

initTask = (taskId, offset, path, exists) ->
{
taskId
offset
path
exists
type: 'LOG_TASK_INIT'
}

taskFileDoesNotExist = (taskGroupId, taskId) ->
{
taskId
taskGroupId
type: 'LOG_TASK_FILE_DOES_NOT_EXIST'
}

taskGroupReady = (taskGroupId) ->
{
taskGroupId
type: 'LOG_TASK_GROUP_READY'
}

taskHistory = (taskGroupId, taskId, taskHistory) ->
{
taskGroupId
taskId
taskHistory
type: 'LOG_TASK_HISTORY'
}

getTasks = (taskGroup, tasks) ->
taskGroup.taskIds.map (taskId) -> tasks[taskId]

updateFilesizes = ->
(dispatch, getState) ->
{ tasks } = getState()
for taskId of tasks
fetchData(taskId, tasks[taskId.path]).done ({offset}) ->
dispatch(taskFilesize(taskId, offset))

updateGroups = ->
(dispatch, getState) ->
getState().taskGroups.map (taskGroup, taskGroupId) ->
unless taskGroup.pendingRequests
if taskGroup.top
dispatch(taskGroupFetchPrevious(taskGroupId))
if taskGroup.bottom or taskGroup.tailing
dispatch(taskGroupFetchNext(taskGroupId))

updateTaskStatuses = ->
(dispatch, getState) ->
{tasks, taskGroups} = getState()
taskGroups.map (taskGroup, taskGroupId) ->
getTasks(taskGroup, tasks).map ({taskId, terminated}) ->
if terminated
Promise.resolve()
else
dispatch(updateTaskStatus(taskGroupId, taskId))

updateTaskStatus = (taskGroupId, taskId) ->
(dispatch, getState) ->
fetchTaskHistory(taskId, ['taskUpdates']).done (data) ->
dispatch(taskHistory(taskGroupId, taskId, data))

taskGroupFetchNext = (taskGroupId) ->
(dispatch, getState) ->
{tasks, taskGroups, logRequestLength, maxLines} = getState()

taskGroup = taskGroups[taskGroupId]
tasks = getTasks(taskGroup, tasks)

# bail early if there's already a pending request
if taskGroup.pendingRequests
return Promise.resolve()

dispatch({taskGroupId, type: 'LOG_REQUEST_START'})
promises = tasks.map ({taskId, maxOffset, path, initialDataLoaded}) ->
if initialDataLoaded
xhr = fetchData(taskId, path, maxOffset, logRequestLength)
xhr.done ({data, offset, nextOffset}) ->
if data.length > 0
nextOffset = offset + data.length
dispatch(taskData(taskGroupId, taskId, data, offset, nextOffset, true, maxLines))
else
Promise.resolve() # reject("initialDataLoaded is false for task #{taskId}")

Promise.all(promises).then -> dispatch({taskGroupId, type: 'LOG_REQUEST_END'})

taskGroupFetchPrevious = (taskGroupId) ->
(dispatch, getState) ->
{tasks, taskGroups, logRequestLength, maxLines} = getState()

taskGroup = taskGroups[taskGroupId]
tasks = getTasks(taskGroup, tasks)

# bail early if all tasks are at the top
if _.all(tasks.map ({minOffset}) -> minOffset is 0)
return Promise.resolve()

# bail early if there's already a pending request
if taskGroup.pendingRequests
return Promise.resolve()

dispatch({taskGroupId, type: 'LOG_REQUEST_START'})
promises = tasks.map ({taskId, minOffset, path, initialDataLoaded}) ->
if minOffset > 0 and initialDataLoaded
xhr = fetchData(taskId, path, Math.max(minOffset - logRequestLength, 0), Math.min(logRequestLength, minOffset))
xhr.done ({data, offset, nextOffset}) ->
if data.length > 0
nextOffset = offset + data.length
dispatch(taskData(taskGroupId, taskId, data, offset, nextOffset, false, maxLines))
else
Promise.resolve() # reject("initialDataLoaded is false for task #{taskId}")

Promise.all(promises).then -> dispatch({taskGroupId, type: 'LOG_REQUEST_END'})

taskData = (taskGroupId, taskId, data, offset, nextOffset, append, maxLines) ->
{
taskGroupId
taskId
data
offset
nextOffset
append
maxLines
type: 'LOG_TASK_DATA'
}

taskFilesize = (taskId, filesize) ->
{
taskId
filesize
type: 'LOG_TASK_FILESIZE'
}

taskGroupTop = (taskGroupId, visible) ->
(dispatch, getState) ->
if getState().taskGroups[taskGroupId].top != visible
dispatch({taskGroupId, visible, type: 'LOG_TASK_GROUP_TOP'})
if visible
dispatch(taskGroupFetchPrevious(taskGroupId))

taskGroupBottom = (taskGroupId, visible, tailing=false) ->
(dispatch, getState) ->
{ taskGroups, tasks } = getState()
taskGroup = taskGroups[taskGroupId]
if taskGroup.tailing != tailing
if tailing is false or _.all(getTasks(taskGroup, tasks).map(({maxOffset, filesize}) -> maxOffset >= filesize))
dispatch({taskGroupId, tailing, type: 'LOG_TASK_GROUP_TAILING'})
if taskGroup.bottom != visible
dispatch({taskGroupId, visible, type: 'LOG_TASK_GROUP_BOTTOM'})
if visible
dispatch(taskGroupFetchNext(taskGroupId))

clickPermalink = (offset) ->
{
offset
type: 'LOG_CLICK_OFFSET_LINK'
}

selectLogColor = (color) ->
{
color
type: 'LOG_SELECT_COLOR'
}

switchViewMode = (newViewMode) ->
(dispatch, getState) ->
{ taskGroups, path, activeRequest, search, viewMode } = getState()

if newViewMode in ['custom', viewMode]
return

taskIds = _.flatten(_.pluck(taskGroups, 'taskIds'))

dispatch({viewMode: newViewMode, type: 'LOG_SWITCH_VIEW_MODE'})
dispatch(initialize(activeRequest.requestId, path, search, taskIds))

setCurrentSearch = (newSearch) -> # TODO: can we do something less heavyweight?
(dispatch, getState) ->
{activeRequest, path, taskGroups, currentSearch} = getState()
if newSearch != currentSearch
dispatch(initialize(activeRequest.requestId, path, newSearch, _.flatten(_.pluck(taskGroups, 'taskIds'))))

toggleTaskLog = (taskId) ->
(dispatch, getState) ->
{search, path, tasks, viewMode} = getState()
if taskId of tasks
# only remove task if it's not the last one
if Object.keys(tasks).length > 1
dispatch({taskId, type: 'LOG_REMOVE_TASK'})
else
return
else
if viewMode is 'split'
dispatch(addTaskGroup([taskId], search))

resolvedPath = path.replace('$TASK_ID', taskId)

fetchData(taskId, resolvedPath).done ({offset}) ->
dispatch(initTask(taskId, offset, resolvedPath, true))

getState().taskGroups.map (taskGroup, taskGroupId) ->
if taskId in taskGroup.taskIds
dispatch(updateTaskStatus(taskGroupId, taskId))
dispatch(taskGroupFetchPrevious(taskGroupId)).then ->
dispatch(taskGroupReady(taskGroupId))

removeTaskGroup = (taskGroupId) ->
(dispatch, getState) ->
{ taskIds } = getState().taskGroups[taskGroupId]
dispatch({taskGroupId, taskIds, type: 'LOG_REMOVE_TASK_GROUP'})

expandTaskGroup = (taskGroupId) ->
(dispatch, getState) ->
{ taskIds } = getState().taskGroups[taskGroupId]
dispatch({taskGroupId, taskIds, type: 'LOG_EXPAND_TASK_GROUP'})

scrollToTop = (taskGroupId) ->
(dispatch, getState) ->
{ taskIds } = getState().taskGroups[taskGroupId]
dispatch({taskGroupId, taskIds, type: 'LOG_SCROLL_TO_TOP'})
dispatch(taskGroupFetchNext(taskGroupId))

scrollAllToTop = () ->
(dispatch, getState) ->
dispatch({type: 'LOG_SCROLL_ALL_TO_TOP'})
getState().taskGroups.map (taskGroup, taskGroupId) ->
dispatch(taskGroupFetchNext(taskGroupId))

scrollToBottom = (taskGroupId) ->
(dispatch, getState) ->
{ taskIds } = getState().taskGroups[taskGroupId]
dispatch({taskGroupId, taskIds, type: 'LOG_SCROLL_TO_BOTTOM'})
dispatch(taskGroupFetchPrevious(taskGroupId))

scrollAllToBottom = () ->
(dispatch, getState) ->
dispatch({type: 'LOG_SCROLL_ALL_TO_BOTTOM'})
getState().taskGroups.map (taskGroup, taskGroupId) ->
dispatch(taskGroupFetchPrevious(taskGroupId))

module.exports = {
initialize
initializeUsingActiveTasks
taskGroupFetchNext
taskGroupFetchPrevious
clickPermalink
updateGroups
updateTaskStatuses
updateFilesizes
taskGroupTop
taskGroupBottom
selectLogColor
switchViewMode
setCurrentSearch
toggleTaskLog
scrollToTop
scrollAllToTop
scrollToBottom
scrollAllToBottom
removeTaskGroup
expandTaskGroup
}
1 change: 1 addition & 0 deletions SingularityUI/app/assets/index.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
redirectOnUnauthorizedUrl: "{{{redirectOnUnauthorizedUrl}}}"
};
</script>
<script src="{{{staticRoot}}}/js/vendor.bundle.js"></script>
<script src="{{{staticRoot}}}/js/app.js"></script>
</head>
{{#navColor}}
Expand Down
Loading