Refactored the logging system and built skeletons for the DataStorages.

This commit is contained in:
Fey 2026-01-24 22:25:20 +01:00
parent 3111168579
commit 7094ae45d2
21 changed files with 363 additions and 38 deletions

1
.gitignore vendored
View File

@ -37,3 +37,4 @@
/Voile.Storage.Postgresql/bin/Debug/net8.0 /Voile.Storage.Postgresql/bin/Debug/net8.0
/Voile.Storage.SqlServer/bin/Debug/net8.0 /Voile.Storage.SqlServer/bin/Debug/net8.0
/Voile.Storage.Sqlite/obj /Voile.Storage.Sqlite/obj
/Voile.Storage.Sqlite/bin/Debug/net8.0

View File

@ -4,6 +4,7 @@ using System.Linq;
using System.Reflection; using System.Reflection;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Voile.Common.Logging;
using Voile.Common.Reflection; using Voile.Common.Reflection;
using Voile.Patchouli.Data; using Voile.Patchouli.Data;
@ -16,16 +17,19 @@ namespace Voile.Patchouli.Reflection
public static VoilePluginManager _instance; public static VoilePluginManager _instance;
private static Type _dataStorageFactoryType = typeof(IVoileDataStorageFactory); private static Type _dataStorageFactoryType = typeof(IVoileDataStorageFactory);
private static Type _objectStorageFactoryType = typeof(IVoileObjectStorageFactory); private static Type _objectStorageFactoryType = typeof(IVoileObjectStorageFactory);
private static VoileLogger logger;
public static VoilePluginManager GetInstance()
public static VoilePluginManager GetInstance()
{ {
if (_instance == null) if (_instance == null)
{ {
_instance = new VoilePluginManager(); _instance = new VoilePluginManager();
logger = VoileLogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
} }
return _instance; return _instance;
} }
private List<Assembly> _loadedAssemblies; private List<Assembly> _loadedAssemblies;
private List<VoilePluginInfo> _knownDataStorages; private List<VoilePluginInfo> _knownDataStorages;
private List<VoilePluginInfo> _knownObjectStorages; private List<VoilePluginInfo> _knownObjectStorages;
@ -37,6 +41,8 @@ namespace Voile.Patchouli.Reflection
if (_loadedAssemblies.Contains(assembly)) if (_loadedAssemblies.Contains(assembly))
return false; return false;
logger.Info("Scanning assembly: {0}", assembly.GetName().Name);
bool result = false; bool result = false;
Type[] types = assembly.GetTypes(); Type[] types = assembly.GetTypes();
@ -86,7 +92,20 @@ namespace Voile.Patchouli.Reflection
public IReadOnlyList<VoilePluginInfo> GetDataStorages() public IReadOnlyList<VoilePluginInfo> GetDataStorages()
{ {
if (_knownDataStorages == null)
{
return new List<VoilePluginInfo>();
}
return _knownDataStorages.AsReadOnly(); return _knownDataStorages.AsReadOnly();
} }
public DirectoryInfo GetVoileHomeDirectory()
{
Assembly? assembly = Assembly.GetEntryAssembly();
string location = assembly.Location;
FileInfo locationFi = new FileInfo(location);
DirectoryInfo directory = locationFi.Directory;
return directory;
}
} }
} }

View File

@ -0,0 +1,47 @@
using Oracle.ManagedDataAccess.Client;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Voile.Common.DRM;
using Voile.Common.Reflection;
using Voile.Patchouli.Data;
using Voile.Patchouli.Reflection;
namespace Voile.Storage.Oracle
{
[VoilePlugin]
[VoilePluginId(2)]
[NeedsEntitlement("{D48BDD34-7D5B-4543-BFA7-71A8C763A298}")]
internal class OracleDataStorageFactory : IVoileDataStorageFactory
{
public OracleDataStorageFactory()
{
}
public IVoileDataStorage CreateDataStorage()
{
throw new NotImplementedException();
}
private OracleConnectionStringBuilder ocsb;
[Description("The IP-Address or Hostname the TNS-Listener in running on.")]
public string Host { get; set; }
[Description("The Port on which TNS-Listener is listening on.")]
public ushort Port { get; set; }
[Description("The Service Name registered with TNS-Listener. Usually, this is the name of your Pluggable Database. Note that this is not the SID!")]
public string Service { get; set; }
[Description("The username to login with")]
public string User { get; set; }
[Description("The password to login with")]
public string Password { get; set; }
}
}

