Skip to content

Commit cf797ba

Browse files
committed
Add MySQL data provider.
1 parent 6ff3658 commit cf797ba

File tree

8 files changed

+246
-6
lines changed

8 files changed

+246
-6
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,3 +348,4 @@ MigrationBackup/
348348

349349
# Ionide (cross platform F# VS Code tools) working folder
350350
.ionide/
351+
nuget.exe

README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ _Serilog.UI.MsSqlServerProvider_ [NuGet package](https://www.nuget.org/packages/
1616
Install-Package Serilog.UI.MsSqlServerProvider
1717
```
1818

19+
_Serilog.UI.MySqlProvider_ [NuGet package](https://www.nuget.org/packages/Serilog.UI.MySqlProvider):
20+
21+
```powershell
22+
Install-Package Serilog.UI.MySqlProvider
23+
```
24+
1925
_Serilog.UI.PostgreSqlProvider_ [NuGet package](https://www.nuget.org/packages/Serilog.UI.PostgreSqlProvider):
2026

2127
```powershell
@@ -41,12 +47,6 @@ public void ConfigureServices(IServiceCollection services)
4147
{
4248
// Register the serilog UI services
4349
services.AddSerilogUi(options => options.UseSqlServer("ConnectionString", "LogTableName"));
44-
// or
45-
// services.AddSerilogUi(options => options.UseNpgSql("ConnectionString", "LogTableName"));
46-
// or
47-
// services.AddSerilogUi(options => options.UseMongoDb("ConnectionString", "DatabaseName", "CollectionName"))
48-
// or
49-
// services.AddSerilogUi(options => options.UseElasticSearchDb(endpoint: new System.Uri("http://localhost:9200"), indexName: "logging-index"))
5050
}
5151
```
5252

Serilog.Ui.sln

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SampleWebApp", "samples\Sam
2121
EndProject
2222
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serilog.Ui.ElasticSearchProvider", "src\Serilog.Ui.ElasticSearchProvider\Serilog.Ui.ElasticSearchProvider.csproj", "{4CC537BE-8695-4976-BA43-B8484664C169}"
2323
EndProject
24+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Serilog.Ui.MySqlProvider", "src\Serilog.Ui.MySqlProvider\Serilog.Ui.MySqlProvider.csproj", "{3363E5F5-54A0-413D-807C-D27DBFD52CC8}"
25+
EndProject
2426
Global
2527
GlobalSection(SolutionConfigurationPlatforms) = preSolution
2628
Debug|Any CPU = Debug|Any CPU
@@ -55,6 +57,10 @@ Global
5557
{4CC537BE-8695-4976-BA43-B8484664C169}.Debug|Any CPU.Build.0 = Debug|Any CPU
5658
{4CC537BE-8695-4976-BA43-B8484664C169}.Release|Any CPU.ActiveCfg = Release|Any CPU
5759
{4CC537BE-8695-4976-BA43-B8484664C169}.Release|Any CPU.Build.0 = Release|Any CPU
60+
{3363E5F5-54A0-413D-807C-D27DBFD52CC8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
61+
{3363E5F5-54A0-413D-807C-D27DBFD52CC8}.Debug|Any CPU.Build.0 = Debug|Any CPU
62+
{3363E5F5-54A0-413D-807C-D27DBFD52CC8}.Release|Any CPU.ActiveCfg = Release|Any CPU
63+
{3363E5F5-54A0-413D-807C-D27DBFD52CC8}.Release|Any CPU.Build.0 = Release|Any CPU
5864
EndGlobalSection
5965
GlobalSection(SolutionProperties) = preSolution
6066
HideSolutionNode = FALSE
@@ -67,6 +73,7 @@ Global
6773
{A6195106-0818-4CE1-A889-BB734960DD8D} = {ACA69857-2E3E-468C-B0B0-A86852E3492D}
6874
{30C8AE36-8117-4E52-8140-8440D3C9AD39} = {157CA77C-513A-409F-8045-E68739AAC8C8}
6975
{4CC537BE-8695-4976-BA43-B8484664C169} = {ACA69857-2E3E-468C-B0B0-A86852E3492D}
76+
{3363E5F5-54A0-413D-807C-D27DBFD52CC8} = {ACA69857-2E3E-468C-B0B0-A86852E3492D}
7077
EndGlobalSection
7178
GlobalSection(ExtensibilityGlobals) = postSolution
7279
SolutionGuid = {88374732-FEAD-4375-9CF1-75331A37CF07}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
using Microsoft.Extensions.DependencyInjection;
2+
using Serilog.Ui.Core;
3+
using System;
4+
5+
namespace Serilog.Ui.MySqlProvider.Extensions
6+
{
7+
/// <summary>
8+
/// MySQL data provider specific extension methods for <see cref="SerilogUiOptionsBuilder"/>.
9+
/// </summary>
10+
public static class SerilogUiOptionBuilderExtensions
11+
{
12+
/// <summary>
13+
/// Configures the SerilogUi to connect to a MySQL database.
14+
/// </summary>
15+
/// <param name="optionsBuilder"> The options builder. </param>
16+
/// <param name="connectionString"> The connection string. </param>
17+
/// <param name="tableName"> Name of the table. </param>
18+
/// <exception cref="ArgumentNullException"> throw if connectionString is null </exception>
19+
/// <exception cref="ArgumentNullException"> throw is tableName is null </exception>
20+
public static void UseMySqlServer(
21+
this SerilogUiOptionsBuilder optionsBuilder,
22+
string connectionString,
23+
string tableName
24+
)
25+
{
26+
if (string.IsNullOrEmpty(connectionString))
27+
throw new ArgumentNullException(nameof(connectionString));
28+
29+
if (string.IsNullOrEmpty(tableName))
30+
throw new ArgumentNullException(nameof(tableName));
31+
32+
var relationProvider = new RelationalDbOptions
33+
{
34+
ConnectionString = connectionString,
35+
TableName = tableName
36+
};
37+
38+
((ISerilogUiOptionsBuilder)optionsBuilder).Services.AddSingleton(relationProvider);
39+
((ISerilogUiOptionsBuilder)optionsBuilder).Services.AddScoped<IDataProvider, MySqlDataProvider>();
40+
}
41+
}
42+
}
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
using Dapper;
2+
using MySql.Data.MySqlClient;
3+
using Serilog.Ui.Core;
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Text;
7+
using System.Threading.Tasks;
8+
9+
namespace Serilog.Ui.MySqlProvider
10+
{
11+
public class MySqlDataProvider : IDataProvider
12+
{
13+
private readonly RelationalDbOptions _options;
14+
15+
public MySqlDataProvider(RelationalDbOptions options)
16+
{
17+
_options = options ?? throw new ArgumentNullException(nameof(options));
18+
}
19+
20+
public async Task<(IEnumerable<LogModel>, int)> FetchDataAsync(
21+
int page,
22+
int count,
23+
string logLevel = null,
24+
string searchCriteria = null,
25+
DateTime? startDate = null,
26+
DateTime? endDate = null
27+
)
28+
{
29+
var logsTask = GetLogsAsync(page - 1, count, logLevel, searchCriteria, startDate, endDate);
30+
var logCountTask = CountLogsAsync(logLevel, searchCriteria, startDate, endDate);
31+
32+
await Task.WhenAll(logsTask, logCountTask);
33+
34+
return (await logsTask, await logCountTask);
35+
}
36+
37+
private async Task<IEnumerable<LogModel>> GetLogsAsync(
38+
int page,
39+
int count,
40+
string level,
41+
string searchCriteria,
42+
DateTime? startDate,
43+
DateTime? endDate)
44+
{
45+
var queryBuilder = new StringBuilder();
46+
queryBuilder.Append("SELECT Id, Message, LogLevel AS `Level`, TimeStamp, Exception, Properties From `");
47+
queryBuilder.Append(_options.TableName);
48+
queryBuilder.Append("` ");
49+
50+
GenerateWhereClause(queryBuilder, level, searchCriteria, startDate, endDate);
51+
52+
queryBuilder.Append("ORDER BY Id DESC LIMIT @Offset, @Count");
53+
54+
using (var connection = new MySqlConnection(_options.ConnectionString))
55+
{
56+
var param = new
57+
{
58+
Offset = page * count,
59+
Count = count,
60+
Level = level,
61+
Search = searchCriteria != null ? $"%{searchCriteria}%" : null,
62+
StartDate = startDate,
63+
EndDate = endDate
64+
};
65+
var logs = await connection.QueryAsync<MySqlLogModel>(queryBuilder.ToString(), param);
66+
var index = 1;
67+
foreach (var log in logs)
68+
log.RowNo = (page * count) + index++;
69+
70+
return logs;
71+
}
72+
}
73+
74+
private async Task<int> CountLogsAsync(
75+
string level,
76+
string searchCriteria,
77+
DateTime? startDate = null,
78+
DateTime? endDate = null)
79+
{
80+
var queryBuilder = new StringBuilder();
81+
queryBuilder.Append("SELECT COUNT(Id) FROM `");
82+
queryBuilder.Append(_options.TableName);
83+
queryBuilder.Append("` ");
84+
85+
GenerateWhereClause(queryBuilder, level, searchCriteria, startDate, endDate);
86+
87+
using (var connection = new MySqlConnection(_options.ConnectionString))
88+
{
89+
return await connection.ExecuteScalarAsync<int>(queryBuilder.ToString(),
90+
new
91+
{
92+
Level = level,
93+
Search = searchCriteria != null ? "%" + searchCriteria + "%" : null,
94+
StartDate = startDate,
95+
EndDate = endDate
96+
});
97+
}
98+
}
99+
100+
private void GenerateWhereClause(
101+
StringBuilder queryBuilder,
102+
string level,
103+
string searchCriteria,
104+
DateTime? startDate = null,
105+
DateTime? endDate = null)
106+
{
107+
var whereIncluded = false;
108+
109+
if (!string.IsNullOrEmpty(level))
110+
{
111+
queryBuilder.Append("WHERE LogLevel = @Level ");
112+
whereIncluded = true;
113+
}
114+
115+
if (!string.IsNullOrEmpty(searchCriteria))
116+
{
117+
queryBuilder.Append(whereIncluded
118+
? "AND (Message LIKE @Search OR Exception LIKE @Search) "
119+
: "WHERE (Message LIKE @Search OR Exception LIKE @Search) ");
120+
whereIncluded = true;
121+
}
122+
123+
if (startDate != null)
124+
{
125+
queryBuilder.Append(whereIncluded
126+
? "AND TimeStamp >= @StartDate "
127+
: "WHERE TimeStamp >= @StartDate ");
128+
whereIncluded = true;
129+
}
130+
131+
if (endDate != null)
132+
{
133+
queryBuilder.Append(whereIncluded
134+
? "AND TimeStamp <= @EndDate "
135+
: "WHERE TimeStamp <= @EndDate ");
136+
}
137+
}
138+
}
139+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
using Serilog.Ui.Core;
2+
3+
namespace Serilog.Ui.MySqlProvider
4+
{
5+
internal class MySqlLogModel : LogModel
6+
{
7+
public override string PropertyType => "json";
8+
}
9+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>netstandard2.0</TargetFramework>
5+
</PropertyGroup>
6+
7+
<ItemGroup>
8+
<PackageReference Include="Dapper" Version="2.0.35" />
9+
<PackageReference Include="MySql.Data" Version="8.0.23" />
10+
</ItemGroup>
11+
12+
<ItemGroup>
13+
<ProjectReference Include="..\Serilog.Ui.Core\Serilog.Ui.Core.csproj" />
14+
</ItemGroup>
15+
</Project>
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
3+
<metadata>
4+
<id>Serilog.UI.MySqlProvider</id>
5+
<version>$version$</version>
6+
<title>Serilog.UI.MySqlProvider</title>
7+
<authors>Mohsen Esmailpour</authors>
8+
<owners>mo.esmp</owners>
9+
<projectUrl>https://github.com/mo-esmp/serilog-ui</projectUrl>
10+
<license type="expression">MIT</license>
11+
<requireLicenseAcceptance>false</requireLicenseAcceptance>
12+
<description>MySQL and MariaDB data provider for Serilog UI.</description>
13+
<releaseNotes></releaseNotes>
14+
<copyright></copyright>
15+
<tags>serilog serilog-ui serilog.sinks.mysql serilog.sinks.mariadb</tags>
16+
<dependencies>
17+
<group targetFramework=".NETStandard2.0">
18+
<dependency id="Microsoft.Extensions.DependencyInjection.Abstractions" version="3.1.3" exclude="Build,Analyzers" />
19+
<dependency id="Dapper" version="2.0.35" exclude="Build,Analyzers" />
20+
<dependency id="MySql.Data" version="8.0.23" exclude="Build,Analyzers" />
21+
</group>
22+
</dependencies>
23+
</metadata>
24+
<files>
25+
<file src="bin\Release\netstandard2.0\*.dll" target="lib/netstandard2.0" />
26+
</files>
27+
</package>

0 commit comments

Comments
 (0)