Log file changes

New: Update log files are available in the UI
Fixed: UI error after clearing log files
pull/2/head
Mark McDowall 10 years ago
parent 66873b04d4
commit f5d46ffcd2

@ -13,7 +13,10 @@ namespace NzbDrone.Api.Extensions.Pipelines
private void Handle(NancyContext context)
{
context.Response.Headers.Add("X-ApplicationVersion", BuildInfo.Version.ToString());
if (!context.Response.Headers.ContainsKey("X-ApplicationVersion"))
{
context.Response.Headers.Add("X-ApplicationVersion", BuildInfo.Version.ToString());
}
}
}
}

@ -6,11 +6,11 @@ using NzbDrone.Common.EnvironmentInfo;
namespace NzbDrone.Api.Frontend.Mappers
{
public class LogFileMapper : StaticResourceMapperBase
public class UpdateLogFileMapper : StaticResourceMapperBase
{
private readonly IAppFolderInfo _appFolderInfo;
public LogFileMapper(IAppFolderInfo appFolderInfo, IDiskProvider diskProvider, Logger logger)
public UpdateLogFileMapper(IAppFolderInfo appFolderInfo, IDiskProvider diskProvider, Logger logger)
: base(diskProvider, logger)
{
_appFolderInfo = appFolderInfo;
@ -21,12 +21,12 @@ namespace NzbDrone.Api.Frontend.Mappers
var path = resourceUrl.Replace('/', Path.DirectorySeparatorChar);
path = Path.GetFileName(path);
return Path.Combine(_appFolderInfo.GetLogFolder(), path);
return Path.Combine(_appFolderInfo.GetUpdateLogFolder(), path);
}
public override bool CanHandle(string resourceUrl)
{
return resourceUrl.StartsWith("/logfile/") && resourceUrl.EndsWith(".txt");
return resourceUrl.StartsWith("/updatelogfile/") && resourceUrl.EndsWith(".txt");
}
}
}

@ -0,0 +1,32 @@
using System.IO;
using NLog;
using NzbDrone.Common;
using NzbDrone.Common.Disk;
using NzbDrone.Common.EnvironmentInfo;
namespace NzbDrone.Api.Frontend.Mappers
{
public class LogFileMapper : StaticResourceMapperBase
{
private readonly IAppFolderInfo _appFolderInfo;
public LogFileMapper(IAppFolderInfo appFolderInfo, IDiskProvider diskProvider, Logger logger)
: base(diskProvider, logger)
{
_appFolderInfo = appFolderInfo;
}
protected override string Map(string resourceUrl)
{
var path = resourceUrl.Replace('/', Path.DirectorySeparatorChar);
path = Path.GetFileName(path);
return Path.Combine(_appFolderInfo.GetLogFolder(), path);
}
public override bool CanHandle(string resourceUrl)
{
return resourceUrl.StartsWith("/logfile/") && resourceUrl.EndsWith(".txt");
}
}
}

@ -1,63 +1,43 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using NzbDrone.Common;
using NzbDrone.Common.Disk;
using NzbDrone.Common.EnvironmentInfo;
using Nancy;
using Nancy.Responses;
using NzbDrone.Core.Configuration;
namespace NzbDrone.Api.Logs
{
public class LogFileModule : NzbDroneRestModule<LogFileResource>
public class LogFileModule : LogFileModuleBase
{
private const string LOGFILE_ROUTE = @"/(?<filename>nzbdrone(?:\.\d+)?\.txt)";
private readonly IAppFolderInfo _appFolderInfo;
private readonly IDiskProvider _diskProvider;
public LogFileModule(IAppFolderInfo appFolderInfo,
IDiskProvider diskProvider)
: base("log/file")
IDiskProvider diskProvider,
IConfigFileProvider configFileProvider)
: base(diskProvider, configFileProvider, "")
{
_appFolderInfo = appFolderInfo;
_diskProvider = diskProvider;
GetResourceAll = GetLogFiles;
}
Get[LOGFILE_ROUTE] = options => GetLogFile(options.filename);
protected override IEnumerable<string> GetLogFiles()
{
return _diskProvider.GetFiles(_appFolderInfo.GetLogFolder(), SearchOption.TopDirectoryOnly);
}
private List<LogFileResource> GetLogFiles()
protected override string GetLogFilePath(string filename)
{
var result = new List<LogFileResource>();
var files = _diskProvider.GetFiles(_appFolderInfo.GetLogFolder(), SearchOption.TopDirectoryOnly);
return Path.Combine(_appFolderInfo.GetLogFolder(), filename);
}
for (int i = 0; i < files.Length; i++)
protected override string DownloadUrlRoot
{
get
{
var file = files[i];
result.Add(new LogFileResource
{
Id = i + 1,
Filename = Path.GetFileName(file),
LastWriteTime = _diskProvider.FileGetLastWriteUtc(file)
});
return "logfile";
}
return result.OrderByDescending(l => l.LastWriteTime).ToList();
}
private Response GetLogFile(string filename)
{
var filePath = Path.Combine(_appFolderInfo.GetLogFolder(), filename);
if (!_diskProvider.FileExists(filePath))
return new NotFoundResponse();
var data = _diskProvider.ReadAllText(filePath);
return new TextResponse(data);
}
}
}