View File

@ -6,4 +6,12 @@
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<PackageReference Include="Oracle.ManagedDataAccess.Core" Version="23.26.100" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Voile.Patchouli\Voile.Patchouli.csproj" />
</ItemGroup>
</Project> </Project>

View File

@ -0,0 +1,40 @@
using Npgsql;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Voile.Common.Reflection;
using Voile.Patchouli.Data;
using Voile.Patchouli.Reflection;
namespace Voile.Storage.Postgresql
{
[VoilePlugin]
[VoilePluginId(4)]
internal class PostgresqlDataStorageFactory : IVoileDataStorageFactory
{
public IVoileDataStorage CreateDataStorage()
{
throw new NotImplementedException();
}
private NpgsqlConnectionStringBuilder ncsb;
[Description("The IP Adress or Hostname on which PostgreSQL is running.")]
public string Host { get; set; }
[Description("The Port number on which PostgreSQL is listening on.")]
public ushort Port { get; set; }
[Description("The username to login with.")]
public string Username { get; set; }
[Description("The password to login with.")]
public string Password { get; set; }
[Description("The database inside PostgreSQL to connect to")]
public string DatabaseName { get; set; }
}
}

View File

@ -6,4 +6,12 @@
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<PackageReference Include="Npgsql" Version="10.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Voile.Patchouli\Voile.Patchouli.csproj" />
</ItemGroup>
</Project> </Project>

View File

@ -36,5 +36,5 @@ GO
3. Start the container: 3. Start the container:
`docker run --rm -it -e "MSSQL_SA_PASSWORD=Voile12345" --memory=1G --name sqlserver -e "ACCEPT_EULA=Y" -v /data/sqlserver:/var/opt/mssql/data -p 1433:1433 mcr.microsoft.com/mssql/server:2025-latest` `docker run --rm -it -e "MSSQL_PID=Express" -e "MSSQL_SA_PASSWORD=Voile12345" --memory=1G --name sqlserver -e "ACCEPT_EULA=Y" -v /data/sqlserver:/var/opt/mssql/data -p 1433:1433 mcr.microsoft.com/mssql/server:2025-latest`

View File

@ -0,0 +1,42 @@
using Microsoft.Data.SqlClient;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Voile.Common.DRM;
using Voile.Common.Reflection;
using Voile.Patchouli.Data;
using Voile.Patchouli.Reflection;
namespace Voile.Storage.SqlServer
{
[VoilePlugin]
[VoilePluginId(5)]
[NeedsEntitlement("{95EA2C4C-259F-4A26-A2A6-1F17ED2A397A}")]
internal class SqlServerDataStorageFactory : IVoileDataStorageFactory
{
private SqlConnectionStringBuilder scsb;
[Description("The host SQL Server is running on.")]
public string Host { get; set; }
[Description("The port SQL Server is listening on.")]
public ushort Port { get; set; }
[Description("The username to login with.")]
public string Username { get; set; }
[Description("The password to login with.")]
public string Password { get; set; }
[Description("The SQL Server database to connect with")]
public string Database { get; set; }
public IVoileDataStorage CreateDataStorage()
{
throw new NotImplementedException();
}
}
}

View File

@ -6,4 +6,12 @@
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Data.SqlClient" Version="6.1.4" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Voile.Patchouli\Voile.Patchouli.csproj" />
</ItemGroup>
</Project> </Project>

