From 98c4b658c49f3ff4cbd54eb3473d6762d0fbec89 Mon Sep 17 00:00:00 2001 From: Kukks Date: Fri, 28 Aug 2020 08:49:13 +0200 Subject: [PATCH] Add invoice event severity closes #1681 https://i.imgur.com/eyMO9M3.png --- BTCPayServer.Data/Data/InvoiceEventData.cs | 21 ++++++++ ...200901161733_AddInvoiceEventLogSeverity.cs | 32 +++++++++++++ .../ApplicationDbContextModelSnapshot.cs | 3 ++ BTCPayServer/Controllers/InvoiceController.cs | 18 +++---- .../InvoiceNotificationManager.cs | 14 +++--- BTCPayServer/Logging/InvoiceLog.cs | 14 +++--- .../Bitcoin/BitcoinLikePaymentHandler.cs | 6 +-- .../PayJoin/PayJoinEndpointController.cs | 3 +- .../Services/Invoices/InvoiceRepository.cs | 48 +++++++++---------- BTCPayServer/Views/Invoice/Invoice.cshtml | 2 +- 10 files changed, 110 insertions(+), 51 deletions(-) create mode 100644 BTCPayServer.Data/Migrations/20200901161733_AddInvoiceEventLogSeverity.cs diff --git a/BTCPayServer.Data/Data/InvoiceEventData.cs b/BTCPayServer.Data/Data/InvoiceEventData.cs index d41c65f17..7042b7d3f 100644 --- a/BTCPayServer.Data/Data/InvoiceEventData.cs +++ b/BTCPayServer.Data/Data/InvoiceEventData.cs @@ -20,6 +20,7 @@ namespace BTCPayServer.Data } public string Message { get; set; } + public EventSeverity Severity { get; set; } = EventSeverity.Info; internal static void OnModelCreating(ModelBuilder builder) { @@ -35,5 +36,25 @@ namespace BTCPayServer.Data #pragma warning restore CS0618 }); } + + public enum EventSeverity + { + Info, + Error, + Success, + Warning + } + + public string GetCssClass() + { + return Severity switch + { + EventSeverity.Info => "info", + EventSeverity.Error => "danger", + EventSeverity.Success => "success", + EventSeverity.Warning => "warning", + _ => null + }; + } } } diff --git a/BTCPayServer.Data/Migrations/20200901161733_AddInvoiceEventLogSeverity.cs b/BTCPayServer.Data/Migrations/20200901161733_AddInvoiceEventLogSeverity.cs new file mode 100644 index 000000000..6b0c35fc1 --- /dev/null +++ b/BTCPayServer.Data/Migrations/20200901161733_AddInvoiceEventLogSeverity.cs @@ -0,0 +1,32 @@ +using BTCPayServer.Data; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace BTCPayServer.Migrations +{ + + [DbContext(typeof(ApplicationDbContext))] + [Migration("20200901161733_AddInvoiceEventLogSeverity")] + public partial class AddInvoiceEventLogSeverity : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Severity", + table: "InvoiceEvents", + nullable: false, + defaultValue: 0); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + if (this.SupportDropColumn(migrationBuilder.ActiveProvider)) + { + migrationBuilder.DropColumn( + name: "Severity", + table: "InvoiceEvents"); + + } + } + } +} diff --git a/BTCPayServer.Data/Migrations/ApplicationDbContextModelSnapshot.cs b/BTCPayServer.Data/Migrations/ApplicationDbContextModelSnapshot.cs index 7964d82b1..4e0ae306e 100644 --- a/BTCPayServer.Data/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/BTCPayServer.Data/Migrations/ApplicationDbContextModelSnapshot.cs @@ -240,6 +240,9 @@ namespace BTCPayServer.Migrations b.Property("Message") .HasColumnType("TEXT"); + b.Property("Severity") + .HasColumnType("INTEGER"); + b.Property("Timestamp") .HasColumnType("TEXT"); diff --git a/BTCPayServer/Controllers/InvoiceController.cs b/BTCPayServer/Controllers/InvoiceController.cs index a9a2a00a4..c4eba1e73 100644 --- a/BTCPayServer/Controllers/InvoiceController.cs +++ b/BTCPayServer/Controllers/InvoiceController.cs @@ -181,7 +181,7 @@ namespace BTCPayServer.Controllers internal async Task CreateInvoiceCoreRaw(InvoiceEntity entity, StoreData store, IPaymentFilter invoicePaymentMethodFilter, CancellationToken cancellationToken = default) { InvoiceLogs logs = new InvoiceLogs(); - logs.Write("Creation of invoice starting"); + logs.Write("Creation of invoice starting", InvoiceEventData.EventSeverity.Info); var getAppsTaggingStore = _InvoiceRepository.GetAppsTaggingStore(store.Id); var storeBlob = store.GetStoreBlob(); @@ -273,7 +273,7 @@ namespace BTCPayServer.Controllers } catch (AggregateException ex) { - ex.Handle(e => { logs.Write($"Error while fetching rates {ex}"); return true; }); + ex.Handle(e => { logs.Write($"Error while fetching rates {ex}", InvoiceEventData.EventSeverity.Error); return true; }); } await _InvoiceRepository.AddInvoiceLogs(entity.Id, logs); }); @@ -286,16 +286,16 @@ namespace BTCPayServer.Controllers return Task.WhenAll(fetchingByCurrencyPair.Select(async pair => { var rateResult = await pair.Value; - logs.Write($"{pair.Key}: The rating rule is {rateResult.Rule}"); - logs.Write($"{pair.Key}: The evaluated rating rule is {rateResult.EvaluatedRule}"); + logs.Write($"{pair.Key}: The rating rule is {rateResult.Rule}", InvoiceEventData.EventSeverity.Info); + logs.Write($"{pair.Key}: The evaluated rating rule is {rateResult.EvaluatedRule}", InvoiceEventData.EventSeverity.Info); if (rateResult.Errors.Count != 0) { var allRateRuleErrors = string.Join(", ", rateResult.Errors.ToArray()); - logs.Write($"{pair.Key}: Rate rule error ({allRateRuleErrors})"); + logs.Write($"{pair.Key}: Rate rule error ({allRateRuleErrors})", InvoiceEventData.EventSeverity.Error); } foreach (var ex in rateResult.ExchangeExceptions) { - logs.Write($"{pair.Key}: Exception reaching exchange {ex.ExchangeName} ({ex.Exception.Message})"); + logs.Write($"{pair.Key}: Exception reaching exchange {ex.ExchangeName} ({ex.Exception.Message})", InvoiceEventData.EventSeverity.Error); } }).ToArray()); } @@ -331,7 +331,7 @@ namespace BTCPayServer.Controllers paymentMethod.Calculate().Due, supportedPaymentMethod.PaymentId); if (!string.IsNullOrEmpty(errorMessage)) { - logs.Write($"{logPrefix} {errorMessage}"); + logs.Write($"{logPrefix} {errorMessage}", InvoiceEventData.EventSeverity.Error); return null; } @@ -348,11 +348,11 @@ namespace BTCPayServer.Controllers } catch (PaymentMethodUnavailableException ex) { - logs.Write($"{supportedPaymentMethod.PaymentId.CryptoCode}: Payment method unavailable ({ex.Message})"); + logs.Write($"{supportedPaymentMethod.PaymentId.CryptoCode}: Payment method unavailable ({ex.Message})", InvoiceEventData.EventSeverity.Error); } catch (Exception ex) { - logs.Write($"{supportedPaymentMethod.PaymentId.CryptoCode}: Unexpected exception ({ex.ToString()})"); + logs.Write($"{supportedPaymentMethod.PaymentId.CryptoCode}: Unexpected exception ({ex.ToString()})", InvoiceEventData.EventSeverity.Error); } return null; } diff --git a/BTCPayServer/HostedServices/InvoiceNotificationManager.cs b/BTCPayServer/HostedServices/InvoiceNotificationManager.cs index 5502104c6..29b7f5080 100644 --- a/BTCPayServer/HostedServices/InvoiceNotificationManager.cs +++ b/BTCPayServer/HostedServices/InvoiceNotificationManager.cs @@ -6,7 +6,9 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using BTCPayServer.Client.Models; +using BTCPayServer.Data; using BTCPayServer.Events; +using BTCPayServer.Logging; using BTCPayServer.Payments; using BTCPayServer.Services; using BTCPayServer.Services.Invoices; @@ -306,7 +308,7 @@ namespace BTCPayServer.HostedServices List tasks = new List(); // Awaiting this later help make sure invoices should arrive in order - tasks.Add(SaveEvent(invoice.Id, e)); + tasks.Add(SaveEvent(invoice.Id, e, InvoiceEventData.EventSeverity.Info)); // we need to use the status in the event and not in the invoice. The invoice might now be in another status. if (invoice.FullNotifications) @@ -337,26 +339,26 @@ namespace BTCPayServer.HostedServices leases.Add(_EventAggregator.Subscribe(async e => { - await SaveEvent(e.InvoiceId, e); + await SaveEvent(e.InvoiceId, e, InvoiceEventData.EventSeverity.Info); })); leases.Add(_EventAggregator.Subscribe(async e => { - await SaveEvent(e.InvoiceId, e); + await SaveEvent(e.InvoiceId, e, InvoiceEventData.EventSeverity.Info); })); leases.Add(_EventAggregator.Subscribe(async e => { - await SaveEvent(e.InvoiceId, e); + await SaveEvent(e.InvoiceId, e, string.IsNullOrEmpty(e.Error)? InvoiceEventData.EventSeverity.Success: InvoiceEventData.EventSeverity.Error); })); return Task.CompletedTask; } - private Task SaveEvent(string invoiceId, object evt) + private Task SaveEvent(string invoiceId, object evt, InvoiceEventData.EventSeverity severity) { - return _InvoiceRepository.AddInvoiceEvent(invoiceId, evt); + return _InvoiceRepository.AddInvoiceEvent(invoiceId, evt, severity); } public Task StopAsync(CancellationToken cancellationToken) diff --git a/BTCPayServer/Logging/InvoiceLog.cs b/BTCPayServer/Logging/InvoiceLog.cs index da72ada20..b217ff7c1 100644 --- a/BTCPayServer/Logging/InvoiceLog.cs +++ b/BTCPayServer/Logging/InvoiceLog.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using BTCPayServer.Data; namespace BTCPayServer.Logging { @@ -8,20 +9,21 @@ namespace BTCPayServer.Logging { public DateTimeOffset Timestamp { get; set; } public string Log { get; set; } - + public InvoiceEventData.EventSeverity Severity { get; set; } public override string ToString() { - return $"{Timestamp.UtcDateTime}: {Log}"; + return $"{Timestamp.UtcDateTime}:{Severity} {Log}"; } + } public class InvoiceLogs { readonly List _InvoiceLogs = new List(); - public void Write(string data) + public void Write(string data, InvoiceEventData.EventSeverity eventSeverity) { lock (_InvoiceLogs) { - _InvoiceLogs.Add(new InvoiceLog() { Timestamp = DateTimeOffset.UtcNow, Log = data }); + _InvoiceLogs.Add(new InvoiceLog() { Timestamp = DateTimeOffset.UtcNow, Log = data, Severity = eventSeverity}); } } @@ -54,11 +56,11 @@ namespace BTCPayServer.Logging var timespan = DateTimeOffset.UtcNow - _Before; if (timespan.TotalSeconds >= 1.0) { - _logs.Write($"{_msg} took {(int)timespan.TotalSeconds} seconds"); + _logs.Write($"{_msg} took {(int)timespan.TotalSeconds} seconds", InvoiceEventData.EventSeverity.Info); } else { - _logs.Write($"{_msg} took {(int)timespan.TotalMilliseconds} milliseconds"); + _logs.Write($"{_msg} took {(int)timespan.TotalMilliseconds} milliseconds", InvoiceEventData.EventSeverity.Info); } } } diff --git a/BTCPayServer/Payments/Bitcoin/BitcoinLikePaymentHandler.cs b/BTCPayServer/Payments/Bitcoin/BitcoinLikePaymentHandler.cs index 0ca412b39..ba1d36711 100644 --- a/BTCPayServer/Payments/Bitcoin/BitcoinLikePaymentHandler.cs +++ b/BTCPayServer/Payments/Bitcoin/BitcoinLikePaymentHandler.cs @@ -171,11 +171,11 @@ namespace BTCPayServer.Payments.Bitcoin ?.CanSupportTransactionCheck is true; onchainMethod.PayjoinEnabled &= supportedPaymentMethod.IsHotWallet && nodeSupport; if (!supportedPaymentMethod.IsHotWallet) - logs.Write($"{prefix} Payjoin should have been enabled, but your store is not a hotwallet"); + logs.Write($"{prefix} Payjoin should have been enabled, but your store is not a hotwallet", InvoiceEventData.EventSeverity.Warning); if (!nodeSupport) - logs.Write($"{prefix} Payjoin should have been enabled, but your version of NBXplorer or full node does not support it."); + logs.Write($"{prefix} Payjoin should have been enabled, but your version of NBXplorer or full node does not support it.", InvoiceEventData.EventSeverity.Warning); if (onchainMethod.PayjoinEnabled) - logs.Write($"{prefix} Payjoin is enabled for this invoice."); + logs.Write($"{prefix} Payjoin is enabled for this invoice.", InvoiceEventData.EventSeverity.Info); } return onchainMethod; diff --git a/BTCPayServer/Payments/PayJoin/PayJoinEndpointController.cs b/BTCPayServer/Payments/PayJoin/PayJoinEndpointController.cs index 98e57abbf..0560e5aa6 100644 --- a/BTCPayServer/Payments/PayJoin/PayJoinEndpointController.cs +++ b/BTCPayServer/Payments/PayJoin/PayJoinEndpointController.cs @@ -5,6 +5,7 @@ using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; +using BTCPayServer.Data; using BTCPayServer.Events; using BTCPayServer.Filters; using BTCPayServer.HostedServices; @@ -141,7 +142,7 @@ namespace BTCPayServer.Payments.PayJoin await using var ctx = new PayjoinReceiverContext(_invoiceRepository, _explorerClientProvider.GetExplorerClient(network), _payJoinRepository); ObjectResult CreatePayjoinErrorAndLog(int httpCode, PayjoinReceiverWellknownErrors err, string debug) { - ctx.Logs.Write($"Payjoin error: {debug}"); + ctx.Logs.Write($"Payjoin error: {debug}", InvoiceEventData.EventSeverity.Error); return StatusCode(httpCode, CreatePayjoinError(err, debug)); } var explorer = _explorerClientProvider.GetExplorerClient(network); diff --git a/BTCPayServer/Services/Invoices/InvoiceRepository.cs b/BTCPayServer/Services/Invoices/InvoiceRepository.cs index b5d479b80..8c40e40f7 100644 --- a/BTCPayServer/Services/Invoices/InvoiceRepository.cs +++ b/BTCPayServer/Services/Invoices/InvoiceRepository.cs @@ -218,20 +218,19 @@ retry: public async Task AddInvoiceLogs(string invoiceId, InvoiceLogs logs) { - using (var context = _ContextFactory.CreateContext()) + await using var context = _ContextFactory.CreateContext(); + foreach (var log in logs.ToList()) { - foreach (var log in logs.ToList()) + await context.InvoiceEvents.AddAsync(new InvoiceEventData() { - context.InvoiceEvents.Add(new InvoiceEventData() - { - InvoiceDataId = invoiceId, - Message = log.Log, - Timestamp = log.Timestamp, - UniqueId = Encoders.Hex.EncodeData(RandomUtils.GetBytes(10)) - }); - } - await context.SaveChangesAsync().ConfigureAwait(false); + Severity = log.Severity, + InvoiceDataId = invoiceId, + Message = log.Log, + Timestamp = log.Timestamp, + UniqueId = Encoders.Hex.EncodeData(RandomUtils.GetBytes(10)) + }); } + await context.SaveChangesAsync().ConfigureAwait(false); } private string GetDestination(PaymentMethod paymentMethod) @@ -325,23 +324,22 @@ retry: } } - public async Task AddInvoiceEvent(string invoiceId, object evt) + public async Task AddInvoiceEvent(string invoiceId, object evt, InvoiceEventData.EventSeverity severity) { - using (var context = _ContextFactory.CreateContext()) + await using var context = _ContextFactory.CreateContext(); + await context.InvoiceEvents.AddAsync(new InvoiceEventData() { - context.InvoiceEvents.Add(new InvoiceEventData() - { - InvoiceDataId = invoiceId, - Message = evt.ToString(), - Timestamp = DateTimeOffset.UtcNow, - UniqueId = Encoders.Hex.EncodeData(RandomUtils.GetBytes(10)) - }); - try - { - await context.SaveChangesAsync(); - } - catch (DbUpdateException) { } // Probably the invoice does not exists anymore + Severity = severity, + InvoiceDataId = invoiceId, + Message = evt.ToString(), + Timestamp = DateTimeOffset.UtcNow, + UniqueId = Encoders.Hex.EncodeData(RandomUtils.GetBytes(10)) + }); + try + { + await context.SaveChangesAsync(); } + catch (DbUpdateException) { } // Probably the invoice does not exists anymore } private static void MarkUnassigned(string invoiceId, InvoiceEntity entity, ApplicationDbContext context, PaymentMethodId paymentMethodId) diff --git a/BTCPayServer/Views/Invoice/Invoice.cshtml b/BTCPayServer/Views/Invoice/Invoice.cshtml index f1dfdd4f4..fc0a32a84 100644 --- a/BTCPayServer/Views/Invoice/Invoice.cshtml +++ b/BTCPayServer/Views/Invoice/Invoice.cshtml @@ -221,7 +221,7 @@ @foreach (var evt in Model.Events) { - + @evt.Timestamp.ToBrowserDate() @evt.Message