remote logging
This commit is contained in:
parent
9b39de7b76
commit
28ae78cea7
@ -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>
|
||||
@ -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>();
|
||||
|
||||
@ -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>
|
||||
|
||||
|
||||
@ -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"];
|
||||
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user