Skip to content

Commit

Permalink
Merge pull request #24 from pyrocumulus/logging
Browse files Browse the repository at this point in the history
Implement logging functionality for the library. Requests, api rate information, services called and even response bodies can be logged now.
  • Loading branch information
pyrocumulus authored Mar 27, 2020
2 parents 801d243 + c894c92 commit b3270d3
Show file tree
Hide file tree
Showing 25 changed files with 702 additions and 120 deletions.
1 change: 1 addition & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ indent_style = space
indent_size = 4
insert_final_newline = true
charset = utf-8-bom
end_of_line = lf

# XML project files
[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}]
Expand Down
20 changes: 20 additions & 0 deletions codecov.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
codecov:
require_ci_to_pass: yes

coverage:
precision: 2
round: down
range: "70..100"

parsers:
gcov:
branch_detection:
conditional: yes
loop: yes
method: no
macro: no

comment:
layout: "header,diff"
behavior: default
require_changes: false
20 changes: 20 additions & 0 deletions docfx/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,26 @@ var response = await client.Status.AddStatusAsync(status);
}
```

### How to log calls made by the library

The client also supports logging through the standard .NET Core ILogger interface.
In a web app or hosted service you can supply the `ILogger` through dependency injection (DI).
For non-host console applications, use the `LoggerFactory` to instantiate the logger and then provide it to the relevant constructor overload.

See the official [Logging in .NET Core and ASP.NET Core](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/logging/?view=aspnetcore-3.1) for more information.

The various logging levels:

- **Information**: this will only the success state of requests that are made with the used service and parameters; including errors.
- **Debug**: also log the exact requested uri (including query parameters), and api rate information.
- **Trace**: also log 'raw' response content, at a minimal overhead (duplicating memory streams).

**NOTE**:

Your API key and owned system ID are always sent through headers, not the request uri. None of the logging levels also log headers and never will, so logging information should never contain those two aspects. However it is always good to inspect logs before sharing them with anyone, including the maintainer of this project.

### API reference

See the [API reference](api/PVOutput.Net.yml) for details on all the classes provided by this library.

## Contribute
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
/// <summary>
/// Options used to create a PVOutputClient through Microsoft's Dependency Injection.
/// </summary>
public class PVOutputServiceOptions
public class PVOutputClientOptions
{
/// <summary>ApiKey to use with authenticating.</summary>
public string ApiKey { get; set; }
Expand Down
16 changes: 12 additions & 4 deletions src/PVOutput.Net/DependencyInjection/PVOutputServiceExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,19 @@ public static class PVOutputServiceExtensions
/// </summary>
/// <param name="services">The servicecollection to add the client to.</param>
/// <param name="optionsAction">An action to configure the provided options.</param>
public static void AddPVOutputClient(this IServiceCollection services, Action<PVOutputServiceOptions> optionsAction)
[System.Diagnostics.CodeAnalysis.SuppressMessage("Globalization", "CA1303:Do not pass literals as localized parameters", Justification = "Exception messages are non translatable for now")]
public static void AddPVOutputClient(this IServiceCollection services, Action<PVOutputClientOptions> optionsAction)
{
var options = new PVOutputServiceOptions();
optionsAction?.Invoke(options);
services.AddSingleton(sp => new PVOutputClient(options.ApiKey, options.OwnedSystemId));
if (optionsAction == null)
{
throw new ArgumentNullException(nameof(optionsAction), "Please provide options to the PVOutputClient.");
}

var options = new PVOutputClientOptions();
optionsAction.Invoke(options);

services.AddSingleton(options);
services.AddSingleton<PVOutputClient>();
}
}
}
18 changes: 16 additions & 2 deletions src/PVOutput.Net/Modules/ExtendedService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Threading.Tasks;
using Dawn;
using PVOutput.Net.Objects;
using PVOutput.Net.Objects.Core;
using PVOutput.Net.Requests.Handler;
using PVOutput.Net.Requests.Modules;
using PVOutput.Net.Responses;
Expand All @@ -30,8 +31,13 @@ internal ExtendedService(PVOutputClient client) : base(client)
/// <returns>Most recent extended data.</returns>
public Task<PVOutputResponse<IExtended>> GetRecentExtendedDataAsync(CancellationToken cancellationToken = default)
{
var loggingScope = new Dictionary<string, object>()
{
[LoggingEvents.RequestId] = LoggingEvents.ExtendedService_GetRecentExtendedData
};

var handler = new RequestHandler(Client);
return handler.ExecuteSingleItemRequestAsync<IExtended>(new ExtendedRequest(), cancellationToken);
return handler.ExecuteSingleItemRequestAsync<IExtended>(new ExtendedRequest(), loggingScope, cancellationToken);
}

/// <summary>
Expand All @@ -45,11 +51,19 @@ public Task<PVOutputResponse<IExtended>> GetRecentExtendedDataAsync(Cancellation
/// <returns>List of extended data objects.</returns>
public Task<PVOutputArrayResponse<IExtended>> GetExtendedDataForPeriodAsync(DateTime fromDate, DateTime toDate, int? limit = null, CancellationToken cancellationToken = default)
{
var loggingScope = new Dictionary<string, object>()
{
[LoggingEvents.RequestId] = LoggingEvents.ExtendedService_GetExtendedDataForPeriod,
[LoggingEvents.Parameter_FromDate] = fromDate,
[LoggingEvents.Parameter_ToDate] = toDate,
[LoggingEvents.Parameter_Limit] = limit
};

Guard.Argument(toDate, nameof(toDate)).GreaterThan(fromDate);
Guard.Argument(limit, nameof(limit)).LessThan(50);

var handler = new RequestHandler(Client);
return handler.ExecuteArrayRequestAsync<IExtended>(new ExtendedRequest() { FromDate = fromDate, ToDate = toDate, Limit = limit }, cancellationToken);
return handler.ExecuteArrayRequestAsync<IExtended>(new ExtendedRequest() { FromDate = fromDate, ToDate = toDate, Limit = limit }, loggingScope, cancellationToken);
}
}
}
9 changes: 8 additions & 1 deletion src/PVOutput.Net/Modules/FavouriteService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Threading;
using System.Threading.Tasks;
using PVOutput.Net.Objects;
using PVOutput.Net.Objects.Core;
using PVOutput.Net.Requests.Handler;
using PVOutput.Net.Requests.Modules;
using PVOutput.Net.Responses;
Expand All @@ -28,8 +29,14 @@ internal FavouriteService(PVOutputClient client) : base(client)
/// <returns>List of favourites.</returns>
public Task<PVOutputArrayResponse<IFavourite>> GetFavouritesAsync(int? systemId = null, CancellationToken cancellationToken = default)
{
var loggingScope = new Dictionary<string, object>()
{
[LoggingEvents.RequestId] = LoggingEvents.FavouriteService_GetFavourites,
[LoggingEvents.Parameter_SystemId] = systemId
};

var handler = new RequestHandler(Client);
return handler.ExecuteArrayRequestAsync<IFavourite>(new FavouriteRequest() { SystemId = systemId }, cancellationToken);
return handler.ExecuteArrayRequestAsync<IFavourite>(new FavouriteRequest() { SystemId = systemId }, loggingScope, cancellationToken);
}
}
}
28 changes: 25 additions & 3 deletions src/PVOutput.Net/Modules/InsolationService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using PVOutput.Net.Objects;
using PVOutput.Net.Objects.Core;
using PVOutput.Net.Requests.Handler;
using PVOutput.Net.Requests.Modules;
using PVOutput.Net.Responses;
Expand All @@ -30,8 +32,14 @@ internal InsolationService(PVOutputClient client) : base(client)
/// <returns>Insolation data for the owned system.</returns>
public Task<PVOutputArrayResponse<IInsolation>> GetInsolationForOwnSystemAsync(DateTime? date = null, CancellationToken cancellationToken = default)
{
var loggingScope = new Dictionary<string, object>()
{
[LoggingEvents.RequestId] = LoggingEvents.InsolationService_GetInsolationForOwnSystem,
[LoggingEvents.Parameter_Date] = date
};

var handler = new RequestHandler(Client);
var response = handler.ExecuteArrayRequestAsync<IInsolation>(new InsolationRequest { Date = date }, cancellationToken);
var response = handler.ExecuteArrayRequestAsync<IInsolation>(new InsolationRequest { Date = date }, loggingScope, cancellationToken);

if (date.HasValue)
{
Expand All @@ -50,8 +58,15 @@ public Task<PVOutputArrayResponse<IInsolation>> GetInsolationForOwnSystemAsync(D
/// <returns>Insolation data for the requested system.</returns>
public Task<PVOutputArrayResponse<IInsolation>> GetInsolationForSystemAsync(int systemId, DateTime? date = null, CancellationToken cancellationToken = default)
{
var loggingScope = new Dictionary<string, object>()
{
[LoggingEvents.RequestId] = LoggingEvents.InsolationService_GetInsolationForSystem,
[LoggingEvents.Parameter_SystemId] = systemId,
[LoggingEvents.Parameter_Date] = date
};

var handler = new RequestHandler(Client);
var response = handler.ExecuteArrayRequestAsync<IInsolation>(new InsolationRequest { SystemId = systemId, Date = date }, cancellationToken);
var response = handler.ExecuteArrayRequestAsync<IInsolation>(new InsolationRequest { SystemId = systemId, Date = date }, loggingScope, cancellationToken);

if (date.HasValue)
{
Expand All @@ -70,8 +85,15 @@ public Task<PVOutputArrayResponse<IInsolation>> GetInsolationForSystemAsync(int
/// <returns>Insolation data for the requested location.</returns>
public Task<PVOutputArrayResponse<IInsolation>> GetInsolationForLocationAsync(PVCoordinate coordinate, DateTime? date = null, CancellationToken cancellationToken = default)
{
var loggingScope = new Dictionary<string, object>()
{
[LoggingEvents.RequestId] = LoggingEvents.InsolationService_GetInsolationForLocation,
[LoggingEvents.Parameter_Coordinate] = coordinate,
[LoggingEvents.Parameter_Date] = date
};

var handler = new RequestHandler(Client);
var response = handler.ExecuteArrayRequestAsync<IInsolation>(new InsolationRequest { Coordinate = coordinate, Date = date }, cancellationToken);
var response = handler.ExecuteArrayRequestAsync<IInsolation>(new InsolationRequest { Coordinate = coordinate, Date = date }, loggingScope, cancellationToken);

if (date.HasValue)
{
Expand Down
11 changes: 10 additions & 1 deletion src/PVOutput.Net/Modules/MissingService.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Dawn;
using PVOutput.Net.Objects;
using PVOutput.Net.Objects.Core;
using PVOutput.Net.Requests.Handler;
using PVOutput.Net.Requests.Modules;
using PVOutput.Net.Responses;
Expand All @@ -29,10 +31,17 @@ internal MissingService(PVOutputClient client) : base(client)
/// <returns>List of missing dates</returns>
public Task<PVOutputResponse<IMissing>> GetMissingDaysInPeriodAsync(DateTime fromDate, DateTime toDate, CancellationToken cancellationToken = default)
{
var loggingScope = new Dictionary<string, object>()
{
[LoggingEvents.RequestId] = LoggingEvents.MissingService_GetMissingDaysInPeriod,
[LoggingEvents.Parameter_FromDate] = fromDate,
[LoggingEvents.Parameter_ToDate] = toDate
};

Guard.Argument(toDate, nameof(toDate)).GreaterThan(fromDate);

var handler = new RequestHandler(Client);
return handler.ExecuteSingleItemRequestAsync<IMissing>(new MissingRequest { FromDate = fromDate, ToDate = toDate }, cancellationToken);
return handler.ExecuteSingleItemRequestAsync<IMissing>(new MissingRequest { FromDate = fromDate, ToDate = toDate }, loggingScope, cancellationToken);
}
}
}
Loading

0 comments on commit b3270d3

Please sign in to comment.