Merge pull request #1872 from Kukks/invoie-severity

Add invoice event severity
This commit is contained in:
Nicolas Dorier 2020-09-03 21:24:20 +09:00 committed by GitHub
commit 85d393fec3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 110 additions and 51 deletions

View file

@ -20,6 +20,7 @@ namespace BTCPayServer.Data
} }
public string Message { get; set; } public string Message { get; set; }
public EventSeverity Severity { get; set; } = EventSeverity.Info;
internal static void OnModelCreating(ModelBuilder builder) internal static void OnModelCreating(ModelBuilder builder)
{ {
@ -35,5 +36,25 @@ namespace BTCPayServer.Data
#pragma warning restore CS0618 #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
};
}
} }
} }

View file

@ -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<int>(
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");
}
}
}
}

View file

@ -240,6 +240,9 @@ namespace BTCPayServer.Migrations
b.Property<string>("Message") b.Property<string>("Message")
.HasColumnType("TEXT"); .HasColumnType("TEXT");
b.Property<int>("Severity")
.HasColumnType("INTEGER");
b.Property<DateTimeOffset>("Timestamp") b.Property<DateTimeOffset>("Timestamp")
.HasColumnType("TEXT"); .HasColumnType("TEXT");

View file

@ -181,7 +181,7 @@ namespace BTCPayServer.Controllers
internal async Task<InvoiceEntity> CreateInvoiceCoreRaw(InvoiceEntity entity, StoreData store, IPaymentFilter invoicePaymentMethodFilter, CancellationToken cancellationToken = default) internal async Task<InvoiceEntity> CreateInvoiceCoreRaw(InvoiceEntity entity, StoreData store, IPaymentFilter invoicePaymentMethodFilter, CancellationToken cancellationToken = default)
{ {
InvoiceLogs logs = new InvoiceLogs(); 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 getAppsTaggingStore = _InvoiceRepository.GetAppsTaggingStore(store.Id);
var storeBlob = store.GetStoreBlob(); var storeBlob = store.GetStoreBlob();
@ -273,7 +273,7 @@ namespace BTCPayServer.Controllers
} }
catch (AggregateException ex) 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); await _InvoiceRepository.AddInvoiceLogs(entity.Id, logs);
}); });
@ -286,16 +286,16 @@ namespace BTCPayServer.Controllers
return Task.WhenAll(fetchingByCurrencyPair.Select(async pair => return Task.WhenAll(fetchingByCurrencyPair.Select(async pair =>
{ {
var rateResult = await pair.Value; var rateResult = await pair.Value;
logs.Write($"{pair.Key}: The rating rule is {rateResult.Rule}"); logs.Write($"{pair.Key}: The rating rule is {rateResult.Rule}", InvoiceEventData.EventSeverity.Info);
logs.Write($"{pair.Key}: The evaluated rating rule is {rateResult.EvaluatedRule}"); logs.Write($"{pair.Key}: The evaluated rating rule is {rateResult.EvaluatedRule}", InvoiceEventData.EventSeverity.Info);
if (rateResult.Errors.Count != 0) if (rateResult.Errors.Count != 0)
{ {
var allRateRuleErrors = string.Join(", ", rateResult.Errors.ToArray()); 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) 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()); }).ToArray());
} }
@ -331,7 +331,7 @@ namespace BTCPayServer.Controllers
paymentMethod.Calculate().Due, supportedPaymentMethod.PaymentId); paymentMethod.Calculate().Due, supportedPaymentMethod.PaymentId);
if (!string.IsNullOrEmpty(errorMessage)) if (!string.IsNullOrEmpty(errorMessage))
{ {
logs.Write($"{logPrefix} {errorMessage}"); logs.Write($"{logPrefix} {errorMessage}", InvoiceEventData.EventSeverity.Error);
return null; return null;
} }
@ -348,11 +348,11 @@ namespace BTCPayServer.Controllers
} }
catch (PaymentMethodUnavailableException ex) 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) 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; return null;
} }

View file

