Skip to content

Commit

Permalink
✨ log messages for connection, method and query events
Browse files Browse the repository at this point in the history
  • Loading branch information
Odonno committed Aug 14, 2024
1 parent 6c679e3 commit be8f4ad
Show file tree
Hide file tree
Showing 26 changed files with 888 additions and 111 deletions.
71 changes: 67 additions & 4 deletions SurrealDb.Embedded.InMemory/Internals/SurrealDbEngine.InMemory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@
using System.Text.Json;
using System.Threading.Tasks;
using Dahomey.Cbor;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using SurrealDb.Net.Exceptions;
using SurrealDb.Net.Internals;
using SurrealDb.Net.Internals.Cbor;
using SurrealDb.Net.Internals.Constants;
using SurrealDb.Net.Internals.Extensions;
using SurrealDb.Net.Internals.Logging;
using SurrealDb.Net.Internals.Models;
using SurrealDb.Net.Internals.Models.LiveQuery;
using SurrealDb.Net.Internals.Stream;
Expand All @@ -24,8 +27,12 @@ internal class SurrealDbInMemoryEngine : ISurrealDbInMemoryEngine
{
private static int _globalId;

private SurrealDbClientParams? _parameters;
private SurrealDbOptions? _parameters;
private Action<CborOptions>? _configureCborOptions;
private ILogger? _connectionLogger;
private ILogger? _methodLogger;
private ILogger? _queryLogger;

private readonly int _id;
private readonly SurrealDbEmbeddedEngineConfig _config = new();

Expand All @@ -43,12 +50,16 @@ public SurrealDbInMemoryEngine()
}

public void Initialize(
SurrealDbClientParams parameters,
Action<CborOptions>? configureCborOptions
SurrealDbOptions parameters,
Action<CborOptions>? configureCborOptions,
ILoggerFactory? loggerFactory
)
{
_parameters = parameters;
_configureCborOptions = configureCborOptions;
_connectionLogger = loggerFactory?.CreateLogger(DbLoggerCategory.Connection.Name);
_methodLogger = loggerFactory?.CreateLogger(DbLoggerCategory.Method.Name);
_queryLogger = loggerFactory?.CreateLogger(DbLoggerCategory.Query.Name);

if (_parameters.Serialization?.ToLowerInvariant() == SerializationConstants.JSON)
{
Expand Down Expand Up @@ -81,6 +92,8 @@ public async Task Connect(CancellationToken cancellationToken)
{
if (!_isConnected)
{
_connectionLogger?.LogConnectionAttempt(_parameters!.Endpoint!);

var taskCompletionSource = new TaskCompletionSource<bool>();

Action<ByteBuffer> success = (byteBuffer) =>
Expand Down Expand Up @@ -132,6 +145,15 @@ public async Task Connect(CancellationToken cancellationToken)
if (_config.Ns is not null)
{
await Use(_config.Ns, _config.Db!, cancellationToken).ConfigureAwait(false);

if (_config.Db is not null)
{
_connectionLogger?.LogConnectionNamespaceAndDatabaseSet(_config.Ns, _config.Db);
}
else
{
_connectionLogger?.LogConnectionNamespaceSet(_config.Ns);
}
}

_isInitialized = true;
Expand Down Expand Up @@ -373,12 +395,30 @@ public async Task<SurrealDbResponse> RawQuery(
CancellationToken cancellationToken
)
{
if (parameters.Count > 0)
{
_queryLogger?.LogQueryExecute(
query,
SurrealDbLoggerExtensions.FormatQueryParameters(
parameters,
_parameters!.Logging.SensitiveDataLoggingEnabled
)
);
}
else
{
_queryLogger?.LogQueryExecute(query);
}

var list = await SendRequestAsync<List<ISurrealDbResult>>(
Method.Query,
[query, parameters],
cancellationToken
)
.ConfigureAwait(false);

_queryLogger?.LogQuerySuccess();

return new SurrealDbResponse(list);
}

Expand Down Expand Up @@ -602,6 +642,21 @@ CancellationToken cancellationToken
bool requireInitialized = method != Method.Use;
await InternalConnectAsync(requireInitialized, cancellationToken).ConfigureAwait(false);

if (parameters is not null)
{
_methodLogger?.LogMethodExecute(
method.ToString(), // TODO : Avoid ToString()
SurrealDbLoggerExtensions.FormatRequestParameters(
parameters!,
_parameters!.Logging.SensitiveDataLoggingEnabled
)
);
}
else
{
_methodLogger?.LogMethodExecute(method.ToString()); // TODO : Avoid ToString()
}

await using var stream = MemoryStreamProvider.MemoryStreamManager.GetStream();
await CborSerializer
.SerializeAsync(parameters ?? [], stream, GetCborOptions(), cancellationToken)
Expand All @@ -610,6 +665,10 @@ await CborSerializer
bool canGetBuffer = stream.TryGetBuffer(out var bytes);
if (!canGetBuffer)
{
_methodLogger?.LogMethodFailed(
method.ToString(),
"Failed to retrieve serialized buffer."
); // TODO : Avoid ToString()
throw new SurrealDbException("Failed to retrieve serialized buffer.");
}

Expand Down Expand Up @@ -686,6 +745,10 @@ await CborSerializer
}
}

return await taskCompletionSource.Task.ConfigureAwait(false);
var result = await taskCompletionSource.Task.ConfigureAwait(false);

_methodLogger?.LogMethodSuccess(method.ToString()); // TODO : Avoid ToString()

return result;
}
}
21 changes: 14 additions & 7 deletions SurrealDb.Embedded.InMemory/SurrealDbMemoryClient.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.Collections.Immutable;
using Dahomey.Cbor;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using SurrealDb.Embedded.InMemory.Internals;
using SurrealDb.Net;
using SurrealDb.Net.Internals;
Expand All @@ -27,7 +28,7 @@ public class SurrealDbMemoryClient : ISurrealDbClient
/// <param name="namingPolicy">The naming policy to use for serialization.</param>
/// <exception cref="ArgumentException"></exception>
public SurrealDbMemoryClient(string? namingPolicy = null)
: this(new SurrealDbClientParams(ENDPOINT, namingPolicy)) { }
: this(new SurrealDbOptions(ENDPOINT, namingPolicy)) { }

