Skip to content

Commit

Permalink
Merge pull request saleem-mirza#4 from saleem-mirza/develop
Browse files Browse the repository at this point in the history
Code improvement
  • Loading branch information
saleem-mirza authored Oct 21, 2016
2 parents f4ee56f + 15b098f commit 247dd13
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,16 @@ public static class LoggerConfigurationSQLiteExtensions
/// <param name="restrictedToMinimumLevel">The minimum log event level required in order to write an event to the sink.</param>
/// <param name="formatProvider">Supplies culture-specific formatting information, or null.</param>
/// <param name="storeTimestampInUtc">Store timestamp in UTC format</param>
/// <param name="retentionPeriod">The maximum time that a log entry will be kept in the database, or null to disable automatic deletion of old log entries. Non-null values smaller than 1 minute will be replaced with 1 minute.</param>
/// <exception cref="ArgumentNullException">A required parameter is null.</exception>
public static LoggerConfiguration SQLite(
this LoggerSinkConfiguration loggerConfiguration,
string sqliteDbPath,
string tableName = "Logs",
LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum,
IFormatProvider formatProvider = null,
bool storeTimestampInUtc = false)
bool storeTimestampInUtc = false,
TimeSpan? retentionPeriod = null)
{
if (loggerConfiguration == null)
{
Expand Down Expand Up @@ -76,7 +78,8 @@ public static LoggerConfiguration SQLite(
sqliteDbFile.FullName,
tableName,
formatProvider,
storeTimestampInUtc),
storeTimestampInUtc,
retentionPeriod),
restrictedToMinimumLevel);
}
catch (Exception ex)
Expand Down
100 changes: 74 additions & 26 deletions src/Serilog.Sinks.SQLite/Sinks/SQLite/SQLiteSink.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,33 +19,43 @@
using Serilog.Core;
using Serilog.Debugging;
using Serilog.Events;
using Serilog.Extensions;
using Serilog.Sinks.Batch;
using System.Diagnostics;
using System.Linq;
using Serilog.Extensions;

