Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
LogService.cs
1using System.Diagnostics;
2using System.Runtime.CompilerServices;
3using System.Text;
5using Waher.Events;
12using System.Threading; // Added for OperationCanceledException
13
15{
16 [Singleton]
17 internal sealed class LogService : LoadableService, ILogService
18 {
19 private const string startupCrashFileName = "CrashDump.txt";
20 private const string debugLogFileName = "Log.txt";
21 private string bareJid = string.Empty;
22 private bool repairRequested = false;
23
24 private TextWriterEventSink? debugSink;
25 private TextWriter? debugTextWriter;
26 private FileStream? debugFileStream;
27
33 public void AddListener(IEventSink EventSink)
34 {
36 this.bareJid = XmppEventSink.Client?.BareJID ?? string.Empty;
37
38 foreach (IEventSink Sink in Log.Sinks)
39 {
40 if (Sink == EventSink)
41 return;
42 }
44 }
45
50 public void RemoveListener(IEventSink EventSink)
51 {
52 if (EventSink is not null)
54 }
55
61 public void LogDebug(string Message,
62 params KeyValuePair<string, object?>[] Tags)
63 {
64 Log.Debug(Message,
65 string.Empty,
66 this.bareJid,
67 Tags);
68 }
69
76 public void LogDebug(string Message,
77 [CallerFilePath] string FilePath = "",
78 [CallerLineNumber] int LineNumber = 0)
79 {
80 Log.Debug($"{Message} \n (File: {FilePath}, Line: {LineNumber})",
81 string.Empty,
82 this.bareJid,
83 []);
84 }
85
86
92 public void LogInformational(string Message, params KeyValuePair<string, object?>[] Tags)
93 {
94 Log.Informational(Message, string.Empty, this.bareJid, [.. this.GetParameters(Tags)]);
95 }
96
102 public void LogWarning(string Message, params KeyValuePair<string, object?>[] Tags)
103 {
104 Log.Warning(Message, string.Empty, this.bareJid, [.. this.GetParameters(Tags)]);
105 }
106
111 public void LogException(Exception ex)
112 {
113 this.LogException(ex, []);
114 }
115
121 public void LogException(Exception ex, params KeyValuePair<string, object?>[] extraParameters)
122 {
123 // Treat cooperative cancellation as normal flow with minimal noise.
124 if (ex is OperationCanceledException)
125 {
126 // Log a concise informational entry instead of a full exception + stack trace.
127 this.LogInformational("Operation canceled.", [.. this.GetParameters(extraParameters)]);
128 return;
129 }
130
131 ex = Log.UnnestException(ex);
132
133 Debug.WriteLine(ex.ToString());
134 Log.Exception(ex, string.Empty, this.bareJid, [.. this.GetParameters(extraParameters)]);
135
136 if (ex is InconsistencyException && !this.repairRequested)
137 {
138 this.repairRequested = true;
139 Task.Run(RestartForRepair);
140 }
141 }
142
148 public void LogAlert(string Message, params KeyValuePair<string, object?>[] Tags)
149 {
150 Log.Alert(Message, string.Empty, this.bareJid, [.. this.GetParameters(Tags)]);
151 }
152
153 private static async Task RestartForRepair()
154 {
155 ServiceRef.StorageService.FlagForRepair();
156
157 await ServiceRef.UiService.DisplayAlert(ServiceRef.Localizer[nameof(AppResources.ErrorTitle)],
158 ServiceRef.Localizer[nameof(AppResources.RepairRestart)],
159 ServiceRef.Localizer[nameof(AppResources.Ok)]);
160
161 try
162 {
163 await ServiceRef.PlatformSpecific.CloseApplication();
164 }
165 catch (Exception)
166 {
167 Environment.Exit(0);
168 }
169 }
170
176 public void SaveExceptionDump(string Title, string StackTrace)
177 {
178 StackTrace = Log.CleanStackTrace(StackTrace);
179
180 string Contents;
181 string FileName = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), startupCrashFileName);
182
183 if (File.Exists(FileName))
184 Contents = File.ReadAllText(FileName);
185 else
186 Contents = string.Empty;
187
188 File.WriteAllText(FileName, Title + Environment.NewLine + StackTrace + Environment.NewLine + Contents);
189 }
190
195 public string LoadExceptionDump()
196 {
197 string contents;
198 string fileName = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), startupCrashFileName);
199
200 if (File.Exists(fileName))
201 contents = File.ReadAllText(fileName);
202 else
203 contents = string.Empty;
204
205 return contents;
206 }
207
211 public void DeleteExceptionDump()
212 {
213 string fileName = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), startupCrashFileName);
214
215 if (File.Exists(fileName))
216 File.Delete(fileName);
217 }
218
224 public IList<KeyValuePair<string, object?>> GetParameters(params KeyValuePair<string, object?>[] Tags)
225 {
226 List<KeyValuePair<string, object?>> Result =
227 [
228 new KeyValuePair<string, object?>("Platform", DeviceInfo.Platform),
229 new KeyValuePair<string, object?>("RuntimeVersion", typeof(LogService).Assembly.ImageRuntimeVersion),
230 new KeyValuePair<string, object?>("AppVersion", AppInfo.VersionString),
231 new KeyValuePair<string, object?>("Manufacturer", DeviceInfo.Manufacturer),
232 new KeyValuePair<string, object?>("Device Model", DeviceInfo.Model),
233 new KeyValuePair<string, object?>("Device Name", DeviceInfo.Name),
234 new KeyValuePair<string, object?>("OS", DeviceInfo.VersionString),
235 new KeyValuePair<string, object?>("Platform", DeviceInfo.Platform.ToString()),
236 new KeyValuePair<string, object?>("Device Type", DeviceInfo.DeviceType.ToString()),
237 ];
238
239 if (Tags is not null)
240 Result.AddRange(Tags);
241
242 return Result;
243 }
244
245 public override Task Load(bool isResuming, CancellationToken cancellationToken)
246 {
247#if DEBUG
248 this.AddListener(new DebugEventSink());
249#endif
250 return Task.CompletedTask;
251 }
252 public async Task StartDebugLogSessionAsync()
253 {
254 string FileName = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), debugLogFileName);
255
256 // If an old session is still open, end it first
257 if (this.debugSink is not null)
258 await this.EndDebugLogSessionAsync();
259
260 if (File.Exists(FileName))
261 File.Delete(FileName);
262
263 // Create and keep references
264 this.debugFileStream = new FileStream(FileName, FileMode.Create, FileAccess.Write, FileShare.Read);
265 this.debugTextWriter = new StreamWriter(this.debugFileStream, Encoding.UTF8)
266 {
267 AutoFlush = true
268 };
269 this.debugSink = new TextWriterEventSink(debugLogFileName, this.debugTextWriter);
270
271 this.AddListener(this.debugSink);
272 }
273
274 public async Task EndDebugLogSessionAsync()
275 {
276 if (this.debugSink is null)
277 return;
278
279 // Unregister the sink so no more events are written
280 this.RemoveListener(this.debugSink);
281
282 // Dispose the sink (it doesn't own the writer, so no-op, but good practice)
283 await this.debugSink.DisposeAsync();
284 this.debugSink = null;
285
286 // Dispose the writer and the file stream
287 this.debugTextWriter?.Dispose();
288 this.debugTextWriter = null;
289
290 this.debugFileStream?.Dispose();
291 this.debugFileStream = null;
292 }
293
297 public void Dispose()
298 {
299 // If a debug session is still open, clean it up synchronously
300 if (this.debugSink is not null || this.debugTextWriter is not null || this.debugFileStream is not null)
301 {
302 // Block on the async cleanup
303 this.EndDebugLogSessionAsync().GetAwaiter().GetResult();
304 }
305
306 GC.SuppressFinalize(this);
307 }
308 }
309}
A strongly-typed resource class, for looking up localized strings, etc.
static string RepairRestart
Looks up a localized string similar to An inconsistency in the internal database has been detected....
static string Ok
Looks up a localized string similar to OK.
static string ErrorTitle
Looks up a localized string similar to An error has occurred.
Outputs events to the console standard output.
Base class for event sinks.
Definition: EventSink.cs:9
Outputs sniffed data to a text writer.
override Task DisposeAsync()
IDisposableAsync.DisposeAsync
Filters incoming events and passes remaining events to a secondary event sink.
Definition: EventFilter.cs:11
IEventSink SecondarySink
Secondary event sink receiving the events passing the filter.
Definition: EventFilter.cs:243
Static class managing the application event log. Applications and services log events on this static ...
Definition: Log.cs:14
static string CleanStackTrace(string StackTrace)
Cleans a Stack Trace string, removing entries from the asynchronous execution model,...
Definition: Log.cs:194
static IEventSink[] Sinks
Registered sinks.
Definition: Log.cs:132
static void Register(IEventSink EventSink)
Registers an event sink with the event log. Call Unregister(IEventSink) to unregister it,...
Definition: Log.cs:30
static void Exception(Exception Exception, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, params KeyValuePair< string, object >[] Tags)
Logs an exception. Event type will be determined by the severity of the exception.
Definition: Log.cs:1657
static void Warning(string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Logs a warning event.
Definition: Log.cs:576
static void Informational(string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Logs an informational event.
Definition: Log.cs:344
static Exception UnnestException(Exception Exception)
Unnests an exception, to extract the relevant inner exception.
Definition: Log.cs:828
static bool Unregister(IEventSink EventSink)
Unregisters an event sink from the event log.
Definition: Log.cs:47
static void Debug(string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Logs a debug event.
Definition: Log.cs:228
static void Alert(string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Logs an alert event.
Definition: Log.cs:1237
Event sink sending events to a destination over the XMPP network.
XmppClient Client
XMPP Client
Database inconsistency exception. Raised when an inconsistency in the database has been found.
Interface for all event sinks.
Definition: IEventSink.cs:9