Skip to content

Commit 5eb5ca2

Browse files
committed
Merge branch 'master' into release
2 parents 6e05495 + 53da532 commit 5eb5ca2

7 files changed

Lines changed: 110 additions & 31 deletions

File tree

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ services:
3232
vattenfalldynamicpriceapi:
3333
container_name: vattenfalldynamicpriceapi
3434
image: ghcr.io/rene-sackers/vattenfall-dynamic-price-api:latest
35+
environment:
36+
- TZ=Europe/Amsterdam
3537
ports:
3638
- 8080:8080
3739
```

src/VattenfallDynamicPriceApi/Models/Application/SettingsData.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,13 @@ public class SettingsData
99
public string KnownApiBaseUrl { get; set; } = string.Empty;
1010

1111
public string KnownApiKey { get; set; } = string.Empty;
12+
13+
public LoggingSettings Logging { get; set; } = new();
14+
}
15+
16+
public class LoggingSettings
17+
{
18+
public string LogLevel { get; set; }
19+
20+
public string AspNetLogLevel { get; set; }
1221
}
Lines changed: 60 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,68 @@
1+
using Serilog;
2+
using Serilog.Events;
13
using VattenfallDynamicPriceApi;
24
using VattenfallDynamicPriceApi.Services;
35

4-
var builder = WebApplication.CreateSlimBuilder(args);
6+
try
7+
{
8+
SetUpSerilog();
9+
await RunAppAsync(args);
10+
}
11+
catch (Exception ex)
12+
{
13+
Console.WriteLine("Application crashed on startup: " + ex);
14+
Log.Fatal(ex, "Application crashed on startup");
15+
}
16+
finally
17+
{
18+
await Log.CloseAndFlushAsync();
19+
}
520

6-
builder.Services.ConfigureHttpJsonOptions(options =>
21+
return;
22+
23+
static async Task RunAppAsync(string[] args)
724
{
8-
options.SerializerOptions.TypeInfoResolverChain.Insert(0, SourceGenerationContext.Default);
9-
});
25+
var builder = WebApplication.CreateSlimBuilder(args);
26+
27+
builder.Host.UseSerilog();
28+
29+
builder.Services.ConfigureHttpJsonOptions(options =>
30+
{
31+
options.SerializerOptions.TypeInfoResolverChain.Insert(0, SourceGenerationContext.Default);
32+
});
33+
34+
var app = builder.Build();
35+
var dataService = new VattenfallDataService();
36+
await dataService.InitializeAsync();
37+
38+
var version1Group = app.MapGroup("/v1");
39+
version1Group.MapGet("/data", () => dataService.Data);
40+
version1Group.MapGet("/evcc", () => dataService.EvccData);
41+
version1Group.MapGet("/now/electricity", () => dataService.GetCurrentElectricityTariff());
42+
version1Group.MapGet("/now/gas", () => dataService.GetCurrentGasTariff());
43+
44+
await app.RunAsync();
45+
}
46+
47+
static void SetUpSerilog()
48+
{
49+
if (!Enum.TryParse(SettingsProvider.Instance.Settings.Logging.LogLevel, out LogEventLevel logLevel))
50+
logLevel = LogEventLevel.Warning;
51+
52+
if (!Enum.TryParse(SettingsProvider.Instance.Settings.Logging.AspNetLogLevel, out LogEventLevel aspNetLogLevel))
53+
aspNetLogLevel = LogEventLevel.Warning;
1054

11-
var app = builder.Build();
12-
var dataService = new VattenfallDataService();
13-
await dataService.InitializeAsync();
55+
var loggerConfiguration = new LoggerConfiguration();
1456

15-
var version1Group = app.MapGroup("/v1");
16-
version1Group.MapGet("/data", () => dataService.Data);
17-
version1Group.MapGet("/evcc", () => dataService.EvccData);
18-
version1Group.MapGet("/now/electricity", () => dataService.GetCurrentElectricityTariff());
19-
version1Group.MapGet("/now/gas", () => dataService.GetCurrentGasTariff());
57+
loggerConfiguration
58+
.Enrich.FromLogContext()
59+
.MinimumLevel.Is(logLevel)
60+
.MinimumLevel.Override("Microsoft.Hosting.Lifetime", logLevel)
61+
.MinimumLevel.Override("Microsoft.Hosting", aspNetLogLevel)
62+
.MinimumLevel.Override("Microsoft.AspNetCore", aspNetLogLevel);
63+
64+
loggerConfiguration = loggerConfiguration.WriteTo.Console(
65+
outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}");
2066

21-
await app.RunAsync();
67+
Log.Logger = loggerConfiguration.CreateLogger();
68+
}

src/VattenfallDynamicPriceApi/Services/SettingsProvider.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ public class SettingsProvider : IDisposable
2424
private SettingsProvider()
2525
{
2626
var configurationRoot = new ConfigurationBuilder()
27+
.AddJsonFile($"{SettingsFileName}.json", optional: true, reloadOnChange: true)
28+
.AddJsonFile($"{SettingsFileName}.Development.json", optional: true, reloadOnChange: true)
2729
.AddEnvironmentVariables(prefix: "VFAPI_")
2830
.Build();
2931

src/VattenfallDynamicPriceApi/Services/VattenfallDataService.cs

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,37 @@
11
using System.Net;
22
using System.Text.Json;
33
using System.Text.RegularExpressions;
4+
using Serilog;
45
using VattenfallDynamicPriceApi.Extensions;
56
using VattenfallDynamicPriceApi.Models.Evcc;
67
using VattenfallDynamicPriceApi.Models.Vattenfall;
78

89
namespace VattenfallDynamicPriceApi.Services;
910

10-
public partial class VattenfallDataService
11+
public partial class VattenfallDataService : IDisposable, IAsyncDisposable
1112
{
12-
private static readonly TimeSpan CacheDuration = TimeSpan.FromHours(1);
13+
private static readonly TimeSpan CacheDuration = TimeSpan.FromSeconds(60);
1314

1415
public FlexTariffData[]? Data { get; private set; } = [];
1516

1617
public EvccApiHourlyData[]? EvccData { get; private set; } = [];
18+
19+
private Timer? _timer;
1720

1821
public async Task InitializeAsync()
1922
{
20-
Console.WriteLine("Refresh interval: " + CacheDuration);
23+
Log.Information("Refresh interval: " + CacheDuration);
2124

2225
await UpdateDataAsync();
23-
_ = new Timer(RefreshTimerElapsed, null, CacheDuration, CacheDuration);
26+
_timer = new Timer(RefreshTimerElapsed, null, CacheDuration, CacheDuration);
2427
}
2528

2629
private decimal GetCurrentTariffForProductType(string productType, string description)
2730
{
2831
var productData = Data?.FirstOrDefault(d => d.Product == productType);
2932
if (productData == null)
3033
{
31-
Console.WriteLine($"Could not get current {description} tariff, no data");
34+
Log.Error("Could not get current {Description} tariff, no data", description);
3235
return 999;
3336
}
3437

@@ -38,7 +41,7 @@ private decimal GetCurrentTariffForProductType(string productType, string descri
3841
return currentTariff.AmountInclVat;
3942

4043
var highestTariff = productData.TariffData.Max(t => t.AmountInclVat);
41-
Console.WriteLine($"Could not get current {description} tariff, no value found for current time, returning highest value:" + highestTariff);
44+
Log.Error("Could not get current {Description} tariff, no value found for current time, returning highest value: {HighestTariff}", description, highestTariff);
4245

4346
return highestTariff;
4447
}
@@ -53,13 +56,13 @@ private void RefreshTimerElapsed(object? _)
5356
{
5457
try
5558
{
56-
Console.WriteLine("Updating data");
59+
Log.Information("Updating data");
5760
Task.Run(UpdateDataAsync).Wait();
58-
Console.WriteLine("Updated data");
61+
Log.Information("Updated data");
5962
}
6063
catch (Exception e)
6164
{
62-
Console.WriteLine("Failed to update data: " + e);
65+
Log.Error(e, "Failed to update data");
6366
}
6467
}
6568

@@ -71,7 +74,7 @@ private async Task UpdateDataAsync()
7174
var electricityData = Data.FirstOrDefault(d => d.Product == "E");
7275
if (electricityData == null)
7376
{
74-
Console.WriteLine("Could not find electricity data in API response");
77+
Log.Error("Could not find electricity data in API response");
7578
return;
7679
}
7780

@@ -110,7 +113,7 @@ private static async Task<FlexTariffData[]> GetFlexTariffDataAsync(string apiBas
110113
}
111114
catch (Exception e)
112115
{
113-
Console.WriteLine("Failed to get API URL and key dynamically, falling back to known values: " + e);
116+
Log.Error(e, "Failed to get API URL and key dynamically, falling back to known values");
114117
return (SettingsProvider.Instance.Settings.KnownApiBaseUrl, SettingsProvider.Instance.Settings.KnownApiKey);
115118
}
116119
}
@@ -133,22 +136,22 @@ private static async Task<FlexTariffData[]> GetFlexTariffDataAsync(string apiBas
133136
if (string.IsNullOrWhiteSpace(scriptUrl) || !Uri.IsWellFormedUriString(scriptUrl, UriKind.Absolute))
134137
throw new Exception("Could not find the epi-es2015.js script URL");
135138

136-
Console.WriteLine("Found epi JS script: " + scriptUrl);
139+
Log.Information("Found epi JS script: {Url}", scriptUrl);
137140

138141
// Find API base URL in page script
139142
var js = await httpClient.GetStringAsync(scriptUrl);
140143
var apiBaseUrl = ApiBaseUrlRegex().Match(js).Groups["url"].Value.TrimEnd('/');
141144
if (string.IsNullOrWhiteSpace(apiBaseUrl))
142145
throw new Exception("Could not find the API base URL");
143146

144-
Console.WriteLine("API base URL: " + apiBaseUrl);
147+
Log.Information("API base URL: {ApiBaseUrl}", apiBaseUrl);
145148

146149
// Find API key in page script
147150
var apiKey = TariffApiKeyRegex().Match(js).Groups["key"].Value;
148151
if (string.IsNullOrWhiteSpace(apiKey))
149152
throw new Exception("Could not find the API key");
150153

151-
Console.WriteLine("API key: " + apiKey);
154+
Log.Information("API key: {ApiKey}", apiKey);
152155

153156
// Update known values
154157
SettingsProvider.Instance.Settings.KnownApiBaseUrl = apiBaseUrl;
@@ -165,4 +168,15 @@ private static async Task<FlexTariffData[]> GetFlexTariffDataAsync(string apiBas
165168

166169
[GeneratedRegex(@"ocpApimSubscriptionFeaturesDynamicTariffsKey:""(?<key>[^""]*)")]
167170
private static partial Regex TariffApiKeyRegex();
171+
172+
public void Dispose()
173+
{
174+
_timer?.Dispose();
175+
}
176+
177+
public async ValueTask DisposeAsync()
178+
{
179+
if (_timer != null)
180+
await _timer.DisposeAsync();
181+
}
168182
}

src/VattenfallDynamicPriceApi/VattenfallDynamicPriceApi.csproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,10 @@
1818
</Content>
1919
</ItemGroup>
2020

21+
<ItemGroup>
22+
<PackageReference Include="Serilog" Version="4.3.0" />
23+
<PackageReference Include="Serilog.AspNetCore" Version="10.0.0" />
24+
<PackageReference Include="Serilog.Sinks.Console" Version="6.1.1" />
25+
</ItemGroup>
26+
2127
</Project>
Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
{
22
"Logging": {
3-
"LogLevel": {
4-
"Default": "Information",
5-
"Microsoft.AspNetCore": "Warning"
6-
}
3+
"LogLevel": "Information",
4+
"AspNetLogLevel": "Warning"
75
},
6+
"RefreshIntervalSeconds": 3600,
87
"AllowedHosts": "*"
98
}

0 commit comments

Comments
 (0)