Log backend auth failures

This commit is contained in:
2026-05-09 09:45:09 +02:00
parent 00483f7236
commit a2d93da1f3
2 changed files with 73 additions and 4 deletions
+16
View File
@@ -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
+57 -4
View File
@@ -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);