Compare commits

...

2 Commits

Author SHA1 Message Date
duffyduck 96c34e542f Release v0.0.0.29 2026-06-08 13:18:35 +02:00
duffyduck 23223fe0be Add persistent log file and "Protokoll" viewer button
Tray-Meldungen verschwinden - daher ein dauerhaftes Protokoll.

- Logger: threadsicheres Datei-Log in %AppData%\StarfaceOutlookSync\sync.log
  mit Rotation bei 2 MB.
- MainForm protokolliert Sync-Start, Ergebnis, Konflikte (lokal + von anderen
  Arbeitsplaetzen), Fehler und uebersprungene Laeufe (Sperre).
- Neuer Button "Protokoll" oeffnet LogViewerForm (Aktualisieren/Leeren/Ordner
  oeffnen, scrollt ans Ende).
- README/CHANGELOG aktualisiert.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 13:17:53 +02:00
8 changed files with 210 additions and 7 deletions
+6
View File
@@ -38,6 +38,12 @@ Versionsschema ist `x.x.x.x` (siehe `release.sh`).
### Hinzugefuegt
- **Protokoll (Logdatei) + "Protokoll"-Button.** Syncs, Ergebnisse, Konflikte
(lokal und von anderen Arbeitsplaetzen) und Fehler werden dauerhaft in
`%AppData%\StarfaceOutlookSync\sync.log` festgehalten - so ist auch nach dem
Verschwinden einer Tray-Meldung nachvollziehbar, wer/was/wann. Im Hauptfenster
oeffnet der Button "Protokoll" einen Betrachter (Aktualisieren/Leeren/Ordner
oeffnen). Die Datei rotiert bei 2 MB.
- **Clientuebergreifende Konflikt-Hinweise (Mehrplatz).** Wird ein echter
Feld-Konflikt aufgeloest, legt der Client eine Notiz im gemeinsamen Verzeichnis
(`conflicts/`) ab - zugeordnet nach Kontakt (StarfaceId). Ein anderer
+2
View File
@@ -260,6 +260,8 @@ StarfaceOutlookSync.exe
Alle Daten werden lokal pro Benutzer gespeichert:
- `%AppData%\StarfaceOutlookSync\profiles.json` - Sync-Profile mit Zugangsdaten
- `%AppData%\StarfaceOutlookSync\mappings\` - Kontakt-Zuordnungen pro Profil
- `%AppData%\StarfaceOutlookSync\sync.log` - Protokoll (Syncs, Konflikte, Fehler);
im Hauptfenster ueber den Button **Protokoll** einsehbar
### SSL-Zertifikate
+1 -1
View File
@@ -2,7 +2,7 @@
; Erfordert Inno Setup 6.x (https://jrsoftware.org/isinfo.php)
#define MyAppName "Starface Outlook Sync"
#define MyAppVersion "0.0.0.28"
#define MyAppVersion "0.0.0.29"
#define MyAppPublisher "HackerSoft - Hacker-Net Telekommunikation"
#define MyAppURL "https://www.hacker-net.de"
#define MyAppExeName "StarfaceOutlookSync.exe"
@@ -0,0 +1,80 @@
using System;
using System.IO;
using System.Text;
namespace StarfaceOutlookSync.Services
{
/// <summary>
/// Einfaches, threadsicheres Datei-Protokoll. Haelt fest, was bei Syncs
/// passiert (Ergebnisse, Konflikte, Fehler) - dauerhaft nachlesbar, auch
/// nachdem eine Tray-Meldung verschwunden ist.
/// </summary>
public static class Logger
{
private static readonly object _lock = new object();
private const long MaxBytes = 2 * 1024 * 1024; // 2 MB, dann Rotation
public static string LogFilePath { get; } = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"StarfaceOutlookSync", "sync.log");
public static void Log(string message)
{
try
{
lock (_lock)
{
Directory.CreateDirectory(Path.GetDirectoryName(LogFilePath));
Rotate();
var line = $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] {message}{Environment.NewLine}";
File.AppendAllText(LogFilePath, line, Encoding.UTF8);
}
}
catch { }
}
private static void Rotate()
{
try
{
var fi = new FileInfo(LogFilePath);
if (fi.Exists && fi.Length > MaxBytes)
{
var bak = LogFilePath + ".1";
if (File.Exists(bak)) File.Delete(bak);
File.Move(LogFilePath, bak);
}
}
catch { }
}
public static string ReadAll()
{
try
{
lock (_lock)
{
if (!File.Exists(LogFilePath)) return "";
using (var fs = new FileStream(LogFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
using (var sr = new StreamReader(fs, Encoding.UTF8))
return sr.ReadToEnd();
}
}
catch { return ""; }
}
public static void Clear()
{
try
{
lock (_lock)
{
if (File.Exists(LogFilePath)) File.Delete(LogFilePath);
var bak = LogFilePath + ".1";
if (File.Exists(bak)) File.Delete(bak);
}
}
catch { }
}
}
}
@@ -7,9 +7,9 @@
<AssemblyTitle>Starface Outlook Sync</AssemblyTitle>
<Company>HackerSoft - Hacker-Net Telekommunikation</Company>
<Product>Starface Outlook Sync</Product>
<Version>0.0.0.28</Version>
<AssemblyVersion>0.0.0.28</AssemblyVersion>
<FileVersion>0.0.0.28</FileVersion>
<Version>0.0.0.29</Version>
<AssemblyVersion>0.0.0.29</AssemblyVersion>
<FileVersion>0.0.0.29</FileVersion>
<Description>Synchronisiert Outlook-Kontakte mit Starface Telefonanlage</Description>
<Copyright>Stefan Hacker - HackerSoft</Copyright>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
+1 -1
View File
@@ -27,7 +27,7 @@ namespace StarfaceOutlookSync.UI
var lblVersion = new Label
{
Text = "Version 0.0.0.28",
Text = "Version 0.0.0.29",
Left = 0, Top = 56, Width = 340, Height = 20,
TextAlign = ContentAlignment.MiddleCenter,
ForeColor = Color.Gray
@@ -0,0 +1,90 @@
using System;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
using StarfaceOutlookSync.Services;
namespace StarfaceOutlookSync.UI
{
/// <summary>Zeigt das Sync-Protokoll an (read-only) mit Aktualisieren/Leeren.</summary>
public class LogViewerForm : Form
{
private TextBox _txt;
private Button _btnClose;
private Panel _panel;
public LogViewerForm()
{
Text = "Protokoll";
Size = new Size(720, 500);
StartPosition = FormStartPosition.CenterParent;
Font = new Font("Segoe UI", 9);
_txt = new TextBox
{
Multiline = true,
ReadOnly = true,
ScrollBars = ScrollBars.Both,
WordWrap = false,
Dock = DockStyle.Fill,
BackColor = Color.White,
Font = new Font("Consolas", 9)
};
_panel = new Panel { Dock = DockStyle.Bottom, Height = 44 };
var btnRefresh = new Button { Text = "Aktualisieren", Left = 10, Top = 8, Width = 100, Height = 28 };
btnRefresh.Click += (s, e) => LoadLog();
var btnClear = new Button { Text = "Leeren", Left = 118, Top = 8, Width = 80, Height = 28 };
btnClear.Click += (s, e) =>
{
if (MessageBox.Show("Protokoll wirklich leeren?", "Protokoll",
MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
{
Logger.Clear();
LoadLog();
}
};
var btnFolder = new Button { Text = "Ordner oeffnen", Left = 206, Top = 8, Width = 120, Height = 28 };
btnFolder.Click += (s, e) => OpenFolder();
_btnClose = new Button
{
Text = "Schliessen", Top = 8, Width = 100, Height = 28,
Anchor = AnchorStyles.Top | AnchorStyles.Right
};
_btnClose.Click += (s, e) => Close();
_panel.Controls.AddRange(new Control[] { btnRefresh, btnClear, btnFolder, _btnClose });
Controls.Add(_txt);
Controls.Add(_panel);
Load += (s, e) =>
{
_btnClose.Left = _panel.ClientSize.Width - _btnClose.Width - 10;
LoadLog();
};
}
private void LoadLog()
{
_txt.Text = Logger.ReadAll();
_txt.SelectionStart = _txt.TextLength;
_txt.ScrollToCaret();
}
private void OpenFolder()
{
try
{
var dir = Path.GetDirectoryName(Logger.LogFilePath);
Process.Start(new ProcessStartInfo { FileName = dir, UseShellExecute = true });
}
catch { }
}
}
}
+27 -2
View File
@@ -20,7 +20,7 @@ namespace StarfaceOutlookSync.UI
private NotifyIcon _trayIcon;
private ContextMenuStrip _trayMenu;
private ListView _profileList;
private Button _btnNew, _btnEdit, _btnDelete, _btnSync, _btnReset, _btnSettings, _btnInfo;
private Button _btnNew, _btnEdit, _btnDelete, _btnSync, _btnReset, _btnSettings, _btnLog, _btnInfo;
private StatusStrip _statusBar;
private ToolStripStatusLabel _statusLabel;
private Timer _autoSyncTimer;
@@ -129,10 +129,13 @@ namespace StarfaceOutlookSync.UI
_btnSettings = new Button { Text = "Einstellungen", Width = 95, Height = 30 };
_btnSettings.Click += (s, e) => ShowSettings();
_btnLog = new Button { Text = "Protokoll", Width = 80, Height = 30 };
_btnLog.Click += (s, e) => ShowLog();
_btnInfo = new Button { Text = "Info", Width = 50, Height = 30 };
_btnInfo.Click += (s, e) => ShowAbout();
buttonPanel.Controls.AddRange(new Control[] { _btnNew, _btnEdit, _btnDelete, _btnSync, _btnReset, _btnSettings, _btnInfo });
buttonPanel.Controls.AddRange(new Control[] { _btnNew, _btnEdit, _btnDelete, _btnSync, _btnReset, _btnSettings, _btnLog, _btnInfo });
// Statusbar
_statusBar = new StatusStrip();
@@ -358,9 +361,12 @@ namespace StarfaceOutlookSync.UI
if (crossLock == null)
{
SetStatus("Anderer Arbeitsplatz synchronisiert gerade - uebersprungen.");
Logger.Log($"Sync '{profile.Name}' uebersprungen: anderer Arbeitsplatz synct gerade.");
return;
}
Logger.Log($"Sync gestartet: '{profile.Name}' (Richtung: {profile.SyncDirection})");
// Konflikt-Hinweise von ANDEREN Arbeitsplaetzen anzeigen, BEVOR der
// Sync den eigenen Stand auf den Gewinner-Wert aktualisiert (sonst
// koennte man nicht mehr erkennen, ob man betroffen war).
@@ -379,6 +385,10 @@ namespace StarfaceOutlookSync.UI
_trayIcon.ShowBalloonTip(3000, "Starface Sync", msg,
result.Errors > 0 ? ToolTipIcon.Warning : ToolTipIcon.Info);
Logger.Log($"Sync fertig: {msg}");
foreach (var em in result.ErrorMessages)
Logger.Log($" Fehler: {em}");
// Echte Feld-Konflikte gesondert melden, damit der Benutzer weiss,
// dass ein Wert ueberschrieben wurde.
if (result.Conflicts.Count > 0)
@@ -389,6 +399,9 @@ namespace StarfaceOutlookSync.UI
_trayIcon.ShowBalloonTip(10000,
$"Konflikt bei {result.Conflicts.Count} Kontakt(en)", detail, ToolTipIcon.Warning);
foreach (var c in result.Conflicts)
Logger.Log($" KONFLIKT: {c}");
// Andere Arbeitsplaetze ueber das gemeinsame Verzeichnis informieren.
_conflictNotifier.Write(sharedDir, result.Conflicts);
}
@@ -400,6 +413,7 @@ namespace StarfaceOutlookSync.UI
_trayIcon.ShowBalloonTip(3000, "Starface Sync Fehler",
ex.Message, ToolTipIcon.Error);
SetStatus($"Fehler: {ex.Message}");
Logger.Log($"Sync FEHLER '{profile.Name}': {ex.Message}");
}
finally
{
@@ -461,6 +475,9 @@ namespace StarfaceOutlookSync.UI
var pending = _conflictNotifier.GetPending(sharedDir, MyContactsByStarfaceId());
if (pending.Count == 0) return;
foreach (var p in pending)
Logger.Log($" KONFLIKT (anderer Arbeitsplatz): {p}");
var detail = string.Join("\n", pending.Take(5).Select(p => p.ToString()));
if (pending.Count > 5)
detail += $"\n... und {pending.Count - 5} weitere";
@@ -513,6 +530,14 @@ namespace StarfaceOutlookSync.UI
}
}
private void ShowLog()
{
using (var log = new LogViewerForm())
{
log.ShowDialog(this);
}
}
private void ExitApplication()
{
_autoSyncTimer?.Stop();