View File

@ -0,0 +1,26 @@
using Microsoft.Data.Sqlite;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Voile.Common.Reflection;
using Voile.Patchouli.Data;
using Voile.Patchouli.Reflection;
namespace Voile.Storage.Sqlite
{
[VoilePlugin]
[VoilePluginId(3)]
public class SqliteDataStorageFactory : IVoileDataStorageFactory
{
public IVoileDataStorage CreateDataStorage()
{
throw new NotImplementedException();
}
public string FilePath { get; set; }
private SqliteConnectionStringBuilder scsb;
}
}

View File

@ -6,6 +6,10 @@
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Data.Sqlite.Core" Version="10.0.2" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Voile.Patchouli\Voile.Patchouli.csproj" /> <ProjectReference Include="..\Voile.Patchouli\Voile.Patchouli.csproj" />
</ItemGroup> </ItemGroup>

View File

@ -9,13 +9,16 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Forms; using System.Windows.Forms;
using Voile.Common.Logging;
using Voile.Common.Logging.Sinks;
using WeifenLuo.WinFormsUI.Docking; using WeifenLuo.WinFormsUI.Docking;
namespace Voile.DockContents namespace Voile.DockContents
{ {
public partial class DockLog : DockContent, IAppender public partial class DockLog : DockContent, IVoileLogSink
{ {
private static readonly log4net.ILog logger = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); private static VoileLog4netSink log4netSink;
private static VoileLogger logger;
public DockLog() public DockLog()
{ {
InitializeComponent(); InitializeComponent();
@ -23,6 +26,12 @@ namespace Voile.DockContents
richTextBox1.AllowDrop = true; richTextBox1.AllowDrop = true;
richTextBox1.DragEnter += RichTextBox1_DragEnter; richTextBox1.DragEnter += RichTextBox1_DragEnter;
richTextBox1.DragDrop += RichTextBox1_DragDrop; richTextBox1.DragDrop += RichTextBox1_DragDrop;
if (logger == null)
{
logger = VoileLogManager.GetLogger(this.GetType());
logger.Debug("Initalize DockLog");
}
} }
public void Logger(string s) public void Logger(string s)
@ -34,6 +43,26 @@ namespace Voile.DockContents
richTextBox1.ResumeLayout(); richTextBox1.ResumeLayout();
} }
public void OnLogMessage(VoileLogMessage message)
{
Logger(String.Format("{0} {1} {2} - {3}", message.Timestamp, message.Level, message.SourceName, message.Message));
}
private bool initalized;
internal void InitalizeLogging()
{
if (initalized)
{
throw new Voile.Common.VoileException("Logging already initalized!");
}
log4netSink = new VoileLog4netSink();
log4netSink.Initalize();
VoileLogManager.GetInstance().AddSink(this);
initalized = true;
}
private void RichTextBox1_DragDrop(object? sender, DragEventArgs e) private void RichTextBox1_DragDrop(object? sender, DragEventArgs e)
{ {
} }
@ -42,30 +71,6 @@ namespace Voile.DockContents
{ {
} }
bool wasInitalized;
public void InitalizeLogging()
{
if (wasInitalized)
throw new Exception("already initalized");
var hierarchy = (log4net.Repository.Hierarchy.Hierarchy)log4net.LogManager.GetRepository();
hierarchy.Root.AddAppender(this);
hierarchy.Root.Level = log4net.Core.Level.All;
log4net.Config.BasicConfigurator.Configure(hierarchy);
wasInitalized = true;
}
private log4net.Layout.PatternLayout logLayout;
public void DoAppend(LoggingEvent loggingEvent)
{
if (logLayout == null)
{
logLayout = new log4net.Layout.PatternLayout();
logLayout.ConversionPattern = "%date %level %logger - %message";
logLayout.ActivateOptions();
}
string v = logLayout.Format(loggingEvent);
Logger(v);
}
} }
} }

View File

