Log backend auth failures
This commit is contained in:
@@ -150,6 +150,22 @@ Das Tray-Icon zeigt den aktuellen Zustand:
|
||||
- Grün: Worker ist aktiviert, Backend-Konfiguration ist vollständig, Drucker ist verfügbar und der letzte Backend-Kontakt war erfolgreich.
|
||||
- Rot: Konfiguration fehlt, Drucker ist nicht verfügbar, Worker ist deaktiviert oder der Backend-Kontakt ist fehlgeschlagen.
|
||||
|
||||
## Logs und 401-Fehler
|
||||
|
||||
Logs liegen standardmäßig unter:
|
||||
|
||||
`C:\ProgramData\LabelPrintAgent\logs\label-print-agent-YYYYMMDD.log`
|
||||
|
||||
Bei HTTP-Fehlern wie `401 Unauthorized` protokolliert der Agent:
|
||||
|
||||
- HTTP-Methode und URL
|
||||
- Statuscode und Reason Phrase
|
||||
- ob ein Authorization-Header gesetzt wurde
|
||||
- Authorization-Scheme, z. B. `Bearer`
|
||||
- Token-Länge, aber niemals den Token selbst
|
||||
- `WWW-Authenticate`-Header des Backends
|
||||
- gekürzten Response-Body des Backends
|
||||
|
||||
## Nicht mehr im Agent
|
||||
|
||||
- keine Layout-JSON-Verwaltung
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Net.Http.Headers;
|
||||
using System.Net.Http.Json;
|
||||
using System.Text.Json;
|
||||
using LabelPrintAgent.Configuration;
|
||||
using Serilog;
|
||||
|
||||
namespace LabelPrintAgent.Backend;
|
||||
|
||||
@@ -28,7 +29,7 @@ public sealed class BackendClient
|
||||
return null;
|
||||
}
|
||||
|
||||
response.EnsureSuccessStatusCode();
|
||||
await EnsureSuccessWithDiagnosticsAsync(response, request, cancellationToken);
|
||||
return await response.Content.ReadFromJsonAsync<BackendLabelJob>(cancellationToken: cancellationToken);
|
||||
}
|
||||
|
||||
@@ -49,7 +50,7 @@ public sealed class BackendClient
|
||||
var settings = _settingsStore.Load();
|
||||
using var request = CreateRequest(HttpMethod.Get, MakeAbsoluteUrl(settings.Backend.BaseUrl, job.LabelImageUrl), settings);
|
||||
using var response = await _httpClient.SendAsync(request, cancellationToken);
|
||||
response.EnsureSuccessStatusCode();
|
||||
await EnsureSuccessWithDiagnosticsAsync(response, request, cancellationToken);
|
||||
var bytesFromUrl = await response.Content.ReadAsByteArrayAsync(cancellationToken);
|
||||
return CreateBitmap(bytesFromUrl);
|
||||
}
|
||||
@@ -83,7 +84,7 @@ public sealed class BackendClient
|
||||
using var request = CreateRequest(HttpMethod.Post, MakeAbsoluteUrl(settings.Backend.BaseUrl, path), settings);
|
||||
request.Content = JsonContent.Create(body);
|
||||
using var response = await _httpClient.SendAsync(request, cancellationToken);
|
||||
response.EnsureSuccessStatusCode();
|
||||
await EnsureSuccessWithDiagnosticsAsync(response, request, cancellationToken);
|
||||
}
|
||||
|
||||
public async Task WatchServerSentEventsAsync(Func<CancellationToken, Task> onLabelJobAvailable, CancellationToken cancellationToken = default)
|
||||
@@ -93,7 +94,7 @@ public sealed class BackendClient
|
||||
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("text/event-stream"));
|
||||
|
||||
using var response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
|
||||
response.EnsureSuccessStatusCode();
|
||||
await EnsureSuccessWithDiagnosticsAsync(response, request, cancellationToken);
|
||||
|
||||
await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken);
|
||||
using var reader = new StreamReader(stream);
|
||||
@@ -125,6 +126,48 @@ public sealed class BackendClient
|
||||
return request;
|
||||
}
|
||||
|
||||
private static async Task EnsureSuccessWithDiagnosticsAsync(
|
||||
HttpResponseMessage response,
|
||||
HttpRequestMessage request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string responseBody;
|
||||
try
|
||||
{
|
||||
responseBody = response.Content is null
|
||||
? string.Empty
|
||||
: await response.Content.ReadAsStringAsync(cancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
responseBody = $"<Antwort konnte nicht gelesen werden: {ex.Message}>";
|
||||
}
|
||||
|
||||
var authorization = request.Headers.Authorization;
|
||||
var wwwAuthenticate = string.Join("; ", response.Headers.WwwAuthenticate.Select(value => value.ToString()));
|
||||
|
||||
Log.Warning(
|
||||
"Backend request failed: {Method} {Url} -> {StatusCode} {ReasonPhrase}. " +
|
||||
"AuthorizationHeaderPresent={AuthorizationHeaderPresent}, AuthorizationScheme={AuthorizationScheme}, " +
|
||||
"TokenLength={TokenLength}, WwwAuthenticate={WwwAuthenticate}, ResponseBody={ResponseBody}",
|
||||
request.Method.Method,
|
||||
request.RequestUri,
|
||||
(int)response.StatusCode,
|
||||
response.ReasonPhrase,
|
||||
authorization is not null,
|
||||
authorization?.Scheme ?? string.Empty,
|
||||
authorization?.Parameter?.Length ?? 0,
|
||||
wwwAuthenticate,
|
||||
Truncate(responseBody, 2000));
|
||||
|
||||
response.EnsureSuccessStatusCode();
|
||||
}
|
||||
|
||||
private static string BuildUrl(string baseUrl, string path, string agentId)
|
||||
{
|
||||
var separator = path.Contains('?') ? '&' : '?';
|
||||
@@ -144,6 +187,16 @@ public sealed class BackendClient
|
||||
|
||||
private static string EnsureTrailingSlash(string url) => url.EndsWith('/') ? url : url + "/";
|
||||
|
||||
private static string Truncate(string value, int maxLength)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value) || value.Length <= maxLength)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
return value[..maxLength] + "...";
|
||||
}
|
||||
|
||||
private static Bitmap CreateBitmap(byte[] bytes)
|
||||
{
|
||||
using var stream = new MemoryStream(bytes);
|
||||
|
||||
Reference in New Issue
Block a user