/// <summary>
/// Creates a new SurrealDbMemoryClient using a specific configuration.
Expand All @@ -36,23 +37,29 @@ public SurrealDbMemoryClient(string? namingPolicy = null)
/// <exception cref="ArgumentException"></exception>
/// <exception cref="ArgumentNullException"></exception>
public SurrealDbMemoryClient(SurrealDbOptions configuration)
: this(new SurrealDbClientParams(configuration)) { }
: this(configuration, null) { }

internal SurrealDbMemoryClient(
SurrealDbClientParams parameters,
Action<CborOptions>? configureCborOptions = null
SurrealDbOptions parameters,
Action<CborOptions>? configureCborOptions = null,
ILoggerFactory? loggerFactory = null
)
{
Uri = new Uri(ENDPOINT);
NamingPolicy = parameters.NamingPolicy;

_engine = new SurrealDbInMemoryEngine();
_engine.Initialize(parameters, configureCborOptions);
_engine.Initialize(parameters, configureCborOptions, loggerFactory);

if (parameters.Username is not null)
Configure(parameters.Ns, parameters.Db, parameters.Username, parameters.Password);
Configure(
parameters.Namespace,
parameters.Database,
parameters.Username,
parameters.Password
);
else
Configure(parameters.Ns, parameters.Db, parameters.Token);
Configure(parameters.Namespace, parameters.Database, parameters.Token);
}

public Task Authenticate(Jwt jwt, CancellationToken cancellationToken = default)
Expand Down
18 changes: 16 additions & 2 deletions SurrealDb.Examples.WeatherApi/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
using SurrealDb.Examples.WeatherApi.Models;
using SurrealDb.Net;

// 💡 Be careful, you should only enable this flag if you have the appropriate security measures in place based on the sensitivity of this data
const bool sensitiveDataLoggingEnabled = true;

var builder = WebApplication.CreateBuilder(args);

var services = builder.Services;
Expand All @@ -19,7 +22,13 @@
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
c.IncludeXmlComments(xmlPath);
});
services.AddSurreal(configuration.GetConnectionString("SurrealDB")!);
services.AddSurreal(
SurrealDbOptions
.Create()
.FromConnectionString(configuration.GetConnectionString("SurrealDB")!)
.EnableSensitiveDataLogging(sensitiveDataLoggingEnabled)
.Build()
);

var app = builder.Build();

Expand All @@ -46,7 +55,12 @@ async Task InitializeDbAsync()
SurrealDbOptions
.Create()
.FromConnectionString(configuration.GetConnectionString("SurrealDB")!)
.Build()
.EnableSensitiveDataLogging(sensitiveDataLoggingEnabled)
.Build(),
loggerFactory: LoggerFactory.Create(builder =>
{
builder.AddConsole();
})
);

