Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Develop #1848

Merged
merged 4 commits into from
Feb 15, 2025
Merged

Develop #1848

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions PkSoftwareService.Custom.Backend/IInMemorySink.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace PkSoftwareService.Custom.Backend;

public interface IInMemorySink
{
/// <summary>
/// Returns a snapshot of the current log messages.
/// </summary>
List<string> GetLogs();

int GetCapacity();
void UpdateCapacity(int newCapacity);
}
31 changes: 21 additions & 10 deletions PkSoftwareService.Custom.Backend/InMemorySink.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@

namespace PkSoftwareService.Custom.Backend;

public class InMemorySink : ILogEventSink
public class InMemorySink : ILogEventSink, IInMemorySink
{
private readonly int _capacity;
private readonly Queue<string> _logMessages;
private int _capacity;
private Queue<string> _logMessages;
private readonly object _syncRoot = new object();
private readonly MessageTemplateTextFormatter _formatter;

Expand All @@ -17,7 +17,7 @@ public class InMemorySink : ILogEventSink
/// <param name="outputTemplate">The output template (should match your Console sink).</param>
/// <param name="formatProvider">Optional format provider.</param>
/// <param name="capacity">Max number of messages to store.</param>
public InMemorySink(string outputTemplate, IFormatProvider? formatProvider = null, int capacity = 20000)
public InMemorySink(string outputTemplate, int capacity, IFormatProvider? formatProvider = null)
{
_capacity = capacity;
_logMessages = new Queue<string>(capacity);
Expand Down Expand Up @@ -53,14 +53,25 @@ public List<string> GetLogs()
}
}

/// <summary>
/// Optionally clear all logs.
/// </summary>
public void Clear()
public int GetCapacity()
{
lock (_syncRoot)
return _capacity;
}

public void UpdateCapacity(int newCapacity)
{
if (newCapacity < 1)
{
_logMessages.Clear();
throw new ArgumentException("Log capacity can not be lower than 1");
}

if (newCapacity < _capacity)
{
lock (_syncRoot)
{
_logMessages = new(_logMessages.TakeLast(newCapacity));
}
}
_capacity = newCapacity;
}
}
97 changes: 93 additions & 4 deletions TeslaSolarCharger/Client/Pages/Support.razor
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,55 @@

<h1>Support</h1>


<h3>Logging</h3>

<MudAlert Severity="Severity.Warning"
NoIcon="true"
ContentAlignment="HorizontalAlignment.Left">
<h5>Never share logs publicly</h5>
Logs might contain sensitive information like your vehicle's location. Do not share logs publicly.
</MudAlert>
<h3>General</h3>

<RightAlignedButtonComponent ButtonText="Download Logs"
StartIcon="@Icons.Material.Filled.Download"
OnButtonClicked="@(_ => NavigationManager.NavigateTo("api/Debug/DownloadLogs", true))"></RightAlignedButtonComponent>

<MudAlert Severity="Severity.Warning"
NoIcon="true"
ContentAlignment="HorizontalAlignment.Left">
Do not change the configuration as this might lead to extremly high memory usage. All Settings will be reset after a restart.
</MudAlert>

<div class="row">
@if (_logCapacity == default || _logLevel == default)
{
<PlaceholderComponent></PlaceholderComponent>
}
else
{
<div class="col">
<GenericInput For="() => _logLevel.Value"
LabelName="Log Level"
ShouldBeInErrorState="@(_logLevelSetErrorMessage != default)"
ErrorMessage="@_logLevelSetErrorMessage"
OnButtonClicked="@(UpdateLogLevel)"
StringIdDropDownOptions="LogLevelOptions"
PostfixButtonStartIcon="@Icons.Material.Filled.Save" />
</div>
<div class="col">
<GenericInput For="() => _logCapacity.Value"
LabelName="Log Capacity"
ShouldBeInErrorState="@(_logCapacitySetErrorMessage != default)"
ErrorMessage="@_logCapacitySetErrorMessage"
OnButtonClicked="@(UpdateLogCapacity)"
PostfixButtonStartIcon="@Icons.Material.Filled.Save" />
</div>
}
</div>

<hr />

