Skip to content

Commit

Permalink
Merge pull request #524 from serilog-mssql/dev
Browse files Browse the repository at this point in the history
Release 6.6.0
  • Loading branch information
ckadluba authored Feb 23, 2024
2 parents 5ba23fc + e7ea121 commit 8feab2d
Show file tree
Hide file tree
Showing 24 changed files with 303 additions and 145 deletions.
3 changes: 3 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# 6.6.0
* Fixed issue #509: Add SqlInsertStatementWriter which uses INSERT statements instead of SqlBulkCopy (thanks to @BrettJaner)

# 6.5.2
* Fixed issue #517: Updated Microsoft.Data.SqlClient to 5.1.5 to fix CVE-2024-21319

Expand Down
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ Basic settings of the sink are configured using the properties in a `MSSqlServer
* `BatchPeriod`
* `EagerlyEmitFirstEvent`
* `LevelSwitch`
* `UseSqlBulkCopy`

### TableName

Expand Down Expand Up @@ -291,6 +292,11 @@ This setting is not used by the audit sink as it writes each event immediately a

A switch allowing the pass-through minimum level to be changed at runtime. If this is set, the parameter `restrictedToMinimumLevel` in the [sink configuration method](#sink-configuration) is ignored.

### UseSqlBulkCopy

A flag to use `SqlBulkCopy` instead of individual INSERT statements when writing log events. The default is `true`.
This setting is not used by the audit sink as it always uses INSERT statements to write events.

## ColumnOptions Object

Features of the log table are defined by changing properties on a `ColumnOptions` object:
Expand Down Expand Up @@ -319,7 +325,7 @@ Setting this to `true` changes the table to the clustered columnstore index (CCI

### DisableTriggers

Disabling triggers can significantly improve batch-write performance.
Disabling triggers can significantly improve batch-write performance. Only applies when `SqlBulkCopy` is used.

### AdditionalColumns

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2023 Serilog Contributors
// Copyright 2024 Serilog Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2023 Serilog Contributors
// Copyright 2024 Serilog Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ private static void ReadBatchSettings(IConfigurationSection config, MSSqlServerS
SetProperty.IfNotNull<int>(config["batchPostingLimit"], val => sinkOptions.BatchPostingLimit = val);
SetProperty.IfNotNull<string>(config["batchPeriod"], val => sinkOptions.BatchPeriod = TimeSpan.Parse(val, CultureInfo.InvariantCulture));
SetProperty.IfNotNull<bool>(config["eagerlyEmitFirstEvent"], val => sinkOptions.EagerlyEmitFirstEvent = val);
SetProperty.IfNotNull<bool>(config["useSqlBulkCopy"], val => sinkOptions.UseSqlBulkCopy = val);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,12 @@ public ValueConfigElement EagerlyEmitFirstEvent
{
get => (ValueConfigElement)base[nameof(EagerlyEmitFirstEvent)];
}

[ConfigurationProperty(nameof(UseSqlBulkCopy))]
public ValueConfigElement UseSqlBulkCopy
{
get => (ValueConfigElement)base[nameof(UseSqlBulkCopy)];
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ private static void ReadBatchSettings(MSSqlServerConfigurationSection config, MS
SetProperty.IfProvided<string>(config.BatchPeriod, nameof(config.BatchPeriod.Value), value => sinkOptions.BatchPeriod = TimeSpan.Parse(value, CultureInfo.InvariantCulture));
SetProperty.IfProvided<bool>(config.EagerlyEmitFirstEvent, nameof(config.EagerlyEmitFirstEvent.Value),
value => sinkOptions.EagerlyEmitFirstEvent = value);
SetProperty.IfProvided<bool>(config.UseSqlBulkCopy, nameof(config.UseSqlBulkCopy.Value),
value => sinkOptions.UseSqlBulkCopy = value);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<Description>A Serilog sink that writes events to Microsoft SQL Server and Azure SQL</Description>
<VersionPrefix>6.5.2</VersionPrefix>
<VersionPrefix>6.6.0</VersionPrefix>
<Authors>Michiel van Oudheusden;Christian Kadluba;Serilog Contributors</Authors>
<TargetFrameworks>netstandard2.0;net462;net472;net6.0</TargetFrameworks>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ public ICollection<StandardColumn> Store

/// <summary>
/// Indicates if triggers should be disabled when inserting log entries.
/// Only applies when SqlBulkCopy is used.
/// </summary>
public bool DisableTriggers { get; set; }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,14 @@ internal static SinkDependencies Create(
sqlCreateDatabaseWriter, sqlConnectionFactoryNoDb),
SqlTableCreator = new SqlTableCreator(
sqlCreateTableWriter, sqlConnectionFactory),
SqlBulkBatchWriter = new SqlBulkBatchWriter(
sinkOptions.TableName, sinkOptions.SchemaName, columnOptions.DisableTriggers,
sqlConnectionFactory, logEventDataGenerator),
SqlLogEventWriter = new SqlLogEventWriter(
SqlBulkBatchWriter = sinkOptions.UseSqlBulkCopy
? (ISqlBulkBatchWriter)new SqlBulkBatchWriter(
sinkOptions.TableName, sinkOptions.SchemaName, columnOptions.DisableTriggers,
sqlConnectionFactory, logEventDataGenerator)
: (ISqlBulkBatchWriter)new SqlInsertStatementWriter(
sinkOptions.TableName, sinkOptions.SchemaName,
sqlConnectionFactory, logEventDataGenerator),
SqlLogEventWriter = new SqlInsertStatementWriter(
sinkOptions.TableName, sinkOptions.SchemaName,
sqlConnectionFactory, logEventDataGenerator)
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2023 Serilog Contributors
// Copyright 2024 Serilog Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2023 Serilog Contributors
// Copyright 2024 Serilog Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public MSSqlServerSinkOptions()
BatchPostingLimit = MSSqlServerSink.DefaultBatchPostingLimit;
BatchPeriod = MSSqlServerSink.DefaultPeriod;
EagerlyEmitFirstEvent = true;
UseSqlBulkCopy = true;
}

internal MSSqlServerSinkOptions(
Expand Down Expand Up @@ -77,5 +78,10 @@ internal MSSqlServerSinkOptions(
/// A switch allowing the pass-through minimum level to be changed at runtime
/// </summary>
public LoggingLevelSwitch LevelSwitch { get; set; }

/// <summary>
/// Flag to use <see cref="Microsoft.Data.SqlClient.SqlBulkCopy"/> instead of individual INSERT statements (default: true)
/// </summary>
public bool UseSqlBulkCopy { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2023 Serilog Contributors
// Copyright 2024 Serilog Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2023 Serilog Contributors
// Copyright 2024 Serilog Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Data;
using System.Threading.Tasks;

namespace Serilog.Sinks.MSSqlServer.Platform.SqlClient
{
Expand All @@ -10,5 +11,6 @@ internal interface ISqlCommandWrapper : IDisposable

void AddParameter(string parameterName, object value);
int ExecuteNonQuery();
Task<int> ExecuteNonQueryAsync();
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Data;
using System.Threading.Tasks;
using Microsoft.Data.SqlClient;

namespace Serilog.Sinks.MSSqlServer.Platform.SqlClient
Expand Down Expand Up @@ -43,6 +44,9 @@ public void AddParameter(string parameterName, object value)
public int ExecuteNonQuery() =>
_sqlCommand.ExecuteNonQuery();

public Task<int> ExecuteNonQueryAsync() =>
_sqlCommand.ExecuteNonQueryAsync();

protected virtual void Dispose(bool disposing)
{
if (!_disposedValue)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Text;
using System.Threading.Tasks;
using Serilog.Debugging;
using Serilog.Events;
using Serilog.Sinks.MSSqlServer.Output;
using static System.FormattableString;

namespace Serilog.Sinks.MSSqlServer.Platform
{
internal class SqlInsertStatementWriter : ISqlBulkBatchWriter, ISqlLogEventWriter
{
private readonly string _tableName;
private readonly string _schemaName;
private readonly ISqlConnectionFactory _sqlConnectionFactory;
private readonly ILogEventDataGenerator _logEventDataGenerator;

public SqlInsertStatementWriter(
string tableName,
string schemaName,
ISqlConnectionFactory sqlConnectionFactory,
ILogEventDataGenerator logEventDataGenerator)
{
_tableName = tableName ?? throw new ArgumentNullException(nameof(tableName));
_schemaName = schemaName ?? throw new ArgumentNullException(nameof(schemaName));
_sqlConnectionFactory = sqlConnectionFactory ?? throw new ArgumentNullException(nameof(sqlConnectionFactory));
_logEventDataGenerator = logEventDataGenerator ?? throw new ArgumentNullException(nameof(logEventDataGenerator));
}

public Task WriteBatch(IEnumerable<LogEvent> events, DataTable dataTable) => WriteBatch(events);

public void WriteEvent(LogEvent logEvent) => WriteBatch(new[] { logEvent }).GetAwaiter().GetResult();

public async Task WriteBatch(IEnumerable<LogEvent> events)
{
try
{
using (var cn = _sqlConnectionFactory.Create())
{
await cn.OpenAsync().ConfigureAwait(false);

foreach (var logEvent in events)
{
using (var command = cn.CreateCommand())
{
command.CommandType = CommandType.Text;

var fieldList = new StringBuilder(Invariant($"INSERT INTO [{_schemaName}].[{_tableName}] ("));
var parameterList = new StringBuilder(") VALUES (");

var index = 0;
foreach (var field in _logEventDataGenerator.GetColumnsAndValues(logEvent))
{
if (index != 0)
{
fieldList.Append(',');
parameterList.Append(',');
}

fieldList.Append(Invariant($"[{field.Key}]"));
parameterList.Append("@P");
parameterList.Append(index);

command.AddParameter(Invariant($"@P{index}"), field.Value);

index++;
}

parameterList.Append(')');
fieldList.Append(parameterList);

command.CommandText = fieldList.ToString();

await command.ExecuteNonQueryAsync().ConfigureAwait(false);
}
}
}
}
catch (Exception ex)
{
SelfLog.WriteLine("Unable to write log event to the database due to following error: {0}", ex);
throw;
}
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -149,5 +149,19 @@ public void ConfigureSinkOptionsSetsEagerlyEmitFirstEvent()
// Assert
Assert.True(result.EagerlyEmitFirstEvent);
}

[Fact]
public void ConfigureSinkOptionsSetsUseSqlBulkCopy()
{
// Arrange
_configurationSectionMock.Setup(s => s["useSqlBulkCopy"]).Returns("false");
var sut = new MicrosoftExtensionsSinkOptionsProvider();

// Act
var result = sut.ConfigureSinkOptions(new MSSqlServerSinkOptions(), _configurationSectionMock.Object);

// Assert
Assert.False(result.UseSqlBulkCopy);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,5 +67,21 @@ public void ConfigureSinkOptionsReadsEnlistInTransaction()
// Assert
Assert.True(sinkOptions.EnlistInTransaction);
}

[Fact]
public void ConfigureSinkOptionsReadsUseSqlBulkCopy()
{
// Arrange
var configSection = new MSSqlServerConfigurationSection();
configSection.UseSqlBulkCopy.Value = "false";
var sinkOptions = new MSSqlServerSinkOptions { UseSqlBulkCopy = true };
var sut = new SystemConfigurationSinkOptionsProvider();

// Act
sut.ConfigureSinkOptions(configSection, sinkOptions);

// Assert
Assert.False(sinkOptions.UseSqlBulkCopy);
}
}
}
Loading

0 comments on commit 8feab2d

Please sign in to comment.