@ -6,7 +6,9 @@ using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using BTCPayServer.Client.Models; using BTCPayServer.Client.Models;
using BTCPayServer.Data;
using BTCPayServer.Events; using BTCPayServer.Events;
using BTCPayServer.Logging;
using BTCPayServer.Payments; using BTCPayServer.Payments;
using BTCPayServer.Services; using BTCPayServer.Services;
using BTCPayServer.Services.Invoices; using BTCPayServer.Services.Invoices;
@ -306,7 +308,7 @@ namespace BTCPayServer.HostedServices
List<Task> tasks = new List<Task>(); List<Task> tasks = new List<Task>();
// Awaiting this later help make sure invoices should arrive in order // 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. // 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) if (invoice.FullNotifications)
@ -337,26 +339,26 @@ namespace BTCPayServer.HostedServices
leases.Add(_EventAggregator.Subscribe<InvoiceDataChangedEvent>(async e => leases.Add(_EventAggregator.Subscribe<InvoiceDataChangedEvent>(async e =>
{ {
await SaveEvent(e.InvoiceId, e); await SaveEvent(e.InvoiceId, e, InvoiceEventData.EventSeverity.Info);
})); }));
leases.Add(_EventAggregator.Subscribe<InvoiceStopWatchedEvent>(async e => leases.Add(_EventAggregator.Subscribe<InvoiceStopWatchedEvent>(async e =>
{ {
await SaveEvent(e.InvoiceId, e); await SaveEvent(e.InvoiceId, e, InvoiceEventData.EventSeverity.Info);
})); }));
leases.Add(_EventAggregator.Subscribe<InvoiceIPNEvent>(async e => leases.Add(_EventAggregator.Subscribe<InvoiceIPNEvent>(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; 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) public Task StopAsync(CancellationToken cancellationToken)

View file

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using BTCPayServer.Data;
namespace BTCPayServer.Logging namespace BTCPayServer.Logging
{ {
@ -8,20 +9,21 @@ namespace BTCPayServer.Logging
{ {
public DateTimeOffset Timestamp { get; set; } public DateTimeOffset Timestamp { get; set; }
public string Log { get; set; } public string Log { get; set; }
public InvoiceEventData.EventSeverity Severity { get; set; }
public override string ToString() public override string ToString()
{ {
return $"{Timestamp.UtcDateTime}: {Log}"; return $"{Timestamp.UtcDateTime}:{Severity} {Log}";
} }
} }
public class InvoiceLogs public class InvoiceLogs
{ {
readonly List<InvoiceLog> _InvoiceLogs = new List<InvoiceLog>(); readonly List<InvoiceLog> _InvoiceLogs = new List<InvoiceLog>();
public void Write(string data) public void Write(string data, InvoiceEventData.EventSeverity eventSeverity)
{ {
lock (_InvoiceLogs) 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; var timespan = DateTimeOffset.UtcNow - _Before;
if (timespan.TotalSeconds >= 1.0) 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 else
{ {
_logs.Write($"{_msg} took {(int)timespan.TotalMilliseconds} milliseconds"); _logs.Write($"{_msg} took {(int)timespan.TotalMilliseconds} milliseconds", InvoiceEventData.EventSeverity.Info);
} }
} }
} }

View file

@ -171,11 +171,11 @@ namespace BTCPayServer.Payments.Bitcoin
?.CanSupportTransactionCheck is true; ?.CanSupportTransactionCheck is true;
onchainMethod.PayjoinEnabled &= supportedPaymentMethod.IsHotWallet && nodeSupport; onchainMethod.PayjoinEnabled &= supportedPaymentMethod.IsHotWallet && nodeSupport;
if (!supportedPaymentMethod.IsHotWallet) 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) 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) 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; return onchainMethod;

View file

@ -5,6 +5,7 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using BTCPayServer.Data;
using BTCPayServer.Events; using BTCPayServer.Events;
using BTCPayServer.Filters; using BTCPayServer.Filters;
using BTCPayServer.HostedServices; using BTCPayServer.HostedServices;
@ -141,7 +142,7 @@ namespace BTCPayServer.Payments.PayJoin
await using var ctx = new PayjoinReceiverContext(_invoiceRepository, _explorerClientProvider.GetExplorerClient(network), _payJoinRepository); await using var ctx = new PayjoinReceiverContext(_invoiceRepository, _explorerClientProvider.GetExplorerClient(network), _payJoinRepository);
ObjectResult CreatePayjoinErrorAndLog(int httpCode, PayjoinReceiverWellknownErrors err, string debug) 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)); return StatusCode(httpCode, CreatePayjoinError(err, debug));
} }
var explorer = _explorerClientProvider.GetExplorerClient(network); var explorer = _explorerClientProvider.GetExplorerClient(network);

View file

@ -218,20 +218,19 @@ retry:
public async Task AddInvoiceLogs(string invoiceId, InvoiceLogs logs) 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() Severity = log.Severity,
{ InvoiceDataId = invoiceId,
InvoiceDataId = invoiceId, Message = log.Log,
Message = log.Log, Timestamp = log.Timestamp,
Timestamp = log.Timestamp, UniqueId = Encoders.Hex.EncodeData(RandomUtils.GetBytes(10))
UniqueId = Encoders.Hex.EncodeData(RandomUtils.GetBytes(10)) });
});
}
await context.SaveChangesAsync().ConfigureAwait(false);
} }
await context.SaveChangesAsync().ConfigureAwait(false);
} }
private string GetDestination(PaymentMethod paymentMethod) 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() Severity = severity,
{ InvoiceDataId = invoiceId,
InvoiceDataId = invoiceId, Message = evt.ToString(),
Message = evt.ToString(), Timestamp = DateTimeOffset.UtcNow,
Timestamp = DateTimeOffset.UtcNow, UniqueId = Encoders.Hex.EncodeData(RandomUtils.GetBytes(10))
UniqueId = Encoders.Hex.EncodeData(RandomUtils.GetBytes(10)) });
}); try
try {
{ await context.SaveChangesAsync();
await context.SaveChangesAsync();
}
catch (DbUpdateException) { } // Probably the invoice does not exists anymore
} }
catch (DbUpdateException) { } // Probably the invoice does not exists anymore
} }
private static void MarkUnassigned(string invoiceId, InvoiceEntity entity, ApplicationDbContext context, PaymentMethodId paymentMethodId) private static void MarkUnassigned(string invoiceId, InvoiceEntity entity, ApplicationDbContext context, PaymentMethodId paymentMethodId)

View file

@ -221,7 +221,7 @@
<tbody> <tbody>
@foreach (var evt in Model.Events) @foreach (var evt in Model.Events)
{ {
<tr> <tr class="alert alert-@evt.GetCssClass()">
<td>@evt.Timestamp.ToBrowserDate()</td> <td>@evt.Timestamp.ToBrowserDate()</td>
<td>@evt.Message</td> <td>@evt.Message</td>
</tr> </tr>