<h3>Car Debug Details</h3>
@if (_debugCars == default)
{
Expand All @@ -38,7 +75,7 @@ else
<div>Is Available in Tesla account: @car.Value.IsAvailableInTeslaAccount</div>
<div>Should be managed: @car.Value.ShouldBeManaged</div>


@if (car.Value.Vin != default && _fleetTelemetryGetConfigs.TryGetValue(car.Value.Vin, out var config))
{
<h4>Fleet Telemetry Config</h4>
Expand All @@ -50,13 +87,13 @@ else
IsDisabled="@(car.Value.Vin == default || !car.Value.IsAvailableInTeslaAccount)"
DisabledToolTipText="@(car.Value.IsAvailableInTeslaAccount ? null : "Can not check config as car is not part of Tesla account")"
OnButtonClicked="@(() => GetFleetTelemetryConfig(car.Value.Vin))"></RightAlignedButtonComponent>

@if (car.Value.Vin != default && _fleetTelemetrySetResults.TryGetValue(car.Value.Vin, out var result))
{
<h4>Fleet Telemetry SetResult</h4>
<pre>@result</pre>
}

<RightAlignedButtonComponent ButtonText="Normal Fleet Configuration Set"
IsLoading="@_isFleetTelemetryLoading"
IsDisabled="@(car.Value.Vin == default || !car.Value.IsAvailableInTeslaAccount)"
Expand All @@ -83,6 +120,20 @@ else

private bool _isFleetTelemetryLoading;

private DtoValue<string>? _logLevel;
private string? _logLevelSetErrorMessage;
private DtoValue<int>? _logCapacity;
private string? _logCapacitySetErrorMessage;

private Dictionary<string, string> LogLevelOptions => new()
{
{ "Verbose", "Verbose" },
{ "Debug", "Debug" },
{ "Information", "Information" },
{ "Warning", "Warning" },
{ "Error", "Error" },
{ "Fatal", "Fatal" },
};

protected override async Task OnInitializedAsync()
{
Expand All @@ -91,6 +142,8 @@ else
{
_debugCars = cars;
}
_logLevel = await HttpClientHelper.SendGetRequestWithSnackbarAsync<DtoValue<string>>("api/Debug/GetLogLevel");
_logCapacity = await HttpClientHelper.SendGetRequestWithSnackbarAsync<DtoValue<int>>("api/Debug/GetLogCapacity");
}


Expand Down Expand Up @@ -139,4 +192,40 @@ else
_fleetTelemetrySetResults[vin] = stringToDisplay;
_isFleetTelemetryLoading = false;
}

private async Task UpdateLogLevel()
{
if (_logLevel == default)
{
return;
}

if (string.IsNullOrEmpty(_logLevel.Value))
{
return;
}
var result = await HttpClientHelper.SendPostRequestAsync<object>($"api/Debug/SetLogLevel?level={Uri.EscapeDataString(_logLevel.Value)}", null);
if (result.HasError)
{
_logLevelSetErrorMessage = result.ErrorMessage;
return;
}
Snackbar.Add("Log level updated", Severity.Success);
}

private async Task UpdateLogCapacity()
{
if (_logCapacity == default)
{
return;
}
var result = await HttpClientHelper.SendPostRequestAsync<object>($"api/Debug/SetLogCapacity?capacity={_logCapacity.Value}", null);
if (result.HasError)
{
_logCapacitySetErrorMessage = result.ErrorMessage;
return;
}
Snackbar.Add("Log capacity updated", Severity.Success);
}

}
43 changes: 25 additions & 18 deletions TeslaSolarCharger/Server/Controllers/DebugController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,28 +10,25 @@

namespace TeslaSolarCharger.Server.Controllers;