@ -0,0 +1,72 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using NzbDrone.Common.Disk;
using Nancy;
using Nancy.Responses;
using NzbDrone.Core.Configuration;
namespace NzbDrone.Api.Logs
{
public abstract class LogFileModuleBase : NzbDroneRestModule<LogFileResource>
{
protected const string LOGFILE_ROUTE = @"/(?<filename>[-.a-zA-Z0-9]+?\.txt)";
private readonly IDiskProvider _diskProvider;
private readonly IConfigFileProvider _configFileProvider;
public LogFileModuleBase(IDiskProvider diskProvider,
IConfigFileProvider configFileProvider,
String route)
: base("log/file" + route)
{
_diskProvider = diskProvider;
_configFileProvider = configFileProvider;
GetResourceAll = GetLogFilesResponse;
Get[LOGFILE_ROUTE] = options => GetLogFileResponse(options.filename);
}
private List<LogFileResource> GetLogFilesResponse()
{
var result = new List<LogFileResource>();
var files = GetLogFiles().ToList();
for (int i = 0; i < files.Count; i++)
{
var file = files[i];
var filename = Path.GetFileName(file);
result.Add(new LogFileResource
{
Id = i + 1,
Filename = filename,
LastWriteTime = _diskProvider.FileGetLastWriteUtc(file),
ContentsUrl = String.Format("/api{0}/{1}/{2}", _configFileProvider.UrlBase, Resource, filename),
DownloadUrl = String.Format("{0}/{1}/{2}", _configFileProvider.UrlBase, DownloadUrlRoot, filename)
});
}
return result.OrderByDescending(l => l.LastWriteTime).ToList();
}
private Response GetLogFileResponse(String filename)
{
var filePath = GetLogFilePath(filename);
if (!_diskProvider.FileExists(filePath))
return new NotFoundResponse();
var data = _diskProvider.ReadAllText(filePath);
return new TextResponse(data);
}
protected abstract IEnumerable<String> GetLogFiles();
protected abstract String GetLogFilePath(String filename);
protected abstract String DownloadUrlRoot { get; }
}
}

@ -7,5 +7,7 @@ namespace NzbDrone.Api.Logs
{
public String Filename { get; set; }
public DateTime LastWriteTime { get; set; }
public String ContentsUrl { get; set; }
public String DownloadUrl { get; set; }
}
}

@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using NzbDrone.Common;
using NzbDrone.Common.Disk;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Core.Configuration;
namespace NzbDrone.Api.Logs
{
public class UpdateLogFileModule : LogFileModuleBase
{
private readonly IAppFolderInfo _appFolderInfo;
private readonly IDiskProvider _diskProvider;
public UpdateLogFileModule(IAppFolderInfo appFolderInfo,
IDiskProvider diskProvider,
IConfigFileProvider configFileProvider)
: base(diskProvider, configFileProvider, "/update")
{
_appFolderInfo = appFolderInfo;
_diskProvider = diskProvider;
}
protected override IEnumerable<String> GetLogFiles()
{
return _diskProvider.GetFiles(_appFolderInfo.GetUpdateLogFolder(), SearchOption.TopDirectoryOnly)
.Where(f => Regex.IsMatch(Path.GetFileName(f), LOGFILE_ROUTE.TrimStart('/'), RegexOptions.IgnoreCase))
.ToList();
}
protected override String GetLogFilePath(String filename)
{
return Path.Combine(_appFolderInfo.GetUpdateLogFolder(), filename);
}
protected override String DownloadUrlRoot
{
get
{
return "updatelogfile";
}
}
}
}

@ -129,6 +129,7 @@
<Compile Include="Extensions\NancyJsonSerializer.cs" />
<Compile Include="Extensions\RequestExtensions.cs" />
<Compile Include="Frontend\IsCacheableSpecification.cs" />
<Compile Include="Frontend\Mappers\UpdateLogFileMapper.cs" />
<Compile Include="Frontend\Mappers\FaviconMapper.cs" />
<Compile Include="Frontend\Mappers\IndexHtmlMapper.cs" />
<Compile Include="Frontend\Mappers\LogFileMapper.cs" />
@ -141,6 +142,8 @@
<Compile Include="Health\HealthModule.cs" />
<Compile Include="History\HistoryResource.cs" />
<Compile Include="History\HistoryModule.cs" />
<Compile Include="Logs\LogFileModuleBase.cs" />
<Compile Include="Logs\UpdateLogFileModule.cs" />
<Compile Include="MediaCovers\MediaCoverModule.cs" />
<Compile Include="Metadata\MetadataResource.cs" />
<Compile Include="Metadata\MetadataModule.cs" />