var tasks = weatherForecasts.Select(weatherForecast =>
Expand Down
2 changes: 1 addition & 1 deletion SurrealDb.Net.Tests/AuthenticateTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public async Task ShouldAuthenticate(string connectionString)

jwt = await client.SignUp(authParams);

await client.Authenticate(jwt);
await client.Authenticate(jwt.Value);

list = await client.Select<Post>("post");
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using SurrealDb.Net.Extensions.DependencyInjection;

namespace SurrealDb.Net.Tests.DependencyInjection;

public class SurrealDbLoggingOptionsTests
{
[Fact]
public void SensitiveDataLoggingShouldBeDisabledByDefault()
{
new SurrealDbLoggingOptions().SensitiveDataLoggingEnabled.Should().BeFalse();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -288,4 +288,23 @@ public void ShouldFailToCreateFromConnectionStringWithClientEndpoint(string clie
.WithParameterName("connectionString")
.WithMessage($"Invalid client endpoint: {client} (Parameter 'connectionString')");
}

[Theory]
[InlineData(true, true)]
[InlineData(false, false)]
public void ShouldSetSensitiveDataLoggingEnabled(bool value, bool expected)
{
var options = new SurrealDbOptionsBuilder().EnableSensitiveDataLogging(value).Build();

options.Endpoint.Should().BeNull();
options.Namespace.Should().BeNull();
options.Database.Should().BeNull();
options.Username.Should().BeNull();
options.Password.Should().BeNull();
options.Token.Should().BeNull();
options.NamingPolicy.Should().BeNull();
options.Serialization.Should().BeNull();
options.Logging.Should().NotBeNull();
options.Logging.SensitiveDataLoggingEnabled.Should().Be(expected);
}
}
77 changes: 77 additions & 0 deletions SurrealDb.Net.Tests/Extensions/SurrealDbLoggerExtensionsTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
using SurrealDb.Net.Internals.Extensions;

namespace SurrealDb.Net.Tests.Extensions;

public class SurrealDbLoggerExtensionsTests
{
[Theory]
[InlineData(null, false, "?")]
[InlineData(null, true, "null")]
[InlineData(true, false, "?")]
[InlineData(true, true, "'True'")]
[InlineData(34, false, "?")]
[InlineData(34, true, "'34'")]
[InlineData("Hello world", false, "?")]
[InlineData("Hello world", true, "'Hello world'")]
public void ShouldFormatParameterValue(
object value,
bool shouldLogParameterValue,
string expected
)
{
SurrealDbLoggerExtensions
.FormatParameterValue(value, shouldLogParameterValue)
.Should()
.Be(expected);
}

[Fact]
public void ShouldFormatRequestParametersWithSensitive()
{
string result = SurrealDbLoggerExtensions.FormatRequestParameters(
[1, "Hello", "test"],
false
);
result.Should().Be("[?, ?, ?]");
}

[Fact]
public void ShouldFormatRequestParametersWithoutSensitive()
{
string result = SurrealDbLoggerExtensions.FormatRequestParameters(
[1, "Hello", "test"],
true
);
result.Should().Be("['1', 'Hello', 'test']");
}

[Fact]
public void ShouldFormatQueryParametersWithSensitive()
{
string result = SurrealDbLoggerExtensions.FormatQueryParameters(
new Dictionary<string, object?>()
{
{ "p0", 1 },
{ "p1", "Hello" },
{ "p2", "test" },
},
false
);
result.Should().Be("$p0=?, $p1=?, $p2=?");
}

[Fact]
public void ShouldFormatQueryParametersWithoutSensitive()
{
string result = SurrealDbLoggerExtensions.FormatQueryParameters(
new Dictionary<string, object?>()
{
{ "p0", 1 },
{ "p1", "Hello" },
{ "p2", "test" },
},
true
);
result.Should().Be("$p0='1', $p1='Hello', $p2='test'");
}
}
24 changes: 24 additions & 0 deletions SurrealDb.Net.Tests/Logging/DbLoggerCategoryTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using SurrealDb.Net.Internals.Logging;

namespace SurrealDb.Net.Tests.Logging;

public class DbLoggerCategoryTests
{
[Fact]
public void ConnectionLoggerCategoryShouldHaveTheCorrectName()
{
DbLoggerCategory.Connection.Name.Should().Be("SurrealDB.Connection");
}

[Fact]
public void MethodLoggerCategoryShouldHaveTheCorrectName()
{
DbLoggerCategory.Method.Name.Should().Be("SurrealDB.Method");
}

[Fact]
public void QueryLoggerCategoryShouldHaveTheCorrectName()
{
DbLoggerCategory.Query.Name.Should().Be("SurrealDB.Query");
}
}
Loading

0 comments on commit be8f4ad

Please sign in to comment.