mirror of
https://github.com/aljazceru/btcpayserver.git
synced 2025-12-17 14:04:26 +01:00
Logs UI in Server Admin (#374)
* add in ui * add in logging viewer * Revert "add in ui" This reverts commit 9614721fa8a439f7097adca69772b5d41f6585d6. * finish basic feature * clean up * improve and fix build * add in debug log level command option * use paging for log file list, use extension to select log files, show message for setting up logging * make paging a little better * add very basic UT for logs * Update ServerController.cs
This commit is contained in:
committed by
Nicolas Dorier
parent
d152d5cd90
commit
c9c7316b7d
@@ -43,8 +43,10 @@ using System.Security.Cryptography.X509Certificates;
|
|||||||
using BTCPayServer.Lightning;
|
using BTCPayServer.Lightning;
|
||||||
using BTCPayServer.Models.WalletViewModels;
|
using BTCPayServer.Models.WalletViewModels;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
|
using BTCPayServer.Models.ServerViewModels;
|
||||||
using BTCPayServer.Security;
|
using BTCPayServer.Security;
|
||||||
using NBXplorer.Models;
|
using NBXplorer.Models;
|
||||||
|
using RatesViewModel = BTCPayServer.Models.StoreViewModels.RatesViewModel;
|
||||||
|
|
||||||
namespace BTCPayServer.Tests
|
namespace BTCPayServer.Tests
|
||||||
{
|
{
|
||||||
@@ -1772,6 +1774,22 @@ namespace BTCPayServer.Tests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
[Trait("Integration", "Integration")]
|
||||||
|
public async Task CheckLogsRoute()
|
||||||
|
{
|
||||||
|
using (var tester = ServerTester.Create())
|
||||||
|
{
|
||||||
|
tester.Start();
|
||||||
|
var user = tester.NewAccount();
|
||||||
|
user.GrantAccess();
|
||||||
|
user.RegisterDerivationScheme("BTC");
|
||||||
|
|
||||||
|
var serverController = user.GetController<ServerController>();
|
||||||
|
var vm = Assert.IsType<LogsViewModel>(Assert.IsType<ViewResult>(await serverController.LogsView()).Model);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
[Trait("Fast", "Fast")]
|
[Trait("Fast", "Fast")]
|
||||||
public void CheckRatesProvider()
|
public void CheckRatesProvider()
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ using NBitcoin.DataEncoders;
|
|||||||
using BTCPayServer.SSH;
|
using BTCPayServer.SSH;
|
||||||
using BTCPayServer.Lightning;
|
using BTCPayServer.Lightning;
|
||||||
using BTCPayServer.Configuration.External;
|
using BTCPayServer.Configuration.External;
|
||||||
|
using Serilog.Events;
|
||||||
|
|
||||||
namespace BTCPayServer.Configuration
|
namespace BTCPayServer.Configuration
|
||||||
{
|
{
|
||||||
@@ -37,6 +38,12 @@ namespace BTCPayServer.Configuration
|
|||||||
get;
|
get;
|
||||||
private set;
|
private set;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string LogFile
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
private set;
|
||||||
|
}
|
||||||
public string DataDir
|
public string DataDir
|
||||||
{
|
{
|
||||||
get;
|
get;
|
||||||
@@ -54,6 +61,16 @@ namespace BTCPayServer.Configuration
|
|||||||
set;
|
set;
|
||||||
} = new List<NBXplorerConnectionSetting>();
|
} = new List<NBXplorerConnectionSetting>();
|
||||||
|
|
||||||
|
public static string GetDebugLog(IConfiguration configuration)
|
||||||
|
{
|
||||||
|
return configuration.GetValue<string>("debuglog", null);
|
||||||
|
}
|
||||||
|
public static LogEventLevel GetDebugLogLevel(IConfiguration configuration)
|
||||||
|
{
|
||||||
|
var raw = configuration.GetValue("debugloglevel", nameof(LogEventLevel.Debug));
|
||||||
|
return (LogEventLevel)Enum.Parse(typeof(LogEventLevel), raw, true);
|
||||||
|
}
|
||||||
|
|
||||||
public void LoadArgs(IConfiguration conf)
|
public void LoadArgs(IConfiguration conf)
|
||||||
{
|
{
|
||||||
NetworkType = DefaultConfiguration.GetNetworkType(conf);
|
NetworkType = DefaultConfiguration.GetNetworkType(conf);
|
||||||
@@ -174,6 +191,13 @@ namespace BTCPayServer.Configuration
|
|||||||
var old = conf.GetOrDefault<Uri>("internallightningnode", null);
|
var old = conf.GetOrDefault<Uri>("internallightningnode", null);
|
||||||
if (old != null)
|
if (old != null)
|
||||||
throw new ConfigException($"internallightningnode should not be used anymore, use btclightning instead");
|
throw new ConfigException($"internallightningnode should not be used anymore, use btclightning instead");
|
||||||
|
|
||||||
|
LogFile = GetDebugLog(conf);
|
||||||
|
if (!string.IsNullOrEmpty(LogFile))
|
||||||
|
{
|
||||||
|
Logs.Configuration.LogInformation("LogFile: " + LogFile);
|
||||||
|
Logs.Configuration.LogInformation("Log Level: " + GetDebugLogLevel(conf));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private SSHSettings ParseSSHConfiguration(IConfiguration conf)
|
private SSHSettings ParseSSHConfiguration(IConfiguration conf)
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ namespace BTCPayServer.Configuration
|
|||||||
app.Option("--sshkeyfilepassword", "Password of the SSH keyfile (default: empty)", CommandOptionType.SingleValue);
|
app.Option("--sshkeyfilepassword", "Password of the SSH keyfile (default: empty)", CommandOptionType.SingleValue);
|
||||||
app.Option("--sshtrustedfingerprints", "SSH Host public key fingerprint or sha256 (default: empty, it will allow untrusted connections)", CommandOptionType.SingleValue);
|
app.Option("--sshtrustedfingerprints", "SSH Host public key fingerprint or sha256 (default: empty, it will allow untrusted connections)", CommandOptionType.SingleValue);
|
||||||
app.Option("--debuglog", "A rolling log file for debug messages.", CommandOptionType.SingleValue);
|
app.Option("--debuglog", "A rolling log file for debug messages.", CommandOptionType.SingleValue);
|
||||||
|
app.Option("--debugloglevel", "The severity you log (default:information)", CommandOptionType.SingleValue);
|
||||||
foreach (var network in provider.GetAll())
|
foreach (var network in provider.GetAll())
|
||||||
{
|
{
|
||||||
var crypto = network.CryptoCode.ToLowerInvariant();
|
var crypto = network.CryptoCode.ToLowerInvariant();
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ using NBitcoin.DataEncoders;
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
@@ -167,6 +168,7 @@ namespace BTCPayServer.Controllers
|
|||||||
vm.DNSDomain = null;
|
vm.DNSDomain = null;
|
||||||
return View(vm);
|
return View(vm);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Route("server/maintenance")]
|
[Route("server/maintenance")]
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
public async Task<IActionResult> Maintenance(MaintenanceViewModel vm, string command)
|
public async Task<IActionResult> Maintenance(MaintenanceViewModel vm, string command)
|
||||||
@@ -625,5 +627,60 @@ namespace BTCPayServer.Controllers
|
|||||||
return View(model);
|
return View(model);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Route("server/logs/{file?}")]
|
||||||
|
public async Task<IActionResult> LogsView(string file = null, int offset = 0)
|
||||||
|
{
|
||||||
|
if (offset < 0)
|
||||||
|
{
|
||||||
|
offset = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
var vm = new LogsViewModel();
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(_Options.LogFile))
|
||||||
|
{
|
||||||
|
vm.StatusMessage = "Error: File Logging Option not specified. " +
|
||||||
|
"You need to set debuglog and optionally " +
|
||||||
|
"debugloglevel in the configuration or through runtime arguments";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var di = Directory.GetParent(_Options.LogFile);
|
||||||
|
if (di == null)
|
||||||
|
{
|
||||||
|
vm.StatusMessage = "Error: Could not load log files";
|
||||||
|
}
|
||||||
|
|
||||||
|
var fileNameWithoutExtension = Path.GetFileNameWithoutExtension(_Options.LogFile);
|
||||||
|
var fileExtension = Path.GetExtension(_Options.LogFile) ?? string.Empty;
|
||||||
|
var logFiles = di.GetFiles($"{fileNameWithoutExtension}*{fileExtension}");
|
||||||
|
vm.LogFileCount = logFiles.Length;
|
||||||
|
vm.LogFiles = logFiles
|
||||||
|
.OrderBy(info => info.LastWriteTime)
|
||||||
|
.Skip(offset)
|
||||||
|
.Take(5)
|
||||||
|
.ToList();
|
||||||
|
vm.LogFileOffset = offset;
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(file)) return View("Logs", vm);
|
||||||
|
vm.Log = "";
|
||||||
|
var path = Path.Combine(di.FullName, file);
|
||||||
|
|
||||||
|
using (var fileStream = new FileStream(
|
||||||
|
path,
|
||||||
|
FileMode.Open,
|
||||||
|
FileAccess.Read,
|
||||||
|
FileShare.ReadWrite))
|
||||||
|
{
|
||||||
|
using (var reader = new StreamReader(fileStream))
|
||||||
|
{
|
||||||
|
vm.Log = await reader.ReadToEndAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return View("Logs", vm);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
19
BTCPayServer/Models/ServerViewModels/LogsViewModel.cs
Normal file
19
BTCPayServer/Models/ServerViewModels/LogsViewModel.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace BTCPayServer.Models.ServerViewModels
|
||||||
|
{
|
||||||
|
public class LogsViewModel
|
||||||
|
{
|
||||||
|
|
||||||
|
public string StatusMessage
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<FileInfo> LogFiles { get; set; } = new List<FileInfo>();
|
||||||
|
public string Log { get; set; }
|
||||||
|
public int LogFileCount { get; set; }
|
||||||
|
public int LogFileOffset{ get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,6 +14,7 @@ namespace BTCPayServer.Models.ServerViewModels
|
|||||||
public LndTypes Type { get; set; }
|
public LndTypes Type { get; set; }
|
||||||
public int Index { get; set; }
|
public int Index { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<LNDServiceViewModel> LNDServices { get; set; } = new List<LNDServiceViewModel>();
|
public List<LNDServiceViewModel> LNDServices { get; set; } = new List<LNDServiceViewModel>();
|
||||||
public bool HasSSH { get; set; }
|
public bool HasSSH { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,18 +55,15 @@ namespace BTCPayServer
|
|||||||
l.AddProvider(new CustomConsoleLogProvider(processor));
|
l.AddProvider(new CustomConsoleLogProvider(processor));
|
||||||
|
|
||||||
// Use Serilog for debug log file.
|
// Use Serilog for debug log file.
|
||||||
string debugLogFile = conf.GetOrDefault<string>("debuglog", null);
|
var debugLogFile = BTCPayServerOptions.GetDebugLog(conf);
|
||||||
if (String.IsNullOrEmpty(debugLogFile) == false)
|
if (string.IsNullOrEmpty(debugLogFile) != false) return;
|
||||||
{
|
Serilog.Log.Logger = new LoggerConfiguration()
|
||||||
Serilog.Log.Logger = new LoggerConfiguration()
|
|
||||||
.Enrich.FromLogContext()
|
.Enrich.FromLogContext()
|
||||||
.MinimumLevel.Debug()
|
.MinimumLevel.Is(BTCPayServerOptions.GetDebugLogLevel(conf))
|
||||||
.WriteTo.File(debugLogFile, rollingInterval: RollingInterval.Day, fileSizeLimitBytes: MAX_DEBUG_LOG_FILE_SIZE, rollOnFileSizeLimit: true, retainedFileCountLimit: 1)
|
.WriteTo.File(debugLogFile, rollingInterval: RollingInterval.Day, fileSizeLimitBytes: MAX_DEBUG_LOG_FILE_SIZE, rollOnFileSizeLimit: true, retainedFileCountLimit: 1)
|
||||||
.CreateLogger();
|
.CreateLogger();
|
||||||
|
|
||||||
l.AddSerilog(Serilog.Log.Logger);
|
l.AddSerilog(Serilog.Log.Logger);
|
||||||
logger.LogDebug($"Debug log file configured for {debugLogFile}.");
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.UseStartup<Startup>()
|
.UseStartup<Startup>()
|
||||||
.Build();
|
.Build();
|
||||||
|
|||||||
40
BTCPayServer/Views/Server/Logs.cshtml
Normal file
40
BTCPayServer/Views/Server/Logs.cshtml
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
@model BTCPayServer.Models.ServerViewModels.LogsViewModel
|
||||||
|
@{
|
||||||
|
ViewData.SetActivePageAndTitle(ServerNavPages.Logs);
|
||||||
|
}
|
||||||
|
|
||||||
|
<h4>@ViewData["Title"]</h4>
|
||||||
|
<partial name="_StatusMessage" for="StatusMessage"/>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<ul>
|
||||||
|
@foreach (var file in Model.LogFiles)
|
||||||
|
{
|
||||||
|
<li>
|
||||||
|
<a asp-action="LogsView" asp-route-file="@file.Name">@file.Name</a>
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
<li>
|
||||||
|
@if (Model.LogFileOffset > 0)
|
||||||
|
{
|
||||||
|
<a asp-action="LogsView" asp-route-offset="@(Model.LogFileOffset - 5)"><<</a>
|
||||||
|
}
|
||||||
|
Showing @Model.LogFileOffset - (@Model.LogFileOffset+@Model.LogFiles.Count) of @Model.LogFileCount
|
||||||
|
@if ((Model.LogFileOffset+ Model.LogFiles.Count) < Model.LogFileCount)
|
||||||
|
{
|
||||||
|
<a asp-action="LogsView" asp-route-offset="@(Model.LogFileOffset + Model.LogFiles.Count)">>></a>
|
||||||
|
}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
@if (!string.IsNullOrEmpty(Model.Log))
|
||||||
|
{
|
||||||
|
<pre>
|
||||||
|
@Model.Log
|
||||||
|
</pre>
|
||||||
|
}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@section Scripts {
|
||||||
|
@await Html.PartialAsync("_ValidationScriptsPartial")
|
||||||
|
}
|
||||||
@@ -7,6 +7,6 @@ namespace BTCPayServer.Views.Server
|
|||||||
{
|
{
|
||||||
public enum ServerNavPages
|
public enum ServerNavPages
|
||||||
{
|
{
|
||||||
Index, Users, Rates, Emails, Policies, Theme, Hangfire, Services, Maintenance
|
Index, Users, Rates, Emails, Policies, Theme, Hangfire, Services, Maintenance, Logs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
<a class="nav-link @ViewData.IsActivePage(ServerNavPages.Services)" asp-action="Services">Services</a>
|
<a class="nav-link @ViewData.IsActivePage(ServerNavPages.Services)" asp-action="Services">Services</a>
|
||||||
<a class="nav-link @ViewData.IsActivePage(ServerNavPages.Theme)" asp-action="Theme">Theme</a>
|
<a class="nav-link @ViewData.IsActivePage(ServerNavPages.Theme)" asp-action="Theme">Theme</a>
|
||||||
<a class="nav-link @ViewData.IsActivePage(ServerNavPages.Maintenance)" asp-action="Maintenance">Maintenance</a>
|
<a class="nav-link @ViewData.IsActivePage(ServerNavPages.Maintenance)" asp-action="Maintenance">Maintenance</a>
|
||||||
|
<a class="nav-link @ViewData.IsActivePage(ServerNavPages.Logs)" asp-action="Logs">Logs</a>
|
||||||
<a class="nav-link @ViewData.IsActivePage(ServerNavPages.Hangfire)" href="~/hangfire" target="_blank">Hangfire</a>
|
<a class="nav-link @ViewData.IsActivePage(ServerNavPages.Hangfire)" href="~/hangfire" target="_blank">Hangfire</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user