@ -0,0 +1,15 @@
using NzbDrone.Core.Messaging.Commands;
namespace NzbDrone.Core.Instrumentation.Commands
{
public class DeleteUpdateLogFilesCommand : Command
{
public override bool SendUpdatesToClient
{
get
{
return true;
}
}
}
}

@ -1,4 +1,5 @@
using System.IO;
using System;
using System.IO;
using NLog;
using NzbDrone.Common;
using NzbDrone.Common.Disk;
@ -12,7 +13,7 @@ namespace NzbDrone.Core.Instrumentation
{
}
public class DeleteLogFilesService : IDeleteLogFilesService, IExecute<DeleteLogFilesCommand>
public class DeleteLogFilesService : IDeleteLogFilesService, IExecute<DeleteLogFilesCommand>, IExecute<DeleteUpdateLogFilesCommand>
{
private readonly IDiskProvider _diskProvider;
private readonly IAppFolderInfo _appFolderInfo;
@ -27,12 +28,14 @@ namespace NzbDrone.Core.Instrumentation
public void Execute(DeleteLogFilesCommand message)
{
var logFiles = _diskProvider.GetFiles(_appFolderInfo.GetLogFolder(), SearchOption.TopDirectoryOnly);
_logger.Debug("Deleting all files in: {0}", _appFolderInfo.GetLogFolder());
_diskProvider.EmptyFolder(_appFolderInfo.GetLogFolder());
}
foreach (var logFile in logFiles)
{
_diskProvider.DeleteFile(logFile);
}
public void Execute(DeleteUpdateLogFilesCommand message)
{
_logger.Debug("Deleting all files in: {0}", _appFolderInfo.GetUpdateLogFolder());
_diskProvider.EmptyFolder(_appFolderInfo.GetUpdateLogFolder());
}
}
}

@ -334,6 +334,7 @@
<Compile Include="Indexers\RssSyncCommand.cs" />
<Compile Include="Indexers\XElementExtensions.cs" />
<Compile Include="Instrumentation\Commands\ClearLogCommand.cs" />
<Compile Include="Instrumentation\Commands\DeleteUpdateLogFilesCommand.cs" />
<Compile Include="Instrumentation\Commands\DeleteLogFilesCommand.cs" />
<Compile Include="Instrumentation\Commands\TrimLogCommand.cs" />
<Compile Include="Instrumentation\DeleteLogFilesService.cs" />

