Skip to content

Commit 6b45c1a

Browse files
committed
logging
1 parent 978b8f6 commit 6b45c1a

File tree

12 files changed

+151
-9
lines changed

12 files changed

+151
-9
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
namespace PolylineAlgorithm.Abstraction;
22

3+
using Microsoft.Extensions.Logging;
4+
35
public interface IPolylineEncodingOptionsBuilder {
46
IPolylineEncodingOptionsBuilder WithBufferSize(int maxBufferSize);
7+
8+
IPolylineEncodingOptionsBuilder WithLoggerFactory(ILoggerFactory loggerFactory);
9+
510
PolylineEncodingOptions Build();
611
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
namespace PolylineAlgorithm.Abstraction.Internal;
2+
3+
using Microsoft.Extensions.Logging;
4+
5+
internal static partial class Logging {
6+
[LoggerMessage(0, LogLevel.Error, "Argument {argumentName} cannot be null.")]
7+
internal static partial void LogNullArgumentError(this ILogger logger, string argumentName);
8+
9+
[LoggerMessage(0, LogLevel.Error, "Argument {argumentName} cannot be empty.")]
10+
internal static partial void LogEmptyArgumentError(this ILogger logger, string argumentName);
11+
12+
[LoggerMessage(0, LogLevel.Error, "Internal buffer has {bufferLength} length. At position {position} is required additonal {requiredSpace} length.")]
13+
internal static partial void LogInternalBufferOverflowError(this ILogger logger, int position, int bufferLength, int requiredSpace);
14+
15+
[LoggerMessage(0, LogLevel.Error, "Cannot write to internal buffer at position {position}.")]
16+
internal static partial void LogCannotWriteValueToBufferError(this ILogger logger, int position);
17+
18+
[LoggerMessage(0, LogLevel.Error, "Argument {argumentName} is too short. Minimal length is {minimumLength}. Actual length is {actualLength}.")]
19+
internal static partial void LogPolylineCannotBeShorterThanError(this ILogger logger, string argumentName, int actualLength, int minimumLength);
20+
21+
[LoggerMessage(0, LogLevel.Warning, "Requested buffer size of {requestedBufferLength} exceeds maximum allowed buffer length of {maxBufferLength}.")]
22+
internal static partial void LogRequestedBufferSizeExceedsMaxBufferLength(this ILogger logger, int requestedBufferLength, int maxBufferLength);
23+
}

src/PolylineAlgorithm.Abstraction/PolylineAlgorithm.Abstraction.csproj

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99
<NeutralLanguage>en</NeutralLanguage>
1010
</PropertyGroup>
1111

12+
<PropertyGroup>
13+
<EnableConfigurationBindingGenerator>true</EnableConfigurationBindingGenerator>
14+
</PropertyGroup>
15+
1216
<PropertyGroup>
1317
<AnalysisMode>All</AnalysisMode>
1418
<AnalysisLevel>latest</AnalysisLevel>
@@ -49,6 +53,7 @@
4953
<PrivateAssets>all</PrivateAssets>
5054
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
5155
</PackageReference>
56+
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.*" />
5257
</ItemGroup>
5358

5459
<ItemGroup>

src/PolylineAlgorithm.Abstraction/PolylineDecoder.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ public IEnumerable<TCoordinate> Decode(TPolyline polyline) {
5050
ReadOnlyMemory<char> sequence = GetReadOnlyMemory(polyline);
5151

5252
if (sequence.Length < Defaults.Polyline.MinEncodedCoordinateLength) {
53+
Options
54+
.UseLoggerFor<PolylineDecoder<TPolyline, TCoordinate>>()
55+
.LogPolylineCannotBeShorterThanError(nameof(sequence), sequence.Length, Defaults.Polyline.MinEncodedCoordinateLength);
56+
5357
throw new ArgumentException(string.Format(ExceptionMessageResource.PolylineCannotBeShorterThanExceptionMessage, sequence.Length), nameof(polyline));
5458
}
5559

src/PolylineAlgorithm.Abstraction/PolylineEncoder.cs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
namespace PolylineAlgorithm.Abstraction;
77

8+
using Microsoft.Extensions.Logging;
89
using PolylineAlgorithm.Abstraction.Internal;
910
using PolylineAlgorithm.Abstraction.Properties;
1011
using System;
@@ -48,12 +49,17 @@ public PolylineEncoder(PolylineEncodingOptions options) {
4849
/// </exception>
4950
public TPolyline Encode(IEnumerable<TCoordinate> coordinates) {
5051
if (coordinates is null) {
52+
Options
53+
.UseLoggerFor<PolylineEncoder<TCoordinate, TPolyline>>().LogNullArgumentError(nameof(coordinates));
5154
throw new ArgumentNullException(nameof(coordinates));
5255
}
5356

5457
int count = GetCount(coordinates);
5558

5659
if (count == 0) {
60+
Options
61+
.UseLoggerFor<PolylineEncoder<TCoordinate, TPolyline>>().LogEmptyArgumentError(nameof(coordinates));
62+
5763
throw new ArgumentException(ExceptionMessageResource.ArgumentCannotBeEmptyEnumerationMessage, nameof(coordinates));
5864
}
5965

@@ -72,12 +78,16 @@ public TPolyline Encode(IEnumerable<TCoordinate> coordinates) {
7278
.Next(PolylineEncoding.Normalize(GetLatitude(enumerator.Current), PolylineEncoding.ValueType.Latitude), PolylineEncoding.Normalize(GetLongitude(enumerator.Current), PolylineEncoding.ValueType.Longitude));
7379

7480
if (GetRemainingBufferSize(position, buffer.Length) < GetRequiredLength(variance)) {
81+
Options
82+
.UseLoggerFor<PolylineEncoder<TCoordinate, TPolyline>>().LogInternalBufferOverflowError(position, buffer.Length, GetRequiredLength(variance));
7583
throw new InternalBufferOverflowException();
7684
}
7785

7886
if (!PolylineEncoding.TryWriteValue(variance.Latitude, ref buffer, ref position)
7987
|| !PolylineEncoding.TryWriteValue(variance.Longitude, ref buffer, ref position)
8088
) {
89+
Options
90+
.UseLoggerFor<PolylineEncoder<TCoordinate, TPolyline>>().LogCannotWriteValueToBufferError(position);
8191
throw new InvalidOperationException();
8292
}
8393

@@ -101,13 +111,16 @@ static int GetRequiredLength(CoordinateVariance variance) =>
101111

102112
int GetBufferLength(int count) {
103113
int maxBufferLength = Options.BufferSize / sizeof(char);
104-
int? requestedBufferLength = count * Defaults.Polyline.MaxEncodedCoordinateLength;
114+
int requestedBufferLength = count * Defaults.Polyline.MaxEncodedCoordinateLength;
115+
116+
if (requestedBufferLength > maxBufferLength) {
117+
Options
118+
.UseLoggerFor<PolylineEncoder<TCoordinate, TPolyline>>().LogRequestedBufferSizeExceedsMaxBufferLength(requestedBufferLength, maxBufferLength);
105119

106-
if (requestedBufferLength is null || requestedBufferLength > maxBufferLength) {
107120
return maxBufferLength;
108121
}
109122

110-
return requestedBufferLength.Value;
123+
return requestedBufferLength;
111124
}
112125
}
113126

src/PolylineAlgorithm.Abstraction/PolylineEncodingOptions.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55

66
namespace PolylineAlgorithm.Abstraction;
77

8+
using Microsoft.Extensions.Logging;
9+
using Microsoft.Extensions.Logging.Abstractions;
10+
811
/// <summary>
912
/// Options for configuring polyline encoding.
1013
/// </summary>
@@ -16,4 +19,8 @@ public sealed class PolylineEncodingOptions {
1619
public int BufferSize { get; internal set; } = 64_000;
1720

1821
public int MaxLength => BufferSize / sizeof(char);
22+
23+
public ILoggerFactory LoggerFactory { get; internal set; } = NullLoggerFactory.Instance;
24+
25+
public ILogger UseLoggerFor<T>() => LoggerFactory.CreateLogger<T>();
1926
}

src/PolylineAlgorithm.Abstraction/PolylineEncodingOptionsBuilder.cs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,23 @@
55

66
namespace PolylineAlgorithm.Abstraction;
77

8+
using Microsoft.Extensions.Logging;
9+
using Microsoft.Extensions.Logging.Abstractions;
10+
811
public class PolylineEncodingOptionsBuilder : IPolylineEncodingOptionsBuilder {
912
private int _bufferSize = 64_000;
13+
private ILoggerFactory _loggerFactory = NullLoggerFactory.Instance;
1014

1115
/// <summary>
1216
/// Creates a new <see cref="IPolylineEncodingOptionsBuilder"/> instance for the specified coordinate type.
1317
/// </summary>
14-
/// <typeparam name="TCoordinate">The type representing a coordinate.</typeparam>
1518
/// <returns>An <see cref="IPolylineEncodingOptionsBuilder"/> instance for configuring polyline encoding options.</returns>
1619
public static IPolylineEncodingOptionsBuilder Create() {
1720
return new PolylineEncodingOptionsBuilder();
1821
}
1922

2023
/// <summary>
21-
/// Builds a new <see cref="PolylineEncodingOptions{TCoordinate}"/> instance using the configured options.
24+
/// Builds a new <see cref="PolylineEncodingOptions"/> instance using the configured options.
2225
/// </summary>
2326
/// <returns>A configured <see cref="PolylineEncodingOptions"/> instance.</returns>
2427
PolylineEncodingOptions IPolylineEncodingOptionsBuilder.Build() {
@@ -38,4 +41,17 @@ IPolylineEncodingOptionsBuilder IPolylineEncodingOptionsBuilder.WithBufferSize(i
3841

3942
return this;
4043
}
44+
45+
/// <summary>
46+
/// Sets the logger factory for logging during encoding operations.
47+
/// </summary>
48+
/// <param name="loggerFactory">The instance of a logger factory.</param>
49+
/// <returns>The current builder instance.</returns>
50+
/// <exception cref="ArgumentNullException">Thrown if <paramref name="loggerFactory"/> is <see langword="null"/></exception>
51+
IPolylineEncodingOptionsBuilder IPolylineEncodingOptionsBuilder.WithLoggerFactory(ILoggerFactory loggerFactory) {
52+
_loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory), "Logger factory cannot be null.");
53+
54+
return this;
55+
56+
}
4157
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
namespace PolylineAlgorithm.Abstraction.Tests;
2+
3+
using Microsoft.Extensions.Logging;
4+
using Microsoft.Extensions.Logging.Testing;
5+
6+
internal class FakeLoggerFactory : ILoggerFactory {
7+
private bool _isDisposed;
8+
9+
public FakeLoggerFactory(FakeLoggerProvider loggerProvider) {
10+
Provider = new FakeLoggerProvider();
11+
}
12+
13+
public ILoggerProvider Provider { get; private set; }
14+
15+
16+
public void AddProvider(ILoggerProvider provider) {
17+
Provider = provider;
18+
}
19+
20+
public ILogger CreateLogger(string categoryName) {
21+
return Provider.CreateLogger(categoryName);
22+
}
23+
24+
protected virtual void Dispose(bool disposing) {
25+
if (!_isDisposed) {
26+
if (disposing) {
27+
28+
}
29+
30+
// TODO: free unmanaged resources (unmanaged objects) and override finalizer
31+
// TODO: set large fields to null
32+
_isDisposed = true;
33+
}
34+
}
35+
36+
public void Dispose() {
37+
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
38+
Dispose(disposing: true);
39+
GC.SuppressFinalize(this);
40+
}
41+
}

tests/PolylineAlgorithm.Abstraction.Tests/PolylineAlgorithm.Abstraction.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
</PropertyGroup>
3131

3232
<ItemGroup>
33+
<PackageReference Include="Microsoft.Extensions.Diagnostics.Testing" Version="9.6.0" />
3334
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.*" />
3435
<PackageReference Include="MSTest" Version="3.*" />
3536
<PackageReference Include="Microsoft.Testing.Platform.MSBuild" Version="1.*" />
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
namespace PolylineAlgorithm.Abstraction.Tests;
2+
3+
using Microsoft.Extensions.Logging;
4+
using Microsoft.Extensions.Logging.Testing;
5+
using Microsoft.VisualStudio.TestTools.UnitTesting;
6+
using System;
7+
8+
[TestClass]
9+
public class PolylineEncoderTest {
10+
internal static readonly FakeLoggerProvider loggerProvider = new();
11+
12+
[TestMethod]
13+
public void TestEncodeEmptyCollection() {
14+
var encoder = new PolylineEncoder(new() { LoggerFactory = new FakeLoggerFactory(loggerProvider) });
15+
void Encode() => encoder.Encode(Array.Empty<(double Latitude, double Longitude)>());
16+
var exception = Assert.ThrowsExactly<ArgumentException>(Encode, "The input collection cannot be empty.");
17+
///loggerProvider.Collector.LatestRecord,(exception, LogLevel.Error, "Argument cannot be empty enumeration");
18+
}
19+
20+
public class PolylineEncoder : PolylineEncoder<(double Latitude, double Longitude), string> {
21+
public PolylineEncoder(PolylineEncodingOptions options) : base(options) { }
22+
23+
protected override string CreatePolyline(ReadOnlyMemory<char> polyline) => polyline.ToString();
24+
protected override double GetLatitude((double Latitude, double Longitude) coordinate) => coordinate.Latitude;
25+
protected override double GetLongitude((double Latitude, double Longitude) coordinate) => coordinate.Longitude;
26+
}
27+
}

0 commit comments

Comments
 (0)