remote logging

This commit is contained in:
iTob 2026-03-01 16:13:49 +01:00
parent 9b39de7b76
commit 28ae78cea7
8 changed files with 310 additions and 49 deletions

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" addBOMForNewFiles="with BOM under Windows, with no BOM otherwise" />
<component name="Encoding" defaultCharsetForPropertiesFiles="UTF-8" />
</project>

View File

@ -1,5 +1,5 @@
using System.IO;
using System.Windows;
using System.IO;
using System.Windows;
using CamBooth.App.Core.AppSettings;
using CamBooth.App.Core.Logging;
@ -11,6 +11,7 @@ using EDSDKLib.API.Base;
using EOSDigital.API;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace CamBooth.App;
@ -26,8 +27,25 @@ public partial class App : Application
{
base.OnStartup(e);
// Konfiguration laden
var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production";
var configBuilder = new ConfigurationBuilder()
.SetBasePath(AppContext.BaseDirectory)
.AddJsonFile("Core/AppSettings/app.settings.json", optional: false, reloadOnChange: true);
if (environment == "Development")
{
configBuilder.AddJsonFile("Core/AppSettings/app.settings.dev.json", optional: true, reloadOnChange: true);
}
var configuration = configBuilder.Build();
var services = new ServiceCollection();
// Register Configuration
services.AddSingleton<IConfiguration>(configuration);
// Register base services
services.AddSingleton<Logger>();
services.AddSingleton<AppSettingsService>();

View File

@ -25,6 +25,13 @@
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.1" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="9.0.1" />
<PackageReference Include="Serilog" Version="4.3.1" />
<PackageReference Include="Serilog.Formatting.Compact" Version="3.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
<PackageReference Include="Serilog.Sinks.Http" Version="9.0.0" />
<PackageReference Include="Serilog.Settings.Configuration" Version="8.0.4" />
<PackageReference Include="Serilog.Sinks.Seq" Version="9.0.0" />
<PackageReference Include="WPF-UI" Version="4.0.0-rc.3" />
</ItemGroup>

View File

@ -1,4 +1,4 @@
using CamBooth.App.Core.Logging;
using CamBooth.App.Core.Logging;
namespace CamBooth.App.Core.AppSettings;
@ -62,6 +62,15 @@ public class AppSettingsService
public string ConfigFileName => loadedConfigFile;
// Logging Settings
public string LogLevel => configuration["LoggingSettings:LogLevel"] ?? "Information";
public string LogDirectory => configuration["LoggingSettings:LogDirectory"] ?? "Logs";
public string? RemoteServerUrl => configuration["LoggingSettings:RemoteServerUrl"];
public string? RemoteServerApiKey => configuration["LoggingSettings:RemoteServerApiKey"];
// Lychee Upload Settings
public string? LycheeApiUrl => configuration["LycheeSettings:ApiUrl"];

View File

@ -11,8 +11,37 @@
"IsShutdownEnabled": false,
"UseMockCamera": true
},
"Serilog": {
"MinimumLevel": {
"Default": "Debug",
"Override": {
"Microsoft": "Warning",
"System": "Warning"
}
},
"WriteTo": [
{
"Name": "Console"
},
{
"Name": "File",
"Args": {
"path": "Logs/cambooth-dev-.log",
"rollingInterval": "Day",
"retainedFileCountLimit": 7,
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff} [{Level:u3}] {Message:lj}{NewLine}{Exception}"
}
}
]
},
"LoggingSettings": {
"LogLevel": "Debug",
"LogDirectory": "Logs",
"RemoteServerUrl": "https://log.grimma-fotobox.de",
"RemoteServerApiKey": "nhnVql3QNgoAxvDWmNyU"
},
"LycheeSettings": {
"ApiUrl": "https://cambooth-pics.rblnews.de",
"ApiUrl": "https://gallery.grimma-fotobox.de",
"Username": "itob",
"Password": "VfVyqal&Nv8U&P",
"DefaultAlbumId": "gMM3W-Pk9mr8k57k-WU2Jz8t",

View File

@ -8,11 +8,40 @@
"PhotoCountdownSeconds": 5,
"FocusDelaySeconds": 2,
"FocusTimeoutMs": 3000,
"IsShutdownEnabled": true,
"UseMockCamera": true
"IsShutdownEnabled": false,
"UseMockCamera": false
},
"Serilog": {
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Warning",
"System": "Warning"
}
},
"WriteTo": [
{
"Name": "Console"
},
{
"Name": "File",
"Args": {
"path": "Logs/cambooth-.log",
"rollingInterval": "Day",
"retainedFileCountLimit": 120,
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff} [{Level:u3}] {Message:lj}{NewLine}{Exception}"
}
}
]
},
"LoggingSettings": {
"LogLevel": "Information",
"LogDirectory": "Logs",
"RemoteServerUrl": "https://log.grimma-fotobox.de",
"RemoteServerApiKey": "8rjvr0zZmceuFZMYydKU"
},
"LycheeSettings": {
"ApiUrl": "https://cambooth-pics.rblnews.de",
"ApiUrl": "https://gallery.grimma-fotobox.de",
"Username": "itob",
"Password": "VfVyqal&Nv8U&P",
"DefaultAlbumId": "gMM3W-Pk9mr8k57k-WU2Jz8t",

View File

@ -1,13 +1,63 @@
using System.Globalization;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using Serilog;
using Serilog.Core;
using Serilog.Events;
using Serilog.Formatting.Json;
using Serilog.Sinks.Http;
using Microsoft.Extensions.Configuration;
namespace CamBooth.App.Core.Logging;
public class Logger
/// <summary>
/// Custom HTTP Client für Serilog HTTP Sink mit API-Key Support
/// </summary>
public class SeqHttpClient : IHttpClient
{
private readonly string _logsDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Logs");
private readonly string _errorLogPath;
private readonly HttpClient _httpClient;
private readonly string _apiKey;
public SeqHttpClient(string apiKey = "")
{
_httpClient = new HttpClient();
_apiKey = apiKey;
// Setze API-Key Header, falls vorhanden
if (!string.IsNullOrWhiteSpace(_apiKey))
{
_httpClient.DefaultRequestHeaders.Add("X-Seq-Api-Key", _apiKey);
}
}
public void Configure(IConfiguration configuration)
{
// Konfiguration vom HTTP Sink - nicht nötig für unseren Use-Case
}
public async Task<HttpResponseMessage> PostAsync(string requestUri, Stream contentStream, CancellationToken cancellationToken)
{
using (var content = new StreamContent(contentStream))
{
content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
return await _httpClient.PostAsync(requestUri, content, cancellationToken);
}
}
public void Dispose()
{
_httpClient?.Dispose();
}
}
public class Logger : IDisposable
{
private readonly Serilog.Core.Logger _serilogLogger;
public event LoggingEventHandler? InfoLog;
public event LoggingEventHandler? ErrorLog;
@ -15,68 +65,185 @@ public class Logger
public event LoggingEventHandler? DebugLog;
public delegate void LoggingEventHandler(string text);
public Logger()
public Logger(IConfiguration configuration)
{
// Logs-Ordner erstellen, falls nicht vorhanden
if (!Directory.Exists(_logsDirectory))
var logLevel = configuration["LoggingSettings:LogLevel"] ?? "Information";
var logDirectory = configuration["LoggingSettings:LogDirectory"] ?? "Logs";
var remoteServerUrl = configuration["LoggingSettings:RemoteServerUrl"];
var remoteServerApiKey = configuration["LoggingSettings:RemoteServerApiKey"];
if (!Directory.Exists(logDirectory))
{
Directory.CreateDirectory(_logsDirectory);
Directory.CreateDirectory(logDirectory);
}
_errorLogPath = Path.Combine(_logsDirectory, "error.txt");
var minimumLevel = ParseLogLevel(logLevel);
var loggerConfig = new LoggerConfiguration()
.MinimumLevel.Is(minimumLevel)
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
.MinimumLevel.Override("System", LogEventLevel.Warning)
.Enrich.FromLogContext()
.Enrich.WithProperty("Application", "CamBooth")
.WriteTo.Console(
outputTemplate: "{Timestamp:dd.MM.yyyy HH:mm:ss} [{Level:u3}] {Message:lj}{NewLine}{Exception}")
.WriteTo.File(
Path.Combine(logDirectory, "cambooth-.log"),
rollingInterval: RollingInterval.Day,
retainedFileCountLimit: 30,
outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff} [{Level:u3}] {Message:lj}{NewLine}{Exception}");
if (!string.IsNullOrWhiteSpace(remoteServerUrl))
{
try
{
loggerConfig.WriteTo.Seq(
serverUrl: remoteServerUrl,
apiKey: remoteServerApiKey,
restrictedToMinimumLevel: minimumLevel
);
Console.WriteLine($"Seq Sink konfiguriert: {remoteServerUrl}");
}
catch (Exception ex)
{
Console.WriteLine($"Fehler beim Konfigurieren des Seq-Sinks: {ex.Message}");
}
}
_serilogLogger = loggerConfig.CreateLogger();
Log.Logger = _serilogLogger;
Info($"Serilog Logger initialisiert - Level: {logLevel}, Directory: {logDirectory}");
if (!string.IsNullOrWhiteSpace(remoteServerUrl))
{
Info($"Remote Server konfiguriert: {remoteServerUrl}");
}
}
private void WriteToErrorLog(string message)
private LogEventLevel ParseLogLevel(string level)
{
try
return level.ToLowerInvariant() switch
{
File.AppendAllText(_errorLogPath, message + Environment.NewLine);
}
catch (Exception ex)
{
Console.WriteLine($"Fehler beim Schreiben in Fehlerlog: {ex.Message}");
}
"verbose" => LogEventLevel.Verbose,
"debug" => LogEventLevel.Debug,
"information" => LogEventLevel.Information,
"warning" => LogEventLevel.Warning,
"error" => LogEventLevel.Error,
"fatal" => LogEventLevel.Fatal,
_ => LogEventLevel.Information
};
}
public void Info(string message)
{
Application.Current.Dispatcher.Invoke(() =>
try
{
message = DateTime.Now.ToString("dd.MM.yyyy HH:mm:ss", CultureInfo.InvariantCulture) + ": [INFO] " + message;
InfoLog?.Invoke(message);
Console.WriteLine(message);
});
if (Application.Current?.Dispatcher != null)
{
Application.Current.Dispatcher.Invoke(() =>
{
var formattedMessage = DateTime.Now.ToString("dd.MM.yyyy HH:mm:ss", CultureInfo.InvariantCulture) + ": [INFO] " + message;
InfoLog?.Invoke(formattedMessage);
});
}
_serilogLogger.Information(message);
}
catch (Exception ex)
{
Console.WriteLine($"Fehler beim Info-Logging: {ex.Message}");
}
}
public void Warning(string message)
{
Application.Current.Dispatcher.Invoke(() =>
try
{
message = DateTime.Now.ToString("dd.MM.yyyy HH:mm:ss", CultureInfo.InvariantCulture) + ": [WARNING] " + message;
WarningLog?.Invoke(message);
Console.WriteLine(message);
WriteToErrorLog(message);
});
if (Application.Current?.Dispatcher != null)
{
Application.Current.Dispatcher.Invoke(() =>
{
var formattedMessage = DateTime.Now.ToString("dd.MM.yyyy HH:mm:ss", CultureInfo.InvariantCulture) + ": [WARNING] " + message;
WarningLog?.Invoke(formattedMessage);
});
}
_serilogLogger.Warning(message);
}
catch (Exception ex)
{
Console.WriteLine($"Fehler beim Warning-Logging: {ex.Message}");
}
}
public void Error(string message)
{
Application.Current.Dispatcher.Invoke(() =>
try
{
message = DateTime.Now.ToString("dd.MM.yyyy HH:mm:ss", CultureInfo.InvariantCulture) + ": [ERROR] " + message;
ErrorLog?.Invoke(message);
Console.WriteLine(message);
WriteToErrorLog(message);
});
if (Application.Current?.Dispatcher != null)
{
Application.Current.Dispatcher.Invoke(() =>
{
var formattedMessage = DateTime.Now.ToString("dd.MM.yyyy HH:mm:ss", CultureInfo.InvariantCulture) + ": [ERROR] " + message;
ErrorLog?.Invoke(formattedMessage);
});
}
_serilogLogger.Error(message);
}
catch (Exception ex)
{
Console.WriteLine($"Fehler beim Error-Logging: {ex.Message}");
}
}
public void Error(string message, Exception exception)
{
try
{
if (Application.Current?.Dispatcher != null)
{
Application.Current.Dispatcher.Invoke(() =>
{
var formattedMessage = DateTime.Now.ToString("dd.MM.yyyy HH:mm:ss", CultureInfo.InvariantCulture) + ": [ERROR] " + message;
ErrorLog?.Invoke(formattedMessage);
});
}
_serilogLogger.Error(exception, message);
}
catch (Exception ex)
{
Console.WriteLine($"Fehler beim Error-Logging: {ex.Message}");
}
}
public void Debug(string message)
{
Application.Current.Dispatcher.Invoke(() =>
try
{
message = DateTime.Now.ToString("dd.MM.yyyy HH:mm:ss", CultureInfo.InvariantCulture) + ": [DEBUG] " + message;
DebugLog?.Invoke(message);
Console.WriteLine(message);
});
if (Application.Current?.Dispatcher != null)
{
Application.Current.Dispatcher.Invoke(() =>
{
var formattedMessage = DateTime.Now.ToString("dd.MM.yyyy HH:mm:ss", CultureInfo.InvariantCulture) + ": [DEBUG] " + message;
DebugLog?.Invoke(formattedMessage);
});
}
_serilogLogger.Debug(message);
}
catch (Exception ex)
{
Console.WriteLine($"Fehler beim Debug-Logging: {ex.Message}");
}
}
public void Dispose()
{
_serilogLogger?.Dispose();
Log.CloseAndFlush();
}
}

View File

@ -6,6 +6,8 @@
- Energiesparmodus abschalten
- Starbildschirm mit freundlicher Begrüßung, kurzer Erklärung, und viel Spaß wünschen mit der FotoCam
- Verschiedene Hinweise anzeigen beim Fotografieren (lächeln, Hasensohren, Zunge raus, Grimasse, usw.)
- Bild über QR Code runterladen
- Bild über QR Code runterladen (QR Code anzeigen, sowie ausdrucken und anklebene)
- Windows updates deaktivieren
- logging einbinden (Elastic order ähnliches)
- logging einbinden (Elastic order ähnliches)
- Router anschließen für Upload
- Configs kontrollieren auf Fotobox