namespace Serilog.Sinks.SQLite
{
internal class SQLiteSink : BatchProvider, ILogEventSink
{
private readonly string _connString;
private readonly IFormatProvider _formatProvider;
private readonly string _sqliteDbPath;
private readonly bool _storeTimestampInUtc;
private readonly string _tableName;

private SqliteConnection _sqlConnection;
private readonly TimeSpan? _retentionPeriod;
private readonly Stopwatch _retentionWatch = new Stopwatch();

public SQLiteSink(string sqlLiteDbPath,
string tableName,
IFormatProvider formatProvider,
bool storeTimestampInUtc)
bool storeTimestampInUtc,
TimeSpan? retentionPeriod)
{
_sqliteDbPath = sqlLiteDbPath;
_connString = CreateConnectionString(sqlLiteDbPath);
_tableName = tableName;
_formatProvider = formatProvider;
_storeTimestampInUtc = storeTimestampInUtc;

if (retentionPeriod.HasValue)
// impose a min retention period of 1 minute
_retentionPeriod = new[] { retentionPeriod.Value, TimeSpan.FromMinutes(1) }.Max();

InitializeDatabase();
}

private static string CreateConnectionString(string dbPath) =>
new SqliteConnectionStringBuilder { DataSource = dbPath }.ConnectionString;

#region ILogEvent implementation

public void Emit(LogEvent logEvent)
Expand All @@ -57,14 +67,13 @@ public void Emit(LogEvent logEvent)

private void InitializeDatabase()
{
_sqlConnection = GetSqLiteConnection();

CreateSqlTable(_sqlConnection);
using (var conn = GetSqLiteConnection())
CreateSqlTable(conn);
}

private SqliteConnection GetSqLiteConnection()
{
var sqlConnection = new SqliteConnection($"Data Source={_sqliteDbPath}");
var sqlConnection = new SqliteConnection(_connString);
sqlConnection.Open();
return sqlConnection;
}
Expand Down Expand Up @@ -111,25 +120,29 @@ protected override void WriteLogEvent(ICollection<LogEvent> logEventsBatch)
{
using (var sqlConnection = GetSqLiteConnection())
{
ApplyRetentionPolicy(sqlConnection);

using (var tr = sqlConnection.BeginTransaction())
{
var sqlCommand = CreateSqlInsertCommand(sqlConnection);
sqlCommand.Transaction = tr;

foreach (var logEvent in logEventsBatch)
using (var sqlCommand = CreateSqlInsertCommand(sqlConnection))
{
sqlCommand.Parameters["@timeStamp"].Value = _storeTimestampInUtc
? logEvent.Timestamp.ToUniversalTime()
: logEvent.Timestamp;
sqlCommand.Parameters["@level"].Value = logEvent.Level.ToString();
sqlCommand.Parameters["@exception"].Value = logEvent.Exception?.ToString() ?? string.Empty;
sqlCommand.Parameters["@renderedMessage"].Value = logEvent.MessageTemplate.ToString();

sqlCommand.Parameters["@properties"].Value = logEvent.Properties.Count > 0
? logEvent.Properties.Json()
: string.Empty;

sqlCommand.ExecuteNonQuery();
sqlCommand.Transaction = tr;

foreach (var logEvent in logEventsBatch)
{
sqlCommand.Parameters["@timeStamp"].Value = _storeTimestampInUtc
? logEvent.Timestamp.ToUniversalTime()
: logEvent.Timestamp;
sqlCommand.Parameters["@level"].Value = logEvent.Level.ToString();
sqlCommand.Parameters["@exception"].Value = logEvent.Exception?.ToString() ?? string.Empty;
sqlCommand.Parameters["@renderedMessage"].Value = logEvent.MessageTemplate.ToString();

sqlCommand.Parameters["@properties"].Value = logEvent.Properties.Count > 0
? logEvent.Properties.Json()
: string.Empty;

sqlCommand.ExecuteNonQuery();
}
}
tr.Commit();
}
Expand All @@ -142,5 +155,40 @@ protected override void WriteLogEvent(ICollection<LogEvent> logEventsBatch)
SelfLog.WriteLine(e.Message);
}
}

private void ApplyRetentionPolicy(SqliteConnection sqlConnection)
{
if (!_retentionPeriod.HasValue)
// there is no retention policy
return;

if (_retentionWatch.IsRunning && _retentionWatch.Elapsed < _retentionPeriod.Value)
// Besides deleting records older than X
// let's only delete records every X often
// because of the check whether the _retentionWatch is running,
// the first write operation during this application run
// will result in deleting old records
return;

var epoch = DateTimeOffset.Now.Subtract(_retentionPeriod.Value);
using (var cmd = CreateSqlDeleteCommand(sqlConnection, epoch))
{
SelfLog.WriteLine("Deleting log entries older than {0}", epoch);
cmd.ExecuteNonQuery();
}

_retentionWatch.Restart();
}

private SqliteCommand CreateSqlDeleteCommand(SqliteConnection sqlConnection, DateTimeOffset epoch)
{
var cmd = sqlConnection.CreateCommand();
cmd.CommandText = $"DELETE FROM {_tableName} WHERE Timestamp < @epoch";
cmd.Parameters.Add(new SqliteParameter("@epoch", DbType.DateTime2)
{
Value = _storeTimestampInUtc ? epoch.ToUniversalTime() : epoch
});
return cmd;
}
}
}
2 changes: 1 addition & 1 deletion src/Serilog.Sinks.SQLite/project.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"title": "Serilog.Sinks.SQLite",
"version": "3.5.8-*",
"version": "3.6.5-*",
"description": "Serilog event sink that writes to SQLite database",
"authors": [
"Saleem Mirza"
Expand Down

0 comments on commit 247dd13

Please sign in to comment.