Skip to content

Commit

Permalink
feat: add logger to client (#181)
Browse files Browse the repository at this point in the history
This PR allows the cache client to accept a `ILoggerFactory`
parameter. This is used to create loggers in the cache client itself,
any contained objects, and so on.

We allow the `loggerFactory` to be `null`. In this case we use the
`NullLoggerFactory` singleton to create concrete loggers. Those
loggers will just perform no-ops.
  • Loading branch information
malandis authored Sep 26, 2022
1 parent ca93db7 commit b51a342
Show file tree
Hide file tree
Showing 7 changed files with 48 additions and 11 deletions.
6 changes: 5 additions & 1 deletion src/Momento.Sdk/Internal/ControlGrpcManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
using Grpc.Core;
using Grpc.Core.Interceptors;
using Grpc.Net.Client;
using Microsoft.Extensions.Logging;
using Momento.Protos.ControlClient;
using Momento.Sdk.Internal;
using static System.Reflection.Assembly;

namespace Momento.Sdk.Internal;
Expand All @@ -16,14 +18,16 @@ internal sealed class ControlGrpcManager : IDisposable
// Some System.Environment.Version remarks to be aware of
// https://learn.microsoft.com/en-us/dotnet/api/system.environment.version?view=netstandard-2.0#remarks
private readonly string runtimeVersion = "dotnet:" + System.Environment.Version;
private readonly ILogger _logger;

public ControlGrpcManager(string authToken, string endpoint)
public ControlGrpcManager(string authToken, string endpoint, ILoggerFactory? loggerFactory = null)
{
var uri = $"https://{endpoint}";
this.channel = GrpcChannel.ForAddress(uri, new GrpcChannelOptions() { Credentials = ChannelCredentials.SecureSsl });
List<Header> headers = new List<Header> { new Header(name: Header.AuthorizationKey, value: authToken), new Header(name: Header.AgentKey, value: version), new Header(name: Header.RuntimeVersionKey, value: runtimeVersion) };
CallInvoker invoker = channel.Intercept(new HeaderInterceptor(headers));
Client = new ScsControl.ScsControlClient(invoker);
this._logger = Utils.CreateOrNullLogger<ControlGrpcManager>(loggerFactory);
}

public void Dispose()
Expand Down
6 changes: 5 additions & 1 deletion src/Momento.Sdk/Internal/DataGrpcManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
using Grpc.Core;
using Grpc.Core.Interceptors;
using Grpc.Net.Client;
using Microsoft.Extensions.Logging;
using Momento.Protos.CacheClient;
using Momento.Sdk.Internal;
using static System.Reflection.Assembly;

namespace Momento.Sdk.Internal;
Expand All @@ -17,14 +19,16 @@ public class DataGrpcManager : IDisposable
// Some System.Environment.Version remarks to be aware of
// https://learn.microsoft.com/en-us/dotnet/api/system.environment.version?view=netstandard-2.0#remarks
private readonly string runtimeVersion = "dotnet:" + System.Environment.Version;
private readonly ILogger _logger;

internal DataGrpcManager(string authToken, string host)
internal DataGrpcManager(string authToken, string host, ILoggerFactory? loggerFactory = null)
{
var url = $"https://{host}";
this.channel = GrpcChannel.ForAddress(url, new GrpcChannelOptions() { Credentials = ChannelCredentials.SecureSsl });
List<Header> headers = new List<Header> { new Header(name: Header.AuthorizationKey, value: authToken), new Header(name: Header.AgentKey, value: version), new Header(name: Header.RuntimeVersionKey, value: runtimeVersion) };
CallInvoker invoker = this.channel.Intercept(new HeaderInterceptor(headers));
Client = new Scs.ScsClient(invoker);
this._logger = Utils.CreateOrNullLogger<DataGrpcManager>(loggerFactory);
}

public void Dispose()
Expand Down
7 changes: 5 additions & 2 deletions src/Momento.Sdk/Internal/ScsControlClient.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using Microsoft.Extensions.Logging;
using Momento.Protos.ControlClient;
using Momento.Sdk.Exceptions;
using Momento.Sdk.Responses;
Expand All @@ -10,11 +11,13 @@ internal sealed class ScsControlClient : IDisposable
private readonly ControlGrpcManager grpcManager;
private readonly string authToken;
private const uint DEADLINE_SECONDS = 60;
private readonly ILogger _logger;

public ScsControlClient(string authToken, string endpoint)
public ScsControlClient(string authToken, string endpoint, ILoggerFactory? loggerFactory = null)
{
this.grpcManager = new ControlGrpcManager(authToken, endpoint);
this.grpcManager = new ControlGrpcManager(authToken, endpoint, loggerFactory);
this.authToken = authToken;
this._logger = Utils.CreateOrNullLogger<ScsControlClient>(loggerFactory);
}

public CreateCacheResponse CreateCache(string cacheName)
Expand Down
13 changes: 9 additions & 4 deletions src/Momento.Sdk/Internal/ScsDataClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
using System.Threading.Tasks;
using Google.Protobuf;
using Grpc.Core;
using Microsoft.Extensions.Logging;
using Momento.Protos.CacheClient;
using Momento.Sdk.Exceptions;
using Momento.Sdk.Internal;
using Momento.Sdk.Internal.ExtensionMethods;
using Momento.Sdk.Responses;

Expand All @@ -17,12 +19,14 @@ public class ScsDataClientBase : IDisposable
protected readonly uint defaultTtlSeconds;
protected readonly uint dataClientOperationTimeoutMilliseconds;
protected const uint DEFAULT_DEADLINE_MILLISECONDS = 5000;
protected readonly ILogger _logger;

public ScsDataClientBase(string authToken, string endpoint, uint defaultTtlSeconds, uint? dataClientOperationTimeoutMilliseconds = null)
public ScsDataClientBase(string authToken, string endpoint, uint defaultTtlSeconds, uint? dataClientOperationTimeoutMilliseconds = null, ILoggerFactory? loggerFactory = null)
{
this.grpcManager = new(authToken, endpoint);
this.grpcManager = new(authToken, endpoint, loggerFactory);
this.defaultTtlSeconds = defaultTtlSeconds;
this.dataClientOperationTimeoutMilliseconds = dataClientOperationTimeoutMilliseconds ?? DEFAULT_DEADLINE_MILLISECONDS;
this._logger = Utils.CreateOrNullLogger<ScsDataClient>(loggerFactory);
}

protected Metadata MetadataWithCache(string cacheName)
Expand Down Expand Up @@ -52,9 +56,10 @@ public void Dispose()

internal sealed class ScsDataClient : ScsDataClientBase
{
public ScsDataClient(string authToken, string endpoint, uint defaultTtlSeconds, uint? dataClientOperationTimeoutMilliseconds = null)
: base(authToken, endpoint, defaultTtlSeconds, dataClientOperationTimeoutMilliseconds)
public ScsDataClient(string authToken, string endpoint, uint defaultTtlSeconds, uint? dataClientOperationTimeoutMilliseconds = null, ILoggerFactory? loggerFactory = null)
: base(authToken, endpoint, defaultTtlSeconds, dataClientOperationTimeoutMilliseconds, loggerFactory)
{

}

public async Task<CacheSetResponse> SetAsync(string cacheName, byte[] key, byte[] value, uint? ttlSeconds = null)
Expand Down
13 changes: 13 additions & 0 deletions src/Momento.Sdk/Internal/Utils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
using System.Linq;
using System.Text;
using Google.Protobuf;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;

namespace Momento.Sdk.Internal
{
Expand Down Expand Up @@ -112,6 +114,17 @@ public int GetHashCode(T obj)
/// so comparisons operate on byte-array content instead of reference.
/// </summary>
public static StructuralEqualityComparer<byte[]> ByteArrayComparer = new();

/// <summary>
/// Create a logger using a provided factory or else use <see cref="NullLoggerFactory"/>.
/// </summary>
/// <typeparam name="T">The type for which the logger is scoped.</typeparam>
/// <param name="loggerFactory">The factory to use by default, otherwise <see cref="NullLoggerFactory.Instance"/>.</param>
/// <returns></returns>
public static ILogger<T> CreateOrNullLogger<T>(ILoggerFactory? loggerFactory = null)
{
return (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger<T>();
}
}

namespace ExtensionMethods
Expand Down
1 change: 1 addition & 0 deletions src/Momento.Sdk/Momento.Sdk.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
<PackageReference Include="Google.Protobuf" Version="3.19.0" />
<PackageReference Include="Grpc.Net.Client" Version="2.40.0" />
<PackageReference Include="Grpc.Core" Version="2.41.1" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" />
<PackageReference Include="Momento.Protos" Version="0.31.0" />
<PackageReference Include="JWT" Version="8.4.2" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.16.0" />
Expand Down
13 changes: 10 additions & 3 deletions src/Momento.Sdk/SimpleCacheClient.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Momento.Sdk.Config;
using Momento.Sdk.Exceptions;
using Momento.Sdk.Internal;
Expand All @@ -18,20 +19,26 @@ public class SimpleCacheClient : ISimpleCacheClient
private readonly ScsControlClient controlClient;
private readonly ScsDataClient dataClient;
protected readonly IConfiguration config;
protected readonly ILogger _logger;


/// <summary>
/// Client to perform operations against the Simple Cache Service.
/// </summary>
/// <param name="config">Configuration to use for the transport, retries, middlewares. See <see cref="Configurations"/> for out-of-the-box configuration choices, eg <see cref="Configurations.Laptop.Latest"/></param>
/// <param name="authToken">Momento JWT.</param>
/// <param name="defaultTtlSeconds">Default time to live for the item in cache.</param>
public SimpleCacheClient(IConfiguration config, string authToken, uint defaultTtlSeconds)
/// <param name="loggerFactory">Logger factory to create loggers for contained instances.</param>
public SimpleCacheClient(IConfiguration config, string authToken, uint defaultTtlSeconds, ILoggerFactory? loggerFactory = null)
{
this.config = config;
this._logger = Utils.CreateOrNullLogger<SimpleCacheClient>(loggerFactory);
ValidateRequestTimeout(config.TransportStrategy.GrpcConfig.DeadlineMilliseconds);
Claims claims = JwtUtils.DecodeJwt(authToken);

this.controlClient = new(authToken, claims.ControlEndpoint);
this.dataClient = new(authToken, claims.CacheEndpoint, defaultTtlSeconds, config.TransportStrategy.GrpcConfig.DeadlineMilliseconds);
this.controlClient = new(authToken, claims.ControlEndpoint, loggerFactory);

this.dataClient = new(authToken, claims.CacheEndpoint, defaultTtlSeconds, config.TransportStrategy.GrpcConfig.DeadlineMilliseconds, loggerFactory);
}

/// <inheritdoc />
Expand Down

0 comments on commit b51a342

Please sign in to comment.