public class DebugController(InMemorySink inMemorySink,
Serilog.Core.LoggingLevelSwitch inMemoryLogLevelSwitch,
IFleetTelemetryConfigurationService fleetTelemetryConfigurationService,/*chaning log level switch is not tested*/
ISettings settings,
public class DebugController(IFleetTelemetryConfigurationService fleetTelemetryConfigurationService,
IDebugService debugService) : ApiBaseController
{
[HttpGet]
public IActionResult DownloadLogs()
{
// Get the logs from the in-memory sink.
var logs = inMemorySink.GetLogs();

// Join the log entries into a single string, separated by new lines.
var content = string.Join(Environment.NewLine, logs);

// Convert the string content to a byte array (UTF8 encoding).
var bytes = Encoding.UTF8.GetBytes(content);
var bytes = debugService.GetLogBytes();

// Return the file with the appropriate content type and file name.
return File(bytes, "text/plain", "logs.log");
}

[HttpGet]
public IActionResult GetLogLevel()
{
var level = debugService.GetLogLevel();
return Ok(new DtoValue<string>(level));
}

/// <summary>
/// Adjusts the minimum log level for the in-memory sink.
/// </summary>
Expand All @@ -40,12 +37,22 @@ public IActionResult DownloadLogs()
[HttpPost]
public IActionResult SetLogLevel([FromQuery] string level)
{
if (!Enum.TryParse<LogEventLevel>(level, true, out var newLevel))
{
return BadRequest("Invalid log level. Use one of: Verbose, Debug, Information, Warning, Error, Fatal");
}
inMemoryLogLevelSwitch.MinimumLevel = newLevel;
return Ok($"In-memory sink log level changed to {newLevel}");
debugService.SetLogLevel(level);
return Ok();
}

[HttpGet]
public IActionResult GetLogCapacity()
{
var capacity = debugService.GetLogCapacity();
return Ok(new DtoValue<int>(capacity));
}

[HttpPost]
public IActionResult SetLogCapacity([FromQuery] int capacity)
{
debugService.SetLogCapacity(capacity);
return Ok();
}

[HttpGet]
Expand Down
9 changes: 6 additions & 3 deletions TeslaSolarCharger/Server/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,15 @@

builder.Host.UseSerilog();
const string outputTemplate = "[{Timestamp:dd-MMM-yyyy HH:mm:ss.fff} {Level:u3} {SourceContext}] {Message:lj}{NewLine}{Exception}";
var inMemoryLevelSwitch = new LoggingLevelSwitch(LogEventLevel.Verbose);
var inMemorySink = new InMemorySink(outputTemplate, capacity: 20000);
var inMemorySink = new InMemorySink(outputTemplate, capacity: configurationManager.GetValue<int>("InMemoryLogDefaultCapacity"));

builder.Services.AddSingleton(inMemoryLevelSwitch);
builder.Services.AddSingleton<IInMemorySink>(inMemorySink);
builder.Services.AddSingleton(inMemorySink);

var inMemoryLevelSwitch = new LoggingLevelSwitch(LogEventLevel.Verbose);
builder.Services.AddSingleton(inMemoryLevelSwitch);


var app = builder.Build();

Log.Logger = new LoggerConfiguration()
Expand Down
1 change: 1 addition & 0 deletions TeslaSolarCharger/Server/Services/BackendApiService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ await errorHandlingService.HandleError(nameof(BackendApiService), nameof(Refresh
"Could not refresh backend token", result.ErrorMessage ?? string.Empty, issueKeys.BackendTokenNotRefreshable, null, null);
logger.LogError("Could not refresh backend token. {errorMessage}", result.ErrorMessage);
memoryCache.Remove(constants.BackendTokenStateKey);
logger.LogError("Could not refresh backend token. Error Message: {errorMessage}", result.ErrorMessage);
throw new InvalidOperationException($"Could not refresh backend token {result.ErrorMessage}");
}
await errorHandlingService.HandleErrorResolved(issueKeys.BackendTokenNotRefreshable, null);
Expand Down
6 changes: 0 additions & 6 deletions TeslaSolarCharger/Server/Services/ConfigJsonService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -266,12 +266,6 @@ public async Task UpdateCarBasicConfiguration(int carId, CarBasicConfiguration c
settingsCar.ShouldBeManaged = carBasicConfiguration.ShouldBeManaged;
settingsCar.UseBle = carBasicConfiguration.UseBle;
settingsCar.BleApiBaseUrl = carBasicConfiguration.BleApiBaseUrl;
await fleetTelemetryWebSocketService.DisconnectWebSocketsByVin(carBasicConfiguration.Vin);
}

public Task UpdateCarConfiguration(int carId, DepricatedCarConfiguration carConfiguration)
{
throw new NotImplementedException();
}

public async Task SaveOrUpdateCar(DtoCar car)
Expand Down
5 changes: 5 additions & 0 deletions TeslaSolarCharger/Server/Services/Contracts/IDebugService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,9 @@ namespace TeslaSolarCharger.Server.Services.Contracts;
public interface IDebugService
{
Task<Dictionary<int, DtoDebugCar>> GetCars();
byte[] GetLogBytes();
void SetLogLevel(string level);
void SetLogCapacity(int capacity);
string GetLogLevel();
int GetLogCapacity();
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,5 @@
public interface IFleetTelemetryWebSocketService
{
Task ReconnectWebSocketsForEnabledCars();
Task DisconnectWebSocketsByVin(string vin);
bool IsClientConnected(string vin);
}
Loading
Loading