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.Models.WalletViewModels;
|
||||
using System.Security.Claims;
|
||||
using BTCPayServer.Models.ServerViewModels;
|
||||
using BTCPayServer.Security;
|
||||
using NBXplorer.Models;
|
||||
using RatesViewModel = BTCPayServer.Models.StoreViewModels.RatesViewModel;
|
||||
|
||||
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]
|
||||
[Trait("Fast", "Fast")]
|
||||
public void CheckRatesProvider()
|
||||
|
||||
@@ -16,6 +16,7 @@ using NBitcoin.DataEncoders;
|
||||
using BTCPayServer.SSH;
|
||||
using BTCPayServer.Lightning;
|
||||
using BTCPayServer.Configuration.External;
|
||||
using Serilog.Events;
|
||||
|
||||
namespace BTCPayServer.Configuration
|
||||
{
|
||||
@@ -37,6 +38,12 @@ namespace BTCPayServer.Configuration
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public string LogFile
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
public string DataDir
|
||||
{
|
||||
get;
|
||||
@@ -54,6 +61,16 @@ namespace BTCPayServer.Configuration
|
||||
set;
|
||||
} = 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)
|
||||
{
|
||||
NetworkType = DefaultConfiguration.GetNetworkType(conf);
|
||||
@@ -174,6 +191,13 @@ namespace BTCPayServer.Configuration
|
||||
var old = conf.GetOrDefault<Uri>("internallightningnode", null);
|
||||
if (old != null)
|
||||
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)
|
||||
|
||||
@@ -41,6 +41,7 @@ namespace BTCPayServer.Configuration
|
||||
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("--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())
|
||||
{
|
||||
var crypto = network.CryptoCode.ToLowerInvariant();
|
||||
|
||||
@@ -17,6 +17,7 @@ using NBitcoin.DataEncoders;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
@@ -167,6 +168,7 @@ namespace BTCPayServer.Controllers
|
||||
vm.DNSDomain = null;
|
||||
return View(vm);
|
||||
}
|
||||
|
||||
[Route("server/maintenance")]
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> Maintenance(MaintenanceViewModel vm, string command)
|
||||
@@ -625,5 +627,60 @@ namespace BTCPayServer.Controllers
|
||||
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 int Index { get; set; }
|
||||
}
|
||||
|
||||
public List<LNDServiceViewModel> LNDServices { get; set; } = new List<LNDServiceViewModel>();
|
||||
public bool HasSSH { get; set; }
|
||||
}
|
||||
|
||||
@@ -55,18 +55,15 @@ namespace BTCPayServer
|
||||
l.AddProvider(new CustomConsoleLogProvider(processor));
|
||||
|
||||
// Use Serilog for debug log file.
|
||||
string debugLogFile = conf.GetOrDefault<string>("debuglog", null);
|
||||
if (String.IsNullOrEmpty(debugLogFile) == false)
|
||||
{
|
||||
var debugLogFile = BTCPayServerOptions.GetDebugLog(conf);
|
||||
if (string.IsNullOrEmpty(debugLogFile) != false) return;
|
||||
Serilog.Log.Logger = new LoggerConfiguration()
|
||||
.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)
|
||||
.CreateLogger();
|
||||
|
||||
l.AddSerilog(Serilog.Log.Logger);
|
||||
logger.LogDebug($"Debug log file configured for {debugLogFile}.");
|
||||
}
|
||||
})
|
||||
.UseStartup<Startup>()
|
||||
.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
|
||||
{
|
||||
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.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.Logs)" asp-action="Logs">Logs</a>
|
||||
<a class="nav-link @ViewData.IsActivePage(ServerNavPages.Hangfire)" href="~/hangfire" target="_blank">Hangfire</a>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user