Skip to content

Commit ea83b81

Browse files
authored
feat(sql-providers): unify SQL query generation (#147)
1 parent 00964cc commit ea83b81

File tree

50 files changed

+1296
-743
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+1296
-743
lines changed

Serilog.Ui.sln.DotSettings

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
2-
<s:Boolean x:Key="/Default/UserDictionary/Words/=Mongo/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
2+
<s:Boolean x:Key="/Default/UserDictionary/Words/=Mongo/@EntryIndexedValue">True</s:Boolean>
3+
<s:Boolean x:Key="/Default/UserDictionary/Words/=Postgre/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
namespace Serilog.Ui.Core.QueryBuilder.Sql;
2+
3+
/// <summary>
4+
/// Represents the column names used in the SQL-based sink for logging.
5+
/// </summary>
6+
public abstract class SinkColumnNames
7+
{
8+
/// <summary>
9+
/// Gets or sets the message of the log entry.
10+
/// </summary>
11+
public string Message { get; set; } = string.Empty;
12+
13+
/// <summary>
14+
/// Gets or sets the message template of the log entry.
15+
/// </summary>
16+
public string MessageTemplate { get; set; } = string.Empty;
17+
18+
/// <summary>
19+
/// Gets or sets the level of the log entry.
20+
/// </summary>
21+
public string Level { get; set; } = string.Empty;
22+
23+
/// <summary>
24+
/// Gets or sets the timestamp of the log entry.
25+
/// </summary>
26+
public string Timestamp { get; set; } = string.Empty;
27+
28+
/// <summary>
29+
/// Gets or sets the exception of the log entry.
30+
/// </summary>
31+
public string Exception { get; set; } = string.Empty;
32+
33+
/// <summary>
34+
/// Gets or sets the serialized log event like properties.
35+
/// </summary>
36+
public string LogEventSerialized { get; set; } = string.Empty;
37+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
using Serilog.Ui.Core.Attributes;
2+
using Serilog.Ui.Core.Models;
3+
using System.Reflection;
4+
using static Serilog.Ui.Core.Models.SearchOptions;
5+
6+
namespace Serilog.Ui.Core.QueryBuilder.Sql;
7+
8+
/// <summary>
9+
/// Abstract class that provides methods to build SQL queries for fetching and counting logs.
10+
/// </summary>
11+
public abstract class SqlQueryBuilder<TModel> where TModel : LogModel
12+
{
13+
/// <summary>
14+
/// Builds a SQL query to fetch logs from the specified table.
15+
/// </summary>
16+
/// <param name="columns">The column names used in the sink for logging.</param>
17+
/// <param name="schema">The schema of the table.</param>
18+
/// <param name="tableName">The name of the table.</param>
19+
/// <param name="query">The query parameters for fetching logs.</param>
20+
/// <returns>A SQL query string to fetch logs.</returns>
21+
public abstract string BuildFetchLogsQuery(SinkColumnNames columns, string schema, string tableName, FetchLogsQuery query);
22+
23+
/// <summary>
24+
/// Builds a SQL query to count logs in the specified table.
25+
/// </summary>
26+
/// <param name="columns">The column names used in the sink for logging.</param>
27+
/// <param name="schema">The schema of the table.</param>
28+
/// <param name="tableName">The name of the table.</param>
29+
/// <param name="query">The query parameters for counting logs.</param>
30+
/// <returns>A SQL query string to count logs.</returns>
31+
public abstract string BuildCountLogsQuery(SinkColumnNames columns, string schema, string tableName, FetchLogsQuery query);
32+
33+
/// <summary>
34+
/// Generates a SQL sort clause based on the specified sort property and direction.
35+
/// </summary>
36+
/// <param name="columns">The column names used in the sink for logging.</param>
37+
/// <param name="sortOn">The property to sort on.</param>
38+
/// <param name="sortBy">The direction to sort by.</param>
39+
/// <returns>A SQL sort clause string.</returns>
40+
protected abstract string GenerateSortClause(SinkColumnNames columns, SortProperty sortOn, SortDirection sortBy);
41+
42+
/// <summary>
43+
/// Generates a SQL sort clause based on the specified sort property and direction.
44+
/// </summary>
45+
/// <param name="columns">The column names used in the sink for logging.</param>
46+
/// <param name="sortOn">The property to sort on.</param>
47+
/// <returns>A SQL sort clause string.</returns>
48+
protected static string GetSortColumnName(SinkColumnNames columns, SortProperty sortOn) => sortOn switch
49+
{
50+
SortProperty.Timestamp => columns.Timestamp,
51+
SortProperty.Level => columns.Level,
52+
SortProperty.Message => columns.Message,
53+
_ => columns.Timestamp
54+
};
55+
56+
/// <summary>
57+
/// Determines whether to add the exception column to the WHERE clause based on the presence of the RemovedColumnAttribute.
58+
/// </summary>
59+
/// <returns>True if the exception column should be added to the WHERE clause; otherwise, false.</returns>
60+
protected static bool AddExceptionToWhereClause()
61+
{
62+
PropertyInfo? exceptionProperty = typeof(TModel).GetProperty("Exception");
63+
RemovedColumnAttribute? att = exceptionProperty?.GetCustomAttribute<RemovedColumnAttribute>();
64+
65+
return att is null;
66+
}
67+
}

src/Serilog.Ui.Core/Serilog.Ui.Core.csproj

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,6 @@
99
<ItemGroup>
1010
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.1" />
1111
<PackageReference Include="Microsoft.Extensions.Primitives" Version="8.0.0" />
12-
<PackageReference Include="System.Text.Json" Version="8.0.4"/>
12+
<PackageReference Include="System.Text.Json" Version="8.0.4" />
1313
</ItemGroup>
1414
</Project>
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,64 @@
1-
using System;
2-
using Dapper;
1+
using Dapper;
32
using Microsoft.Extensions.DependencyInjection;
43
using Serilog.Ui.Core;
54
using Serilog.Ui.Core.Interfaces;
65
using Serilog.Ui.Core.Models.Options;
6+
using System;
77

8-
namespace Serilog.Ui.MsSqlServerProvider.Extensions
9-
{
10-
/// <summary>
11-
/// SQL Server data provider specific extension methods for <see cref="ISerilogUiOptionsBuilder"/>.
12-
/// </summary>
13-
public static class SerilogUiOptionBuilderExtensions
14-
{
15-
/// <summary>Configures the SerilogUi to connect to a SQL Server database.</summary>
16-
/// <param name="optionsBuilder"> The options builder. </param>
17-
/// <param name="setupOptions">The Ms Sql options action.</param>
18-
/// <param name="dateTimeCustomParsing">
19-
/// Delegate to customize the DateTime parsing.
20-
/// It throws <see cref="InvalidOperationException" /> if the return DateTime isn't UTC kind.
21-
/// </param>
22-
public static ISerilogUiOptionsBuilder UseSqlServer(
23-
this ISerilogUiOptionsBuilder optionsBuilder,
24-
Action<RelationalDbOptions> setupOptions,
25-
Func<string, DateTime>? dateTimeCustomParsing = null
26-
) => optionsBuilder.UseSqlServer<SqlServerLogModel>(setupOptions, dateTimeCustomParsing);
27-
28-
/// <summary>Configures the SerilogUi to connect to a SQL Server database.</summary>
29-
/// <typeparam name="T">The log model, containing any additional columns. It must inherit <see cref="SqlServerLogModel"/>.</typeparam>
30-
/// <param name="optionsBuilder"> The options builder. </param>
31-
/// <param name="setupOptions">The Ms Sql options action.</param>
32-
/// <param name="dateTimeCustomParsing">
33-
/// Delegate to customize the DateTime parsing.
34-
/// It throws <see cref="InvalidOperationException" /> if the return DateTime isn't UTC kind.
35-
/// </param>
36-
public static ISerilogUiOptionsBuilder UseSqlServer<T>(
37-
this ISerilogUiOptionsBuilder optionsBuilder,
38-
Action<RelationalDbOptions> setupOptions,
39-
Func<string, DateTime>? dateTimeCustomParsing = null
40-
) where T : SqlServerLogModel
41-
{
42-
var dbOptions = new RelationalDbOptions("dbo");
43-
setupOptions(dbOptions);
44-
dbOptions.Validate();
45-
46-
var providerName = dbOptions.GetProviderName(SqlServerDataProvider.MsSqlProviderName);
8+
namespace Serilog.Ui.MsSqlServerProvider.Extensions;
479

48-
optionsBuilder.RegisterExceptionAsStringForProviderKey(providerName);
49-
SqlMapper.AddTypeHandler(new DapperDateTimeHandler(dateTimeCustomParsing));
10+
/// <summary>
11+
/// SQL Server data provider specific extension methods for <see cref="ISerilogUiOptionsBuilder"/>.
12+
/// </summary>
13+
public static class SerilogUiOptionBuilderExtensions
14+
{
15+
/// <summary>Configures the SerilogUi to connect to a SQL Server database.</summary>
16+
/// <param name="optionsBuilder"> The options builder. </param>
17+
/// <param name="setupOptions">The Ms Sql options action.</param>
18+
/// <param name="dateTimeCustomParsing">
19+
/// Delegate to customize the DateTime parsing.
20+
/// It throws <see cref="InvalidOperationException" /> if the return DateTime isn't UTC kind.
21+
/// </param>
22+
public static ISerilogUiOptionsBuilder UseSqlServer(
23+
this ISerilogUiOptionsBuilder optionsBuilder,
24+
Action<RelationalDbOptions> setupOptions,
25+
Func<string, DateTime>? dateTimeCustomParsing = null
26+
) => optionsBuilder.UseSqlServer<SqlServerLogModel>(setupOptions, dateTimeCustomParsing);
5027

51-
var customModel = typeof(T) != typeof(SqlServerLogModel);
52-
if (customModel)
53-
{
54-
optionsBuilder.RegisterColumnsInfo<T>(providerName);
55-
optionsBuilder.Services.AddScoped<IDataProvider>(_ => new SqlServerDataProvider<T>(dbOptions));
28+
/// <summary>Configures the SerilogUi to connect to a SQL Server database.</summary>
29+
/// <typeparam name="T">The log model, containing any additional columns. It must inherit <see cref="SqlServerLogModel"/>.</typeparam>
30+
/// <param name="optionsBuilder"> The options builder. </param>
31+
/// <param name="setupOptions">The Ms Sql options action.</param>
32+
/// <param name="dateTimeCustomParsing">
33+
/// Delegate to customize the DateTime parsing.
34+
/// It throws <see cref="InvalidOperationException" /> if the return DateTime isn't UTC kind.
35+
/// </param>
36+
public static ISerilogUiOptionsBuilder UseSqlServer<T>(
37+
this ISerilogUiOptionsBuilder optionsBuilder,
38+
Action<RelationalDbOptions> setupOptions,
39+
Func<string, DateTime>? dateTimeCustomParsing = null
40+
) where T : SqlServerLogModel
41+
{
42+
SqlServerDbOptions dbOptions = new("dbo");
43+
setupOptions(dbOptions);
44+
dbOptions.Validate();
5645

57-
return optionsBuilder;
58-
}
46+
string providerName = dbOptions.GetProviderName(SqlServerDataProvider.MsSqlProviderName);
47+
optionsBuilder.RegisterExceptionAsStringForProviderKey(providerName);
48+
SqlMapper.AddTypeHandler(new DapperDateTimeHandler(dateTimeCustomParsing));
5949

60-
optionsBuilder.Services.AddScoped<IDataProvider>(_ => new SqlServerDataProvider(dbOptions));
61-
return optionsBuilder;
50+
bool customModel = typeof(T) != typeof(SqlServerLogModel);
51+
if (customModel)
52+
{
53+
optionsBuilder.RegisterColumnsInfo<T>(providerName);
54+
optionsBuilder.Services.AddScoped<IDataProvider>(_ => new SqlServerDataProvider<T>(dbOptions, new SqlServerQueryBuilder<T>()));
6255
}
56+
else
57+
{
58+
optionsBuilder.Services.AddScoped<IDataProvider>(_ =>
59+
new SqlServerDataProvider(dbOptions, new SqlServerQueryBuilder<SqlServerLogModel>()));
60+
}
61+
62+
return optionsBuilder;
6363
}
6464
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
using Serilog.Ui.Core.Models.Options;
2+
using Serilog.Ui.Core.QueryBuilder.Sql;
3+
using Serilog.Ui.MsSqlServerProvider.Models;
4+
5+
namespace Serilog.Ui.MsSqlServerProvider.Extensions;
6+
7+
public class SqlServerDbOptions(string defaultSchemaName) : RelationalDbOptions(defaultSchemaName)
8+
{
9+
public SinkColumnNames ColumnNames { get; } = new SqlServerSinkColumnNames();
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using Serilog.Ui.Core.QueryBuilder.Sql;
2+
3+
namespace Serilog.Ui.MsSqlServerProvider.Models;
4+
5+
internal class SqlServerSinkColumnNames : SinkColumnNames
6+
{
7+
public SqlServerSinkColumnNames()
8+
{
9+
Exception = "Exception";
10+
Level = "Level";
11+
LogEventSerialized = "Properties";
12+
Message = "Message";
13+
MessageTemplate = "";
14+
Timestamp = "TimeStamp";
15+
}
16+
}

src/Serilog.Ui.MsSqlServerProvider/Serilog.Ui.MsSqlServerProvider.csproj

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
<TargetFramework>netstandard2.0</TargetFramework>
77
<LangVersion>latest</LangVersion>
8-
<Version>3.0.0</Version>
8+
<Version>3.1.0</Version>
99

1010
<Description>Microsoft SQL Server data provider for Serilog UI.</Description>
1111
<PackageTags>serilog serilog-ui serilog.sinks.mssqlserver mssqlserver</PackageTags>
@@ -18,5 +18,6 @@
1818

1919
<ItemGroup>
2020
<ProjectReference Include="..\Serilog.Ui.Core\Serilog.Ui.Core.csproj" PrivateAssets="All" />
21+
<InternalsVisibleTo Include="MsSql.Tests" />
2122
</ItemGroup>
2223
</Project>

0 commit comments

Comments
 (0)