@ -1,12 +1,11 @@
'use strict';
define(
[
'backbone',
'System/StatusModel'
], function (Backbone, StatusModel) {
'backbone'
], function (Backbone) {
return Backbone.Model.extend({
url: function () {
return StatusModel.get('urlBase') + '/api/log/file/' + this.get('filename');
return this.get('contentsUrl');
},
parse: function (contents) {

@ -1,16 +1,15 @@
'use strict';
define(
[
'Cells/NzbDroneCell',
'System/StatusModel'
], function (NzbDroneCell, StatusModel) {
'../../../Cells/NzbDroneCell'
], function (NzbDroneCell) {
return NzbDroneCell.extend({
className: 'download-log-cell',
render: function () {
this.$el.empty();
this.$el.html('<a href="{0}/logfile/{1}" class="no-router" target="_blank">Download</a>'.format(StatusModel.get('urlBase'), this.cellValue));
this.$el.html('<a href="{0}" class="no-router" target="_blank">Download</a>'.format(this.cellValue));
return this;
}

@ -1,7 +1,7 @@
'use strict';
define(
[
'Cells/NzbDroneCell'
'../../../Cells/NzbDroneCell'
], function (NzbDroneCell) {
return NzbDroneCell.extend({

@ -7,7 +7,6 @@ define(
'System/Logs/Files/FilenameCell',
'Cells/RelativeDateCell',
'System/Logs/Files/DownloadLogCell',
'System/Logs/Files/LogFileCollection',
'System/Logs/Files/Row',
'System/Logs/Files/ContentsView',
'System/Logs/Files/ContentsModel',
@ -20,7 +19,6 @@ define(
FilenameCell,
RelativeDateCell,
DownloadLogCell,
LogFileCollection,
LogFileRow,
ContentsView,
ContentsModel,
@ -48,18 +46,19 @@ define(
cell : RelativeDateCell
},
{
name : 'filename',
name : 'downloadUrl',
label : '',
cell : DownloadLogCell,
sortable: false
}
],
initialize: function () {
this.collection = new LogFileCollection();
initialize: function (options) {
this.collection = options.collection;
this.deleteFilesCommand = options.deleteFilesCommand;
vent.on(vent.Commands.ShowLogFile, this._fetchLogFileContents, this);
vent.on(vent.Events.CommandComplete, this._commandComplete, this);
this.listenTo(vent, vent.Commands.ShowLogFile, this._fetchLogFileContents);
this.listenTo(vent, vent.Events.CommandComplete, this._commandComplete);
this.listenTo(this.collection, 'sync', this._collectionSynced);
this.collection.fetch();
@ -83,11 +82,10 @@ define(
ownerContext : this,
callback : this._refreshTable
},
{
title : 'Delete Log Files',
icon : 'icon-trash',
command : 'deleteLogFiles',
command : this.deleteFilesCommand,
successMessage : 'Log files have been deleted',
errorMessage : 'Failed to delete log files'
}
@ -125,11 +123,7 @@ define(
this.contents.show(new LoadingView());
var model = options.model;
var filename = model.get('filename');
var contentsModel = new ContentsModel({
filename: filename
});
var contentsModel = new ContentsModel(model.toJSON());
this.listenToOnce(contentsModel, 'sync', this._showDetails);
@ -151,7 +145,7 @@ define(
},
_commandComplete: function (options) {
if (options.command.get('name') === 'deletelogfiles') {
if (options.command.get('name') === this.deleteFilesCommand.toLowerCase()) {
this._refreshTable();
}
}

@ -1,7 +1,7 @@
'use strict';
define(
[
'vent',
'../../../vent',
'backgrid'
], function (vent, Backgrid) {

@ -3,24 +3,29 @@ define(
[
'marionette',
'System/Logs/Table/LogsTableLayout',
'System/Logs/Files/LogFileLayout'
], function (Marionette, LogsTableLayout, LogsFileLayout) {
'System/Logs/Files/LogFileLayout',
'System/Logs/Files/LogFileCollection',
'System/Logs/Updates/LogFileCollection'
], function (Marionette, LogsTableLayout, LogsFileLayout, LogFileCollection, UpdateLogFileCollection) {
return Marionette.Layout.extend({
template: 'System/Logs/LogsLayoutTemplate',
ui: {
tableTab: '.x-table-tab',
filesTab: '.x-files-tab'
tableTab : '.x-table-tab',
filesTab : '.x-files-tab',
updateFilesTab : '.x-update-files-tab'
},
regions: {
table: '#table',
files: '#files'
table : '#table',
files : '#files',
updateFiles : '#update-files'
},
events: {
'click .x-table-tab': '_showTable',
'click .x-files-tab': '_showFiles'
'click .x-table-tab' : '_showTable',
'click .x-files-tab' : '_showFiles',
'click .x-update-files-tab' : '_showUpdateFiles'
},
onShow: function () {
@ -42,7 +47,22 @@ define(
}
this.ui.filesTab.tab('show');
this.files.show(new LogsFileLayout());
this.files.show(new LogsFileLayout({
collection: new LogFileCollection(),
deleteFilesCommand: 'deleteLogFiles'
}));
},
_showUpdateFiles: function (e) {
if (e) {
e.preventDefault();
}
this.ui.updateFilesTab.tab('show');
this.updateFiles.show(new LogsFileLayout({
collection: new UpdateLogFileCollection(),
deleteFilesCommand: 'deleteUpdateLogFiles'
}));
}
});
});

@ -1,15 +1,17 @@
<div class="row">
<div class="col-md-1 col-sm-2">
<div class="col-md-2 col-sm-2">
<ul class="nav nav-pills nav-stacked">
<li><a href="#table" class="x-table-tab no-router">Table</a></li>
<li><a href="#files" class="x-files-tab no-router">Files</a></li>
<li><a href="#update-files" class="x-update-files-tab no-router">Updates</a></li>
</ul>
</div>
<div class="col-md-11 col-sm-10">
<div class="col-md-10 col-sm-10">
<div class="tab-content">
<div class="tab-pane" id="table"></div>
<div class="tab-pane" id="files"></div>
<div class="tab-pane" id="update-files"></div>
</div>
</div>
</div>

@ -0,0 +1,17 @@
'use strict';
define(
[
'backbone',
'System/Logs/Updates/LogFileModel'
], function (Backbone, LogFileModel) {
return Backbone.Collection.extend({
url : window.NzbDrone.ApiRoot + '/log/file/update',
model: LogFileModel,
state: {
sortKey: 'lastWriteTime',
order : 1
}
});
});

@ -0,0 +1,8 @@
'use strict';
define(
[
'backbone'
], function (Backbone) {
return Backbone.Model.extend({
});
});
Loading…
Cancel
Save