mirror of
https://github.com/Radarr/Radarr.git
synced 2024-09-17 15:02:34 +02:00
Log files are viewable in the UI
This commit is contained in:
parent
a9ac2500d5
commit
4c536a077f
29
NzbDrone.Api/Frontend/LogFileMapper.cs
Normal file
29
NzbDrone.Api/Frontend/LogFileMapper.cs
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
using System.IO;
|
||||||
|
using NzbDrone.Common;
|
||||||
|
using NzbDrone.Common.EnvironmentInfo;
|
||||||
|
|
||||||
|
namespace NzbDrone.Api.Frontend
|
||||||
|
{
|
||||||
|
public class LogFileMapper : IMapHttpRequestsToDisk
|
||||||
|
{
|
||||||
|
private readonly IAppFolderInfo _appFolderInfo;
|
||||||
|
|
||||||
|
public LogFileMapper(IAppFolderInfo appFolderInfo)
|
||||||
|
{
|
||||||
|
_appFolderInfo = appFolderInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Map(string resourceUrl)
|
||||||
|
{
|
||||||
|
var path = resourceUrl.Replace('/', Path.DirectorySeparatorChar);
|
||||||
|
path = Path.GetFileName(path);
|
||||||
|
|
||||||
|
return Path.Combine(_appFolderInfo.GetLogFolder(), path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool CanHandle(string resourceUrl)
|
||||||
|
{
|
||||||
|
return resourceUrl.StartsWith("/log");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
45
NzbDrone.Api/Logs/LogFileModule.cs
Normal file
45
NzbDrone.Api/Logs/LogFileModule.cs
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using NzbDrone.Common;
|
||||||
|
using NzbDrone.Common.EnvironmentInfo;
|
||||||
|
|
||||||
|
namespace NzbDrone.Api.Logs
|
||||||
|
{
|
||||||
|
public class LogFileModule : NzbDroneRestModule<LogFileResource>
|
||||||
|
{
|
||||||
|
private readonly IAppFolderInfo _appFolderInfo;
|
||||||
|
private readonly IDiskProvider _diskProvider;
|
||||||
|
|
||||||
|
public LogFileModule(IAppFolderInfo appFolderInfo,
|
||||||
|
IDiskProvider diskProvider)
|
||||||
|
: base("log/files")
|
||||||
|
{
|
||||||
|
_appFolderInfo = appFolderInfo;
|
||||||
|
_diskProvider = diskProvider;
|
||||||
|
GetResourceAll = GetLogFiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<LogFileResource> GetLogFiles()
|
||||||
|
{
|
||||||
|
var result = new List<LogFileResource>();
|
||||||
|
|
||||||
|
var files = _diskProvider.GetFiles(_appFolderInfo.GetLogFolder(), SearchOption.TopDirectoryOnly);
|
||||||
|
|
||||||
|
for (int i = 0; i < files.Length; i++)
|
||||||
|
{
|
||||||
|
var file = files[i];
|
||||||
|
|
||||||
|
result.Add(new LogFileResource
|
||||||
|
{
|
||||||
|
Id = i + 1,
|
||||||
|
Filename = Path.GetFileName(file),
|
||||||
|
LastWriteTime = _diskProvider.GetLastFileWrite(file)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.OrderByDescending(l => l.LastWriteTime).ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
NzbDrone.Api/Logs/LogFileResource.cs
Normal file
11
NzbDrone.Api/Logs/LogFileResource.cs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
using System;
|
||||||
|
using NzbDrone.Api.REST;
|
||||||
|
|
||||||
|
namespace NzbDrone.Api.Logs
|
||||||
|
{
|
||||||
|
public class LogFileResource : RestResource
|
||||||
|
{
|
||||||
|
public String Filename { get; set; }
|
||||||
|
public DateTime LastWriteTime { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,4 @@
|
|||||||
using NzbDrone.Core.Datastore;
|
using NzbDrone.Core.Datastore;
|
||||||
using NzbDrone.Core.History;
|
|
||||||
using NzbDrone.Core.Instrumentation;
|
using NzbDrone.Core.Instrumentation;
|
||||||
using NzbDrone.Api.Mapping;
|
using NzbDrone.Api.Mapping;
|
||||||
|
|
||||||
@ -8,7 +7,6 @@ namespace NzbDrone.Api.Logs
|
|||||||
public class LogModule : NzbDroneRestModule<LogResource>
|
public class LogModule : NzbDroneRestModule<LogResource>
|
||||||
{
|
{
|
||||||
private readonly ILogService _logService;
|
private readonly ILogService _logService;
|
||||||
private readonly IHistoryService _historyService;
|
|
||||||
|
|
||||||
public LogModule(ILogService logService)
|
public LogModule(ILogService logService)
|
||||||
{
|
{
|
||||||
|
@ -92,6 +92,7 @@
|
|||||||
<Compile Include="Extensions\CacheHeaderPipeline.cs" />
|
<Compile Include="Extensions\CacheHeaderPipeline.cs" />
|
||||||
<Compile Include="Extensions\GZipPipeline.cs" />
|
<Compile Include="Extensions\GZipPipeline.cs" />
|
||||||
<Compile Include="Extensions\NancyJsonSerializer.cs" />
|
<Compile Include="Extensions\NancyJsonSerializer.cs" />
|
||||||
|
<Compile Include="Frontend\LogFileMapper.cs" />
|
||||||
<Compile Include="Frontend\IAddCacheHeaders.cs" />
|
<Compile Include="Frontend\IAddCacheHeaders.cs" />
|
||||||
<Compile Include="Frontend\MediaCoverMapper.cs" />
|
<Compile Include="Frontend\MediaCoverMapper.cs" />
|
||||||
<Compile Include="Frontend\IMapHttpRequestsToDisk.cs" />
|
<Compile Include="Frontend\IMapHttpRequestsToDisk.cs" />
|
||||||
@ -106,6 +107,8 @@
|
|||||||
<Compile Include="Indexers\ReleaseModule.cs" />
|
<Compile Include="Indexers\ReleaseModule.cs" />
|
||||||
<Compile Include="Extensions\LazyExtensions.cs" />
|
<Compile Include="Extensions\LazyExtensions.cs" />
|
||||||
<Compile Include="Indexers\ReleaseResource.cs" />
|
<Compile Include="Indexers\ReleaseResource.cs" />
|
||||||
|
<Compile Include="Logs\LogFileResource.cs" />
|
||||||
|
<Compile Include="Logs\LogFileModule.cs" />
|
||||||
<Compile Include="Logs\LogModule.cs" />
|
<Compile Include="Logs\LogModule.cs" />
|
||||||
<Compile Include="Logs\LogResource.cs" />
|
<Compile Include="Logs\LogResource.cs" />
|
||||||
<Compile Include="Mapping\CloneInjection.cs" />
|
<Compile Include="Mapping\CloneInjection.cs" />
|
||||||
|
@ -10,11 +10,7 @@ define(
|
|||||||
className: 'episode-title-cell',
|
className: 'episode-title-cell',
|
||||||
|
|
||||||
events: {
|
events: {
|
||||||
'click': 'showDetails'
|
'click': '_showDetails'
|
||||||
},
|
|
||||||
|
|
||||||
showDetails: function () {
|
|
||||||
App.vent.trigger(App.Commands.ShowEpisodeDetails, {episode: this.cellValue});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
render: function () {
|
render: function () {
|
||||||
@ -26,6 +22,10 @@ define(
|
|||||||
|
|
||||||
this.$el.html(title);
|
this.$el.html(title);
|
||||||
return this;
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
_showDetails: function () {
|
||||||
|
App.vent.trigger(App.Commands.ShowEpisodeDetails, {episode: this.cellValue});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -12,12 +12,13 @@ define(
|
|||||||
'Series/SeriesModel',
|
'Series/SeriesModel',
|
||||||
'Calendar/CalendarLayout',
|
'Calendar/CalendarLayout',
|
||||||
'Logs/Layout',
|
'Logs/Layout',
|
||||||
|
'Logs/Files/Layout',
|
||||||
'Release/Layout',
|
'Release/Layout',
|
||||||
'System/Layout',
|
'System/Layout',
|
||||||
'Shared/NotFoundView',
|
'Shared/NotFoundView',
|
||||||
'Shared/Modal/Region'
|
'Shared/Modal/Region'
|
||||||
], function (App, Marionette, HistoryLayout, SettingsLayout, AddSeriesLayout, SeriesIndexLayout, SeriesDetailsLayout, MissingLayout, SeriesModel, CalendarLayout,
|
], function (App, Marionette, HistoryLayout, SettingsLayout, AddSeriesLayout, SeriesIndexLayout, SeriesDetailsLayout, MissingLayout, SeriesModel, CalendarLayout,
|
||||||
LogsLayout, ReleaseLayout, SystemLayout, NotFoundView) {
|
LogsLayout, LogFileLayout, ReleaseLayout, SystemLayout, NotFoundView) {
|
||||||
return Marionette.Controller.extend({
|
return Marionette.Controller.extend({
|
||||||
|
|
||||||
series : function () {
|
series : function () {
|
||||||
@ -71,9 +72,16 @@ define(
|
|||||||
App.mainRegion.show(new ReleaseLayout());
|
App.mainRegion.show(new ReleaseLayout());
|
||||||
},
|
},
|
||||||
|
|
||||||
logs: function () {
|
logs: function (action) {
|
||||||
this._setTitle('logs');
|
if (action) {
|
||||||
App.mainRegion.show(new LogsLayout());
|
this._setTitle('log files');
|
||||||
|
App.mainRegion.show(new LogFileLayout());
|
||||||
|
}
|
||||||
|
|
||||||
|
else {
|
||||||
|
this._setTitle('logs');
|
||||||
|
App.mainRegion.show(new LogsLayout());
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
system: function () {
|
system: function () {
|
||||||
|
13
UI/Logs/Files/Collection.js
Normal file
13
UI/Logs/Files/Collection.js
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
'use strict';
|
||||||
|
define(['Logs/Files/Model' ],
|
||||||
|
function (LogFileModel) {
|
||||||
|
return Backbone.Collection.extend({
|
||||||
|
url : window.ApiRoot + '/log/files',
|
||||||
|
model: LogFileModel,
|
||||||
|
|
||||||
|
state: {
|
||||||
|
sortKey : 'lastWriteTime',
|
||||||
|
order : 1
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
8
UI/Logs/Files/ContentsModel.js
Normal file
8
UI/Logs/Files/ContentsModel.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
'use strict';
|
||||||
|
define(
|
||||||
|
[
|
||||||
|
'backbone'
|
||||||
|
], function (Backbone) {
|
||||||
|
return Backbone.Model.extend({
|
||||||
|
});
|
||||||
|
});
|
11
UI/Logs/Files/ContentsView.js
Normal file
11
UI/Logs/Files/ContentsView.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
define(
|
||||||
|
[
|
||||||
|
'app',
|
||||||
|
'marionette'
|
||||||
|
], function (App, Marionette) {
|
||||||
|
return Marionette.ItemView.extend({
|
||||||
|
template: 'Logs/Files/ContentsViewTemplate'
|
||||||
|
});
|
||||||
|
});
|
11
UI/Logs/Files/ContentsViewTemplate.html
Normal file
11
UI/Logs/Files/ContentsViewTemplate.html
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<div class="row">
|
||||||
|
<div class="span12">
|
||||||
|
<h3>{{filename}}</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="span12">
|
||||||
|
<pre>{{contents}}</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
18
UI/Logs/Files/FilenameCell.js
Normal file
18
UI/Logs/Files/FilenameCell.js
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
'use strict';
|
||||||
|
define(
|
||||||
|
[
|
||||||
|
'Cells/NzbDroneCell'
|
||||||
|
], function (NzbDroneCell) {
|
||||||
|
return NzbDroneCell.extend({
|
||||||
|
|
||||||
|
className: 'log-filename-cell',
|
||||||
|
|
||||||
|
render: function () {
|
||||||
|
|
||||||
|
var filename = this._getValue();
|
||||||
|
this.$el.html(filename);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
79
UI/Logs/Files/Layout.js
Normal file
79
UI/Logs/Files/Layout.js
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
'use strict';
|
||||||
|
define(
|
||||||
|
[
|
||||||
|
'app',
|
||||||
|
'marionette',
|
||||||
|
'backgrid',
|
||||||
|
'Logs/Files/FilenameCell',
|
||||||
|
'Cells/RelativeDateCell',
|
||||||
|
'Logs/Files/Collection',
|
||||||
|
'Logs/Files/Row',
|
||||||
|
'Logs/Files/ContentsView',
|
||||||
|
'Logs/Files/ContentsModel'
|
||||||
|
], function (App, Marionette, Backgrid, FilenameCell, RelativeDateCell, LogFileCollection, LogFileRow, ContentsView, ContentsModel) {
|
||||||
|
return Marionette.Layout.extend({
|
||||||
|
template: 'Logs/Files/LayoutTemplate',
|
||||||
|
|
||||||
|
regions: {
|
||||||
|
grid : '#x-grid',
|
||||||
|
contents : '#x-contents'
|
||||||
|
},
|
||||||
|
|
||||||
|
columns:
|
||||||
|
[
|
||||||
|
{
|
||||||
|
name : 'filename',
|
||||||
|
label: 'Filename',
|
||||||
|
cell : FilenameCell
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name : 'lastWriteTime',
|
||||||
|
label: 'Last Write Time',
|
||||||
|
cell : RelativeDateCell
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
initialize: function () {
|
||||||
|
this.collection = new LogFileCollection();
|
||||||
|
this.collectionPromise = this.collection.fetch();
|
||||||
|
|
||||||
|
App.vent.on(App.Commands.ShowLogFile, this._showLogFile, this);
|
||||||
|
},
|
||||||
|
|
||||||
|
onShow: function () {
|
||||||
|
var self = this;
|
||||||
|
this._showTable();
|
||||||
|
|
||||||
|
this.collectionPromise.done(function (){
|
||||||
|
self._showLogFile({ model: _.first(self.collection.models) });
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
_showTable: function () {
|
||||||
|
|
||||||
|
this.grid.show(new Backgrid.Grid({
|
||||||
|
row : LogFileRow,
|
||||||
|
columns : this.columns,
|
||||||
|
collection: this.collection,
|
||||||
|
className : 'table table-hover'
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
_showLogFile: function (options) {
|
||||||
|
var self = this;
|
||||||
|
var filename = options.model.get('filename');
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: '/log/' + filename,
|
||||||
|
success: function (data) {
|
||||||
|
var model = new ContentsModel({
|
||||||
|
filename: filename,
|
||||||
|
contents: data
|
||||||
|
});
|
||||||
|
|
||||||
|
self.contents.show(new ContentsView({ model: model }));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
11
UI/Logs/Files/LayoutTemplate.html
Normal file
11
UI/Logs/Files/LayoutTemplate.html
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<div class="row">
|
||||||
|
<div class="span12">
|
||||||
|
<div id="x-grid"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="span12">
|
||||||
|
<div id="x-contents"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
8
UI/Logs/Files/Model.js
Normal file
8
UI/Logs/Files/Model.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
'use strict';
|
||||||
|
define(
|
||||||
|
[
|
||||||
|
'backbone'
|
||||||
|
], function (Backbone) {
|
||||||
|
return Backbone.Model.extend({
|
||||||
|
});
|
||||||
|
});
|
19
UI/Logs/Files/Row.js
Normal file
19
UI/Logs/Files/Row.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
'use strict';
|
||||||
|
define(
|
||||||
|
[
|
||||||
|
'app',
|
||||||
|
'backgrid'
|
||||||
|
], function (App, Backgrid) {
|
||||||
|
|
||||||
|
return Backgrid.Row.extend({
|
||||||
|
className: 'log-file-row',
|
||||||
|
|
||||||
|
events: {
|
||||||
|
'click': '_showContents'
|
||||||
|
},
|
||||||
|
|
||||||
|
_showContents: function () {
|
||||||
|
App.vent.trigger(App.Commands.ShowLogFile, { model: this.model });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
@ -6,8 +6,9 @@ define(
|
|||||||
'Logs/LogTimeCell',
|
'Logs/LogTimeCell',
|
||||||
'Logs/LogLevelCell',
|
'Logs/LogLevelCell',
|
||||||
'Shared/Grid/Pager',
|
'Shared/Grid/Pager',
|
||||||
'Logs/Collection'
|
'Logs/Collection',
|
||||||
], function (Marionette, Backgrid, LogTimeCell, LogLevelCell, GridPager, LogCollection) {
|
'Shared/Toolbar/ToolbarLayout'
|
||||||
|
], function (Marionette, Backgrid, LogTimeCell, LogLevelCell, GridPager, LogCollection, ToolbarLayout) {
|
||||||
return Marionette.Layout.extend({
|
return Marionette.Layout.extend({
|
||||||
template: 'Logs/LayoutTemplate',
|
template: 'Logs/LayoutTemplate',
|
||||||
|
|
||||||
@ -52,7 +53,30 @@ define(
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
showTable: function () {
|
leftSideButtons: {
|
||||||
|
type : 'default',
|
||||||
|
storeState: false,
|
||||||
|
items :
|
||||||
|
[
|
||||||
|
{
|
||||||
|
title: 'Files',
|
||||||
|
icon : 'icon-file',
|
||||||
|
route: 'logs/files'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
initialize: function () {
|
||||||
|
this.collection = new LogCollection();
|
||||||
|
this.collection.fetch();
|
||||||
|
},
|
||||||
|
|
||||||
|
onShow: function () {
|
||||||
|
this._showToolbar();
|
||||||
|
this._showTable();
|
||||||
|
},
|
||||||
|
|
||||||
|
_showTable: function () {
|
||||||
|
|
||||||
this.grid.show(new Backgrid.Grid({
|
this.grid.show(new Backgrid.Grid({
|
||||||
row : Backgrid.Row,
|
row : Backgrid.Row,
|
||||||
@ -67,14 +91,14 @@ define(
|
|||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
initialize: function () {
|
_showToolbar: function () {
|
||||||
this.collection = new LogCollection();
|
this.toolbar.show(new ToolbarLayout({
|
||||||
this.collection.fetch();
|
left :
|
||||||
},
|
[
|
||||||
|
this.leftSideButtons
|
||||||
onShow: function () {
|
],
|
||||||
this.showTable();
|
context: this
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
@import "../Content/FontAwesome/font-awesome";
|
@import "../Content/FontAwesome/font-awesome";
|
||||||
|
@import "../Shared/Styles/clickable";
|
||||||
|
|
||||||
#logs-screen {
|
#logs-screen {
|
||||||
|
|
||||||
@ -45,6 +46,8 @@
|
|||||||
.icon(@remove-sign);
|
.icon(@remove-sign);
|
||||||
color : purple;
|
color : purple;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.log-file-row {
|
||||||
|
.clickable;
|
||||||
|
}
|
@ -1,16 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
define(
|
|
||||||
[
|
|
||||||
'backgrid'
|
|
||||||
], function (Backgrid) {
|
|
||||||
|
|
||||||
return Backgrid.Row.extend({
|
|
||||||
events: {
|
|
||||||
'click .x-search': 'search'
|
|
||||||
},
|
|
||||||
search: function () {
|
|
||||||
window.alert('Episode Search');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
@ -22,6 +22,7 @@ require(
|
|||||||
'missing' : 'missing',
|
'missing' : 'missing',
|
||||||
'history' : 'history',
|
'history' : 'history',
|
||||||
'logs' : 'logs',
|
'logs' : 'logs',
|
||||||
|
'logs/:action' : 'logs',
|
||||||
'rss' : 'rss',
|
'rss' : 'rss',
|
||||||
'system' : 'system',
|
'system' : 'system',
|
||||||
':whatever' : 'notFound'
|
':whatever' : 'notFound'
|
||||||
|
@ -179,7 +179,8 @@ define(
|
|||||||
DeleteSeriesCommand: 'DeleteSeriesCommand',
|
DeleteSeriesCommand: 'DeleteSeriesCommand',
|
||||||
CloseModalCommand : 'CloseModalCommand',
|
CloseModalCommand : 'CloseModalCommand',
|
||||||
ShowEpisodeDetails : 'ShowEpisodeDetails',
|
ShowEpisodeDetails : 'ShowEpisodeDetails',
|
||||||
SaveSettings : 'saveSettings'
|
SaveSettings : 'saveSettings',
|
||||||
|
ShowLogFile : 'showLogFile'
|
||||||
};
|
};
|
||||||
|
|
||||||
app.addInitializer(function () {
|
app.addInitializer(function () {
|
||||||
|
Loading…
Reference in New Issue
Block a user