Implemented live monitor of the logs
This commit is contained in:
@@ -116,6 +116,7 @@
|
||||
<Compile Include="WebServer\ControlMethods\AddOrUpdateBackup.cs" />
|
||||
<Compile Include="WebServer\ControlMethods\GetBackup.cs" />
|
||||
<Compile Include="UpdatePollThread.cs" />
|
||||
<Compile Include="LogWriteHandler.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="app.config" />
|
||||
|
||||
+18
-11
@@ -108,6 +108,11 @@ namespace Duplicati.Server
|
||||
/// </summary>
|
||||
public static long LastDataUpdateID = 0;
|
||||
|
||||
/// <summary>
|
||||
/// The log redirect handler
|
||||
/// </summary>
|
||||
public static LogWriteHandler LogHandler = new LogWriteHandler();
|
||||
|
||||
public static int ServerPort
|
||||
{
|
||||
get
|
||||
@@ -218,11 +223,6 @@ namespace Duplicati.Server
|
||||
}
|
||||
#endif
|
||||
|
||||
if (commandlineOptions.ContainsKey("log-level"))
|
||||
foreach(string s in Enum.GetNames(typeof(Duplicati.Library.Logging.LogMessageType)))
|
||||
if (s.Equals(commandlineOptions["log-level"].Trim(), StringComparison.InvariantCultureIgnoreCase))
|
||||
Duplicati.Library.Logging.Log.LogLevel = (Duplicati.Library.Logging.LogMessageType)Enum.Parse(typeof(Duplicati.Library.Logging.LogMessageType), s);
|
||||
|
||||
//Set the %DUPLICATI_HOME% env variable, if it is not already set
|
||||
if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable(DATAFOLDER_ENV_NAME)))
|
||||
{
|
||||
@@ -278,11 +278,20 @@ namespace Duplicati.Server
|
||||
}
|
||||
}
|
||||
|
||||
// Setup the log redirect
|
||||
Duplicati.Library.Logging.Log.CurrentLog = Program.LogHandler;
|
||||
|
||||
if (commandlineOptions.ContainsKey("log-file"))
|
||||
{
|
||||
if (System.IO.File.Exists(commandlineOptions["log-file"]))
|
||||
System.IO.File.Delete(commandlineOptions["log-file"]);
|
||||
Duplicati.Library.Logging.Log.CurrentLog = new Duplicati.Library.Logging.StreamLog(commandlineOptions["log-file"]);
|
||||
|
||||
var loglevel = Duplicati.Library.Logging.LogMessageType.Error;
|
||||
|
||||
if (commandlineOptions.ContainsKey("log-level"))
|
||||
Enum.TryParse<Duplicati.Library.Logging.LogMessageType>(commandlineOptions["log-level"], true, out loglevel);
|
||||
|
||||
Program.LogHandler.SetServerFile(commandlineOptions["log-file"], loglevel);
|
||||
}
|
||||
|
||||
Version sqliteVersion = new Version((string)Duplicati.Library.SQLiteHelper.SQLiteLoader.SQLiteConnectionType.GetProperty("SQLiteVersion").GetValue(null, null));
|
||||
@@ -418,11 +427,9 @@ namespace Duplicati.Server
|
||||
try { PingPongThread.Abort(); }
|
||||
catch { }
|
||||
|
||||
#if DEBUG
|
||||
//Flush the file
|
||||
using (Duplicati.Library.Logging.Log.CurrentLog as Duplicati.Library.Logging.StreamLog)
|
||||
Duplicati.Library.Logging.Log.CurrentLog = null;
|
||||
#endif
|
||||
if (LogHandler != null)
|
||||
LogHandler.Dispose();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -286,6 +286,21 @@ namespace Duplicati.Server
|
||||
if (data.ExtraOptions != null)
|
||||
foreach(var k in data.ExtraOptions)
|
||||
options[k.Key] = k.Value;
|
||||
|
||||
if (options.ContainsKey("log-file"))
|
||||
{
|
||||
var file = options["log-file"];
|
||||
|
||||
string o;
|
||||
Library.Logging.LogMessageType level;
|
||||
options.TryGetValue("log-level", out o);
|
||||
Enum.TryParse<Library.Logging.LogMessageType>(o, true, out level);
|
||||
|
||||
options.Remove("log-file");
|
||||
options.Remove("log-level");
|
||||
|
||||
Program.LogHandler.SetOperationFile(file, level);
|
||||
}
|
||||
|
||||
using(var controller = new Duplicati.Library.Main.Controller(backup.TargetURL, options, sink))
|
||||
{
|
||||
@@ -365,6 +380,7 @@ namespace Duplicati.Server
|
||||
finally
|
||||
{
|
||||
((RunnerData)data).Controller = null;
|
||||
Program.LogHandler.RemoveOperationFile();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -60,6 +60,7 @@ namespace Duplicati.Server.WebServer
|
||||
SUPPORTED_METHODS.Add("get-license-data", GetLicenseData);
|
||||
SUPPORTED_METHODS.Add("get-changelog", GetChangelog);
|
||||
SUPPORTED_METHODS.Add("locate-uri-db", LocateUriDb);
|
||||
SUPPORTED_METHODS.Add("poll-log-messages", PollLogMessages);
|
||||
}
|
||||
|
||||
public override bool Process (HttpServer.IHttpRequest request, HttpServer.IHttpResponse response, HttpServer.Sessions.IHttpSession session)
|
||||
@@ -349,7 +350,7 @@ namespace Duplicati.Server.WebServer
|
||||
|
||||
bw.OutputOK();
|
||||
}
|
||||
catch (Duplicati.Library.Interface.FolderMissingException fex)
|
||||
catch (Duplicati.Library.Interface.FolderMissingException)
|
||||
{
|
||||
ReportError(response, bw, "missing-folder");
|
||||
}
|
||||
@@ -403,7 +404,8 @@ namespace Duplicati.Server.WebServer
|
||||
GenericModules = Serializable.ServerSettings.GenericModules,
|
||||
WebModules = Serializable.ServerSettings.WebModules,
|
||||
ConnectionModules = Serializable.ServerSettings.ConnectionModules,
|
||||
UsingAlternateUpdateURLs = Duplicati.Library.AutoUpdater.AutoUpdateSettings.UsesAlternateURLs
|
||||
UsingAlternateUpdateURLs = Duplicati.Library.AutoUpdater.AutoUpdateSettings.UsesAlternateURLs,
|
||||
LogLevels = Enum.GetNames(typeof(Duplicati.Library.Logging.LogMessageType))
|
||||
});
|
||||
}
|
||||
|
||||
@@ -649,6 +651,21 @@ namespace Duplicati.Server.WebServer
|
||||
});
|
||||
}
|
||||
|
||||
private void PollLogMessages(HttpServer.IHttpRequest request, HttpServer.IHttpResponse response, HttpServer.Sessions.IHttpSession session, BodyWriter bw)
|
||||
{
|
||||
HttpServer.HttpInput input = request.Method.ToUpper() == "POST" ? request.Form : request.QueryString;
|
||||
var level_str = input["level"].Value ?? "";
|
||||
var id_str = input["id"].Value ?? "";
|
||||
|
||||
Library.Logging.LogMessageType level;
|
||||
long id;
|
||||
|
||||
long.TryParse(id_str, out id);
|
||||
Enum.TryParse(level_str, true, out level);
|
||||
|
||||
bw.OutputOK(Program.LogHandler.AfterID(id, level));
|
||||
}
|
||||
|
||||
private void SendCommand(HttpServer.IHttpRequest request, HttpServer.IHttpResponse response, HttpServer.Sessions.IHttpSession session, BodyWriter bw)
|
||||
{
|
||||
HttpServer.HttpInput input = request.Method.ToUpper() == "POST" ? request.Form : request.QueryString;
|
||||
|
||||
@@ -90,6 +90,15 @@
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script id="live-log-data-template" type="text/x-jquery-tmpl">
|
||||
<div class="log-entry">
|
||||
<div class="log-id">${BackupID}</div>
|
||||
<div class="log-timestamp" title="${When}">${When}</div>
|
||||
<div class="log-message">${Message}</div>
|
||||
<div class="log-exception">${Exception}</div>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script id="log-backup-general-template" type="text/x-jquery-tmpl">
|
||||
<div class="log-entry">
|
||||
<div class="log-operationid">${BackupID}</div>
|
||||
@@ -460,6 +469,17 @@
|
||||
<div id="folder-browse-dialog" class="modal-dialog" title="Choose folder"></div>
|
||||
|
||||
<div id="log-dialog" class="modal-dialog" title="Log data">
|
||||
<ul>
|
||||
<li><a href="#log-tab-stored">Stored</a></li>
|
||||
<li><a href="#log-tab-live">Live</a></li>
|
||||
</ul>
|
||||
|
||||
<div id="log-tab-stored">
|
||||
</div>
|
||||
|
||||
<div id="log-tab-live">
|
||||
<select id="log-tab-live-level"></select>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
@@ -999,7 +999,7 @@ $(document).ready(function() {
|
||||
};
|
||||
|
||||
$('#edit-dialog-form').find('input').change(setStateModified);
|
||||
$('#edit-dialog-form').find('option').change(setStateModified);
|
||||
$('#edit-dialog-form').find('select').change(setStateModified);
|
||||
$('#edit-dialog-form').find('textarea').change(setStateModified);
|
||||
|
||||
$('#use-scheduled-run').change(function() {
|
||||
|
||||
@@ -1,86 +1,174 @@
|
||||
$(document).ready(function() {
|
||||
var appdatacallback = function(data, tag) {
|
||||
$.tmpl($('#log-data-template'), data).appendTo(tag);
|
||||
tag.find('.log-exception').each(function(i, e) {
|
||||
var td = $.tmpl($('#log-data-template'), data);
|
||||
td.appendTo(tag);
|
||||
|
||||
td.find('.log-exception').each(function(i, e) {
|
||||
$(e).html(nl2br($(e).html()));
|
||||
});
|
||||
|
||||
tag.find('.log-exception').click(function(e) {
|
||||
td.find('.log-exception').click(function(e) {
|
||||
e.bubbles = false;
|
||||
return false;
|
||||
});
|
||||
tag.find('.log-timestamp').each(function(i, e) {
|
||||
td.find('.log-timestamp').each(function(i, e) {
|
||||
$(e).text((new Date(parseInt($(e).attr('title')))).toLocaleString());
|
||||
});
|
||||
tag.find('.log-entry').click(function(e) {
|
||||
$(e.target).closest('.log-entry').toggleClass('expanded');
|
||||
td.click(function(e) {
|
||||
$(this).toggleClass('expanded');
|
||||
});
|
||||
};
|
||||
|
||||
var livedatacallback = function(data, tag) {
|
||||
var td = $.tmpl($('#live-log-data-template'), data)
|
||||
td.appendTo(tag);
|
||||
|
||||
td.find('.log-exception').each(function(i, e) {
|
||||
$(e).html(nl2br($(e).html()));
|
||||
});
|
||||
|
||||
td.find('.log-exception').click(function(e) {
|
||||
e.bubbles = false;
|
||||
return false;
|
||||
});
|
||||
td.find('.log-timestamp').each(function(i, e) {
|
||||
$(e).text(new Date($(e).attr('title')).toLocaleString());
|
||||
});
|
||||
td.click(function(e) {
|
||||
$(this).toggleClass('expanded');
|
||||
});
|
||||
|
||||
tag.parent().scrollTop(tag.parent().height());
|
||||
};
|
||||
|
||||
var generaldatacallback = function(data, tag) {
|
||||
$.tmpl($('#log-backup-general-template'), data).appendTo(tag);
|
||||
tag.find('.log-exception').each(function(i, e) {
|
||||
var td = $.tmpl($('#log-backup-general-template'), data)
|
||||
td.appendTo(tag);
|
||||
|
||||
td.find('.log-exception').each(function(i, e) {
|
||||
$(e).html(nl2br($(e).html()));
|
||||
});
|
||||
tag.find('.log-message').each(function(i, e) {
|
||||
td.find('.log-message').each(function(i, e) {
|
||||
$(e).html(nl2br($(e).html()));
|
||||
});
|
||||
|
||||
tag.find('.log-exception').click(function(e) {
|
||||
td.find('.log-exception').click(function(e) {
|
||||
return e.bubbles = false;
|
||||
});
|
||||
tag.find('.log-message').click(function(e) {
|
||||
td.find('.log-message').click(function(e) {
|
||||
return e.bubbles = false;
|
||||
});
|
||||
|
||||
tag.find('.log-timestamp').each(function(i, e) {
|
||||
td.find('.log-timestamp').each(function(i, e) {
|
||||
$(e).text((new Date(parseInt($(e).attr('title')))).toLocaleString());
|
||||
});
|
||||
|
||||
tag.find('.log-entry').click(function(e) {
|
||||
$(e.target).closest('.log-entry').toggleClass('expanded');
|
||||
td.click(function(e) {
|
||||
$(this).toggleClass('expanded');
|
||||
});
|
||||
};
|
||||
|
||||
var remotedatacallback = function(data, tag) {
|
||||
$.tmpl($('#log-backup-remote-template'), data).appendTo(tag);
|
||||
tag.find('.log-data').each(function(i, e) {
|
||||
var td = $.tmpl($('#log-backup-remote-template'), data);
|
||||
td.appendTo(tag);
|
||||
|
||||
td.find('.log-data').each(function(i, e) {
|
||||
$(e).html(nl2br($(e).html()));
|
||||
});
|
||||
tag.find('.log-data').click(function(e) {
|
||||
td.find('.log-data').click(function(e) {
|
||||
return e.bubbles = false;
|
||||
});
|
||||
|
||||
tag.find('.log-timestamp').each(function(i, e) {
|
||||
td.find('.log-timestamp').each(function(i, e) {
|
||||
$(e).text((new Date(parseInt($(e).attr('title')))).toLocaleString());
|
||||
});
|
||||
|
||||
tag.find('.log-entry').click(function(e) {
|
||||
td.click(function(e) {
|
||||
$(e.target).closest('.log-entry').toggleClass('expanded');
|
||||
});
|
||||
};
|
||||
|
||||
var refreshRunning = false;
|
||||
var refreshId = 0;
|
||||
|
||||
var refreshLiveData = function() {
|
||||
if (refreshRunning)
|
||||
return;
|
||||
|
||||
var v = $('#log-tab-live-level').val();
|
||||
if (v == '' || v == 'disabled')
|
||||
return;
|
||||
|
||||
if (!$('#log-dialog').dialog('isOpen'))
|
||||
return;
|
||||
|
||||
refreshRunning = true;
|
||||
|
||||
APP_DATA.callServer({'action': 'poll-log-messages', 'level': v, 'id': refreshId},
|
||||
function(data) {
|
||||
for(var n in data)
|
||||
refreshId = Math.max(refreshId, data[n].ID);
|
||||
|
||||
refreshRunning = false;
|
||||
livedatacallback(data, $('#log-tab-live'));
|
||||
setTimeout(refreshLiveData, 3000);
|
||||
},
|
||||
function() {
|
||||
refreshRunning = false;
|
||||
setTimeout(refreshLiveData, 1000);
|
||||
});
|
||||
};
|
||||
|
||||
$.showAppLog = function() {
|
||||
var dlg_buttons = $('#log-dialog').parent().find('.ui-dialog-buttonpane').find('.ui-button');
|
||||
|
||||
$('#log-dialog').dialog('open');
|
||||
|
||||
var data = $('#log-dialog').data('log');
|
||||
var data = $('#log-tab-stored').data('log');
|
||||
if (!data) {
|
||||
data = {
|
||||
first: null,
|
||||
last: null
|
||||
};
|
||||
$('#log-dialog').data('log', data);
|
||||
$('#log-tab-stored').data('log', data);
|
||||
}
|
||||
|
||||
if (data.first == null) {
|
||||
$('#log-dialog').empty();
|
||||
$('#log-tab-stored').empty();
|
||||
APP_DATA.callServer({'action': 'read-log', 'pagesize': 100}, function(data) {
|
||||
appdatacallback(data, $('#log-dialog'));
|
||||
appdatacallback(data, $('#log-tab-stored'));
|
||||
});
|
||||
};
|
||||
|
||||
var loglevel = $('#log-tab-live-level');
|
||||
loglevel.empty();
|
||||
|
||||
$('#log-tab-live').find('.log-entry').remove();
|
||||
|
||||
loglevel.change(function() {
|
||||
refreshId = 0;
|
||||
|
||||
var v = $('#log-tab-live-level').val();
|
||||
if (v == '' || v == 'disabled')
|
||||
return;
|
||||
|
||||
$('#log-tab-live').find('.log-entry').remove();
|
||||
refreshLiveData();
|
||||
});
|
||||
|
||||
APP_DATA.getServerConfig(function(server_config) {
|
||||
|
||||
loglevel.append($('<option></option>')
|
||||
.attr('value', 'disabled')
|
||||
.text('Disabled'));
|
||||
|
||||
for(var n in server_config.LogLevels)
|
||||
loglevel.append($('<option></option>')
|
||||
.attr('value',server_config.LogLevels[n])
|
||||
.text(server_config.LogLevels[n]));
|
||||
|
||||
}, function() { alert('Failed to read server data'); });
|
||||
|
||||
};
|
||||
|
||||
$.showBackupLog = function(id) {
|
||||
@@ -91,8 +179,14 @@ $(document).ready(function() {
|
||||
$('<div id="restore-search-loader" class="small-loader-icon">').appendTo($('#backup-log-tab-general'));
|
||||
$('<div id="restore-search-loader" class="small-loader-icon">').appendTo($('#backup-log-tab-remote'));
|
||||
|
||||
var loglevel = $('#backup-log-live-level');
|
||||
loglevel.empty();
|
||||
|
||||
loglevel.change(refreshLiveData);
|
||||
|
||||
$('#backup-log-dialog').dialog('open');
|
||||
|
||||
|
||||
APP_DATA.callServer({'action': 'read-log', 'pagesize': 100, 'id': id, 'remotelog': false}, function(generaldata) {
|
||||
APP_DATA.callServer({'action': 'read-log', 'pagesize': 100, 'id': id, 'remotelog': true}, function(remotedata) {
|
||||
$('#backup-log-tab-general').empty();
|
||||
@@ -100,11 +194,13 @@ $(document).ready(function() {
|
||||
|
||||
generaldatacallback(generaldata, $('#backup-log-tab-general'));
|
||||
remotedatacallback(remotedata, $('#backup-log-tab-remote'));
|
||||
|
||||
}, function() { alert('Failed to read log data'); });
|
||||
}, function() { alert('Failed to read log data'); });
|
||||
};
|
||||
|
||||
|
||||
$('#log-dialog').tabs({ active: 0 });
|
||||
$('#log-dialog').dialog({
|
||||
minWidth: 320,
|
||||
width: $('body').width > 600 ? 320 : 600,
|
||||
@@ -115,7 +211,7 @@ $(document).ready(function() {
|
||||
closeOnEscape: true,
|
||||
buttons: [
|
||||
{ text: 'Close', disabled: false, click: function(event, ui) {
|
||||
$('#log-dialog').dialog('close');
|
||||
$(this).dialog('close');
|
||||
}}
|
||||
]
|
||||
});
|
||||
@@ -131,7 +227,7 @@ $(document).ready(function() {
|
||||
closeOnEscape: true,
|
||||
buttons: [
|
||||
{ text: 'Close', disabled: false, click: function(event, ui) {
|
||||
$('#backup-log-dialog').dialog('close');
|
||||
$(this).dialog('close');
|
||||
}}
|
||||
]
|
||||
});
|
||||
|
||||
@@ -27,6 +27,23 @@
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
#log-tab-live .log-entry .log-message
|
||||
{
|
||||
display: inline;
|
||||
}
|
||||
|
||||
|
||||
#log-tab-live .log-entry
|
||||
{
|
||||
height: 1.2em;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#log-tab-live .log-entry.expanded
|
||||
{
|
||||
height: auto;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -277,6 +277,14 @@ div.main-icon-plus {
|
||||
border-top-right-radius: 0px;
|
||||
}
|
||||
|
||||
#log-dialog {
|
||||
padding: 0px;
|
||||
border: 0px none;
|
||||
width: 100%;
|
||||
border-top-left-radius: 0px;
|
||||
border-top-right-radius: 0px;
|
||||
}
|
||||
|
||||
.ui-dialog-titlebar {
|
||||
border-bottom-right-radius: 0px;
|
||||
border-bottom-left-radius: 0px;
|
||||
|
||||
Reference in New Issue
Block a user