@ -160,6 +160,17 @@ namespace Voile
{ {
DisplayTabPage(1); DisplayTabPage(1);
} }
else if (currentTabPage == 1)
{
if (beginnerMode)
{
//TODO: Automatisch SQLite und Directory Storage auswählen
}
else
{
DisplayTabPage(2);
}
}
} }
private void buttonLoadLicenseFile_Click(object sender, EventArgs e) private void buttonLoadLicenseFile_Click(object sender, EventArgs e)

View File

@ -1,3 +1,4 @@
using Voile.Common.Logging;
using Voile.DockContents; using Voile.DockContents;
using Voile.Tasks; using Voile.Tasks;
using WeifenLuo.WinFormsUI.Docking; using WeifenLuo.WinFormsUI.Docking;
@ -6,11 +7,15 @@ namespace Voile
{ {
public partial class Form1 : Form public partial class Form1 : Form
{ {
private static readonly log4net.ILog logger = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); private static VoileLogger logger;
public Form1() public Form1()
{ {
InitializeComponent(); InitializeComponent();
if (logger == null)
{
logger = VoileLogManager.GetLogger(this.GetType());
}
dockPanel1.Theme = vS2003Theme1; dockPanel1.Theme = vS2003Theme1;
logger.Info("Main Form initalized."); logger.Info("Main Form initalized.");

View File

@ -18,7 +18,7 @@ namespace Voile.Persistence
{ {
public override bool Equals(object? obj) public override bool Equals(object? obj)
{ {
DirectoryStorageFactory dsf = obj as DirectoryStorageFactory DirectoryStorageFactory dsf = obj as DirectoryStorageFactory;
if (dsf == null) if (dsf == null)
return false; return false;

View File

@ -1,15 +1,24 @@
using log4net;
using Voile.Common.Logging;
using Voile.Common.Logging.Sinks;
[assembly: log4net.Config.XmlConfigurator(ConfigFile = "log4net.config")] [assembly: log4net.Config.XmlConfigurator(ConfigFile = "log4net.config")]
namespace Voile namespace Voile
{ {
internal static class Program internal static class Program
{ {
private static readonly log4net.ILog logger = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); private static VoileLogger logger;
/// <summary> /// <summary>
/// The main entry point for the application. /// The main entry point for the application.
/// </summary> /// </summary>
[STAThread] [STAThread]
static void Main() static void Main()
{ {
VoileLogManager.GetInstance().AddSink(VoileConsoleSink.GetInstance());
if (logger == null)
{
logger = VoileLogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
}
logger.Info("Hello! This is Voile, an UI to skyscraper8."); logger.Info("Hello! This is Voile, an UI to skyscraper8.");
// To customize application configuration such as set high DPI settings or default font, // To customize application configuration such as set high DPI settings or default font,
// see https://aka.ms/applicationconfiguration. // see https://aka.ms/applicationconfiguration.

View File

@ -1,8 +1,12 @@
# DataStorage Ids # DataStorage Ids
|Id|Name | |Id|Name |Entitlement |
|--|----------------| |--|----------------|-----------------------------------------|
|1 |DirectoryStorage| |1 |DirectoryStorage| |
|2 |Oracle |\{D48BDD34-7D5B-4543-BFA7-71A8C763A298\} |
|3 |SQLite | |
|4 |PostgreSQL | |
|5 |SQL Server |\{95EA2C4C-259F-4A26-A2A6-1F17ED2A397A\} |
# ObjectStorage Ids # ObjectStorage Ids

View File

@ -4,6 +4,8 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Voile.Patchouli.Reflection;
using Voile.Storage.Sqlite;
namespace Voile.Tasks namespace Voile.Tasks
{ {
@ -12,7 +14,11 @@ namespace Voile.Tasks
public Form1 Form1 { get; internal set; } public Form1 Form1 { get; internal set; }
private Ini ini; private Ini ini;
public void Run() public void Run()
{ {
VoilePluginManager voilePluginManager = VoilePluginManager.GetInstance();
voilePluginManager.ScanAssembly(this.GetType().Assembly);
voilePluginManager.ScanAssembly(typeof(SqliteDataStorageFactory).Assembly);
if (IsFirstRun()) if (IsFirstRun())
{ {
FirstRunWizard frw = new FirstRunWizard(); FirstRunWizard frw = new FirstRunWizard();
@ -31,7 +37,9 @@ namespace Voile.Tasks
return true; return true;
} }
voileContext.Ini = new Ini(fileInfo.FullName); voileContext.Ini = new Ini(fileInfo.FullName);
bool firstRunComplete = ini.ReadValue("voile", "firstRunComplete", false); bool firstRunComplete = ini.ReadValue("voile", "firstRunComplete", false);
if (!firstRunComplete) if (!firstRunComplete)

View File

@ -23,6 +23,7 @@
<ProjectReference Include="..\..\skyscraper8\skyscraper8\skyscraper8.csproj" /> <ProjectReference Include="..\..\skyscraper8\skyscraper8\skyscraper8.csproj" />
<ProjectReference Include="..\Sophia.Net.DRM\Sophia.Net.DRM.csproj" /> <ProjectReference Include="..\Sophia.Net.DRM\Sophia.Net.DRM.csproj" />
<ProjectReference Include="..\Voile.Patchouli\Voile.Patchouli.csproj" /> <ProjectReference Include="..\Voile.Patchouli\Voile.Patchouli.csproj" />
<ProjectReference Include="..\Voile.Storage.Sqlite\Voile.Storage.Sqlite.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -49,5 +49,6 @@ namespace Voile
public Ini Ini { get; set; } public Ini Ini { get; set; }
public X509Certificate SophiaNetRootCertificate { get; internal set; } public X509Certificate SophiaNetRootCertificate { get; internal set; }
public bool PluginManagerInitalized { get; set; }
} }
} }

View File

@ -0,0 +1,78 @@
using log4net.Appender;
using log4net.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Voile.Common;
using Voile.Common.Logging;
namespace Voile
{
internal class VoileLog4netSink : IAppender
{
private VoileLogger myOwnLogger;
private bool wasInitalized;
private bool isClosed;
public void Initalize()
{
if (wasInitalized)
{
throw new VoileException("Log sink already initalized.");
}
myOwnLogger = VoileLogManager.GetLogger(GetType());
myOwnLogger.Debug("Initalize VoileLog4netSink");
var hierarchy = (log4net.Repository.Hierarchy.Hierarchy)log4net.LogManager.GetRepository();
hierarchy.Root.AddAppender(this);
hierarchy.Root.Level = log4net.Core.Level.All;
log4net.Config.BasicConfigurator.Configure(hierarchy);
wasInitalized = true;
}
private log4net.Layout.PatternLayout logLayout;
private string log4netName;
public string Name { get => log4netName; set => log4netName = value; }
public void DoAppend(LoggingEvent loggingEvent)
{
if (isClosed)
{
throw new VoileException("Can't append to a closed logger.");
}
if (logLayout == null)
{
logLayout = new log4net.Layout.PatternLayout();
logLayout.ConversionPattern = "%date %level %logger - %message";
logLayout.ActivateOptions();
}
string v = logLayout.Format(loggingEvent);
string loggerName = loggingEvent.LoggerName;
VoileLogLevel logLevel = TranslateLogLevel(loggingEvent.Level);
VoileLogManager.GetLogger(loggerName).Log(logLevel, loggingEvent.RenderedMessage);
}
private VoileLogLevel TranslateLogLevel(Level level)
{
switch(level.Value)
{
case 30000:
return VoileLogLevel.DEBUG;
case 40000:
return VoileLogLevel.INFO;
default:
throw new NotImplementedException(String.Format("Loglevel: {0}", level.Value));
}
}
public void Close()
{
isClosed = true;
}
}
}