Skip to content

Commit 6151fa7

Browse files
authored
Merge pull request #66 from sommmen/feature/multi-log-table-support
feat: Added multi-log table support
2 parents cfb4585 + 5e44d6d commit 6151fa7

File tree

35 files changed

+585
-249
lines changed

35 files changed

+585
-249
lines changed

samples/SampleWebApp/Controllers/HomeController.cs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
11
using Microsoft.AspNetCore.Mvc;
22
using Microsoft.Extensions.Logging;
33
using SampleWebApp.Models;
4-
using System;
5-
using System.Collections.Generic;
64
using System.Diagnostics;
7-
using System.Linq;
8-
using System.Threading.Tasks;
95

106
namespace SampleWebApp.Controllers
117
{

samples/SampleWebApp/Data/ApplicationDbContext.cs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
22
using Microsoft.EntityFrameworkCore;
3-
using System;
4-
using System.Collections.Generic;
5-
using System.Text;
63

74
namespace SampleWebApp.Data
85
{

samples/SampleWebApp/Models/ErrorViewModel.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
using System;
2-
31
namespace SampleWebApp.Models
42
{
53
public class ErrorViewModel
Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,4 @@
1-
{
2-
"iisSettings": {
3-
"windowsAuthentication": false,
4-
"anonymousAuthentication": true,
5-
"iisExpress": {
6-
"applicationUrl": "http://localhost:7501",
7-
"sslPort": 44377
8-
}
9-
},
1+
{
102
"profiles": {
113
"IIS Express": {
124
"commandName": "IISExpress",
@@ -17,12 +9,21 @@
179
},
1810
"SampleWebApp": {
1911
"commandName": "Project",
20-
"dotnetRunMessages": "true",
2112
"launchBrowser": true,
22-
"applicationUrl": "https://localhost:5001;http://localhost:5000",
13+
"launchUrl": "http://localhost:7501/serilog-ui",
2314
"environmentVariables": {
2415
"ASPNETCORE_ENVIRONMENT": "Development"
25-
}
16+
},
17+
"dotnetRunMessages": "true",
18+
"applicationUrl": "http://localhost:44377;http://localhost:7501"
19+
}
20+
},
21+
"iisSettings": {
22+
"windowsAuthentication": false,
23+
"anonymousAuthentication": true,
24+
"iisExpress": {
25+
"applicationUrl": "http://localhost:7501",
26+
"sslPort": 44377
2627
}
2728
}
28-
}
29+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
using System;
2+
using System.Diagnostics;
3+
using System.Threading;
4+
using System.Threading.Tasks;
5+
using Microsoft.Extensions.Configuration;
6+
using Microsoft.Extensions.Hosting;
7+
using Serilog;
8+
using Serilog.Events;
9+
10+
namespace SampleWebApp.Services.HostedServices;
11+
12+
/// <summary>
13+
/// A background service that generates some dummy logs every 5 seconds.
14+
/// </summary>
15+
public class SecondLogDummyLogGeneratorBackgroundService : BackgroundService
16+
{
17+
private readonly IConfiguration _configuration;
18+
19+
public SecondLogDummyLogGeneratorBackgroundService(IConfiguration configuration)
20+
{
21+
_configuration = configuration;
22+
}
23+
24+
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
25+
{
26+
// Create the logger
27+
var logger = new LoggerConfiguration()
28+
.ReadFrom
29+
.Configuration(_configuration, "Serilog2")
30+
.CreateLogger()
31+
.ForContext<SecondLogDummyLogGeneratorBackgroundService>();
32+
33+
var rand = new Random();
34+
35+
// Lets not generate too much data per run.
36+
const int limit = 10;
37+
38+
var count = 0;
39+
while (++count <= limit)
40+
{
41+
// Stop when the application is stopping.
42+
stoppingToken.ThrowIfCancellationRequested();
43+
44+
// Get a random level
45+
var level = (LogEventLevel) rand.NextInt64((long)LogEventLevel.Fatal + 1L);
46+
47+
// Get a random number
48+
var n = rand.NextInt64(0, 101);
49+
50+
Exception exception = null;
51+
52+
if (level >= LogEventLevel.Error)
53+
{
54+
exception = GetException();
55+
}
56+
57+
logger.Write(level, exception, "Here's a random value: {n}", n);
58+
59+
await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken);
60+
}
61+
62+
}
63+
64+
[DebuggerHidden]
65+
private static Exception GetException()
66+
{
67+
try
68+
{
69+
throw new TestException();
70+
}
71+
catch (Exception e)
72+
{
73+
return e;
74+
}
75+
}
76+
77+
/// <summary>
78+
/// Feel free to disable breaking on this exception in your debugger settings.
79+
/// </summary>
80+
private class TestException : Exception
81+
{
82+
public TestException()
83+
: base("This is a test exception.")
84+
{
85+
}
86+
}
87+
}

samples/SampleWebApp/Startup.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using Microsoft.IdentityModel.Tokens;
1010
using SampleWebApp.Authentication.Jwt;
1111
using SampleWebApp.Data;
12+
using SampleWebApp.Services.HostedServices;
1213
using Serilog.Ui.MsSqlServerProvider;
1314
using Serilog.Ui.Web;
1415

@@ -39,9 +40,14 @@ public void ConfigureServices(IServiceCollection services)
3940
services.AddRazorPages();
4041

4142
services.AddSerilogUi(options => options
42-
.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"), "Logs"));
43+
.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"), "Logs")
44+
.UseSqlServer(Configuration.GetConnectionString("SecondLogConnection"), "Logs2")
45+
);
4346

4447
services.AddSwaggerGen();
48+
49+
// Generate some dummy logs
50+
services.AddHostedService<SecondLogDummyLogGeneratorBackgroundService>();
4551
}
4652

4753
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.

samples/SampleWebApp/appsettings.json

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{
22
"ConnectionStrings": {
3-
"DefaultConnection": "Server=(local);Database=aspnet-SampleWebApp-93B544DE-DDCF-47C1-AD8A-BC87C4D6B954;Trusted_Connection=True;MultipleActiveResultSets=true;TrustServerCertificate=True"
3+
"DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=aspnet-SampleWebApp-93B544DE-DDCF-47C1-AD8A-BC87C4D6B954;Trusted_Connection=True;MultipleActiveResultSets=true",
4+
"SecondLogConnection": "Server=(localdb)\\mssqllocaldb;Database=aspnet-SampleWebApp-93B544DE-DDCF-47C1-AD8A-BC87C4D6B954;Trusted_Connection=True;MultipleActiveResultSets=true"
45
},
56

67
"AllowedHosts": "*",
@@ -34,5 +35,22 @@
3435
}
3536
}
3637
]
38+
},
39+
40+
"Serilog2": {
41+
"Using": [ "Serilog.Sinks.MSSqlServer" ],
42+
"MinimumLevel": "Debug",
43+
"WriteTo": [
44+
{
45+
"Name": "MSSqlServer",
46+
"Args": {
47+
"connectionString": "SecondLogConnection",
48+
"sinkOptionsSection": {
49+
"tableName": "Logs2",
50+
"autoCreateSqlTable": true
51+
}
52+
}
53+
}
54+
]
3755
}
3856
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Threading.Tasks;
5+
6+
namespace Serilog.Ui.Core.Services
7+
{
8+
/// <summary>
9+
/// Aggregates multiple <see cref="IDataProvider"/> into one instance.
10+
/// </summary>
11+
public class AggregateDataProvider : IDataProvider
12+
{
13+
/// <summary>
14+
/// <inheritdoc cref="IDataProvider.Name"/>
15+
/// NOTE We assume only one Aggregate provider, so the name is static.
16+
/// </summary>
17+
public string Name => nameof(AggregateDataProvider);
18+
19+
private IDataProvider _dataProvider;
20+
21+
/// <summary>
22+
/// If there is only one data provider, this is it.
23+
/// If there are multiple, this is the current data provider.
24+
/// </summary>
25+
public IDataProvider DataProvider => _dataProvider;
26+
27+
private readonly Dictionary<string, IDataProvider> _dataProviders = new Dictionary<string, IDataProvider>();
28+
29+
public AggregateDataProvider(IEnumerable<IDataProvider> dataProviders)
30+
{
31+
if (dataProviders == null) throw new ArgumentNullException(nameof(dataProviders));
32+
33+
foreach (var grouped in dataProviders
34+
.GroupBy(c => c.Name, e => e, (k, e) => e.ToList()))
35+
{
36+
var name = grouped[0].Name;
37+
38+
if (grouped.Count == 1)
39+
{
40+
_dataProviders.Add(name, grouped[0]);
41+
}
42+
else
43+
{
44+
// When providers with the same name are registered, we ensure uniqueness by generating a key
45+
// I.e. ["MSSQL.dbo.logs", "MSSQL.dbo.logs"] => ["MSSQL.dbo.logs[0]", "MSSQL.dbo.logs[1]"]
46+
for (var i = 0; i < grouped.Count; i++)
47+
{
48+
var dataProvider = grouped[i];
49+
_dataProviders.Add($"{name}[{i}]", dataProvider);
50+
}
51+
}
52+
}
53+
54+
_dataProvider = _dataProviders.First(c => true).Value;
55+
}
56+
57+
public void SwitchToProvider(string key)
58+
=> _dataProvider = _dataProviders[key];
59+
60+
public IEnumerable<string> Keys => _dataProviders.Keys;
61+
62+
#region Delegating members of IDataProvider
63+
64+
public async Task<(IEnumerable<LogModel>, int)> FetchDataAsync(int page, int count, string level = null, string searchCriteria = null, DateTime? startDate = null, DateTime? endDate = null)
65+
{
66+
return await DataProvider.FetchDataAsync(page, count, level, searchCriteria, startDate, endDate);
67+
}
68+
69+
#endregion
70+
71+
}
72+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
namespace Serilog.Ui.Core.Extensions
2+
{
3+
public static class RelationalDbOptionsExtensions
4+
{
5+
public static string ToDataProviderName(this RelationalDbOptions options, string providerName)
6+
=> string.Join(".", providerName, options.Schema, options.TableName);
7+
}
8+
}

src/Serilog.Ui.Core/IDataProvider.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,5 +27,10 @@ public interface IDataProvider
2727
DateTime? startDate = null,
2828
DateTime? endDate = null
2929
);
30+
31+
/// <summary>
32+
/// Name of the provider, used to identify this provider when using multiple.
33+
/// </summary>
34+
string Name { get; }
3035
}
31-
}
36+
}

0 commit comments

Comments
 (0)