Compare commits

...

9 Commits

Author SHA1 Message Date
RMBGAME 0e813d4d24 🐛 Misc 2024-08-29 23:22:28 +08:00
Aigio Liu 7b7ed945d4 🎨 LogConsoleService.ConsoleMaxCharCount 2024-08-29 22:14:42 +08:00
Aigio Liu 585a7f01d0 🎨 Log format 2024-08-29 20:33:10 +08:00
RMBGAME 803d17a5f8 🐛 Misc 2024-08-29 19:10:45 +08:00
Aigio Liu c6a2cd67b4 🐛 WebApplication Logging 2024-08-29 16:32:14 +08:00
RMBGAME e3ef74f08e 💄 ProxyLog UI 2024-08-29 15:56:36 +08:00
RMBGAME b43cc93215 Merge branch 'develop' into feature/ipc-console-log 2024-08-29 13:56:51 +08:00
Aigio Liu 6149625373 🐛 ref 2024-08-28 01:54:58 +08:00
Aigio Liu c6f6dbd7ef 🎨 LogConsole 2024-08-28 01:50:09 +08:00
18 changed files with 636 additions and 12 deletions
@@ -76,6 +76,9 @@
<Compile Include="..\BD.WTTS.Client\Services\Platform\IPlatformService.OS.Linux.Etc.Os_Release.cs"> <Compile Include="..\BD.WTTS.Client\Services\Platform\IPlatformService.OS.Linux.Etc.Os_Release.cs">
<LinkBase>Services\Platform</LinkBase> <LinkBase>Services\Platform</LinkBase>
</Compile> </Compile>
<Compile Include="..\BD.WTTS.Client\Services\Mvvm\LogConsoleService.Logger.cs">
<LinkBase>Services\Mvvm</LinkBase>
</Compile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@@ -104,6 +107,7 @@
<PackageReference Include="System.Drawing.Common" /> <PackageReference Include="System.Drawing.Common" />
<PackageReference Include="System.Runtime.Caching" /> <PackageReference Include="System.Runtime.Caching" />
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" /> <PackageReference Include="Microsoft.Bcl.AsyncInterfaces" />
<PackageReference Include="Utf8StringInterpolation" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="$([MSBuild]::IsOSPlatform('windows')) AND ($(RuntimeIdentifier.StartsWith('win-')) OR $(RuntimeIdentifier) == '')"> <ItemGroup Condition="$([MSBuild]::IsOSPlatform('windows')) AND ($(RuntimeIdentifier.StartsWith('win-')) OR $(RuntimeIdentifier) == '')">
@@ -81,6 +81,7 @@ static void ConfigureServices(IServiceCollection services)
#if DEBUG #if DEBUG
l.AddConsole(); l.AddConsole();
#endif #endif
l.AddProvider(new LogConsoleService.Utf8StringLoggerProvider(moduleName));
}); });
// 设置仓储层数据库文件存放路径 // 设置仓储层数据库文件存放路径
@@ -23,6 +23,8 @@ sealed class LazyReverseProxyServiceImpl : IReverseProxyService
public byte[]? GetFlowStatistics_Bytes() => impl().GetFlowStatistics_Bytes(); public byte[]? GetFlowStatistics_Bytes() => impl().GetFlowStatistics_Bytes();
public string? GetLogAllMessage() => impl().GetLogAllMessage();
public async Task<StartProxyResult> StartProxyAsync(byte[] reverseProxySettings) public async Task<StartProxyResult> StartProxyAsync(byte[] reverseProxySettings)
{ {
var result = await impl().StartProxyAsync(reverseProxySettings); var result = await impl().StartProxyAsync(reverseProxySettings);
@@ -65,6 +65,8 @@ sealed partial class YarpReverseProxyServiceImpl : ReverseProxyServiceImpl, IRev
WebRootPath = RootPath, WebRootPath = RootPath,
}); });
builder.Logging.AddProvider(new LogConsoleService.Utf8StringLoggerProvider(AssemblyInfo.Accelerator));
builder.Services.Configure<HostFilteringOptions>(static o => builder.Services.Configure<HostFilteringOptions>(static o =>
{ {
o.AllowEmptyHosts = true; o.AllowEmptyHosts = true;
@@ -167,6 +169,12 @@ sealed partial class YarpReverseProxyServiceImpl : ReverseProxyServiceImpl, IRev
return bytes; return bytes;
} }
public string? GetLogAllMessage()
{
var result = LogConsoleService.Builder;
return result.ToString();
}
// IDisposable // IDisposable
protected override void DisposeCore() protected override void DisposeCore()
@@ -15,6 +15,12 @@ partial interface IReverseProxyService
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
byte[]? GetFlowStatistics_Bytes(); byte[]? GetFlowStatistics_Bytes();
/// <summary>
/// 获取所有日志信息
/// </summary>
/// <returns></returns>
string? GetLogAllMessage();
} }
public static partial class ReverseProxyServiceExtensions public static partial class ReverseProxyServiceExtensions
@@ -1,14 +1,10 @@
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Data;
using Avalonia.Threading; using Avalonia.Threading;
using Avalonia.VisualTree; using Avalonia.VisualTree;
using LiveChartsCore; using LiveChartsCore;
using LiveChartsCore.Motion;
using LiveChartsCore.SkiaSharpView; using LiveChartsCore.SkiaSharpView;
using LiveChartsCore.SkiaSharpView.Avalonia; using LiveChartsCore.SkiaSharpView.Avalonia;
using LiveChartsCore.SkiaSharpView.Drawing;
using SkiaSharp;
namespace BD.WTTS.UI.Views.Controls; namespace BD.WTTS.UI.Views.Controls;
@@ -0,0 +1,20 @@
<UserControl
x:Class="BD.WTTS.UI.Views.Controls.ProxyLog"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:AvaloniaEdit="using:AvaloniaEdit"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:s="https://steampp.net/services"
xmlns:spp="https://steampp.net/ui"
d:DesignHeight="450"
d:DesignWidth="800"
mc:Ignorable="d">
<Border Background="{DynamicResource CardBackgroundFillColorDefaultBrush}" CornerRadius="{DynamicResource ControlCornerRadius}">
<AvaloniaEdit:TextEditor
x:Name="LogTextbox"
HorizontalScrollBarVisibility="Disabled"
IsReadOnly="True"
ShowLineNumbers="True" />
</Border>
</UserControl>
@@ -0,0 +1,92 @@
using Avalonia.Controls;
using Avalonia.Threading;
using Avalonia.VisualTree;
using AvaloniaEdit;
using AvaloniaEdit.TextMate;
using Org.BouncyCastle.Math;
using TextMateSharp.Grammars;
namespace BD.WTTS.UI.Views.Controls;
public partial class ProxyLog : UserControl
{
CancellationTokenSource cancellation = new();
public ProxyLog()
{
InitializeComponent();
var logTextbox = this.FindControl<TextEditor>("LogTextbox")!;
//Here we initialize RegistryOptions with the theme we want to use.
var registryOptions = new TextMateSharp.Grammars.RegistryOptions(ThemeName.Dark);
//Initial setup of TextMate.
var textMateInstallation = logTextbox.InstallTextMate(registryOptions);
//Here we are getting the language by the extension and right after that we are initializing grammar with this language.
//And that's all 😀, you are ready to use AvaloniaEdit with syntax highlighting!
textMateInstallation.SetGrammar(registryOptions.GetScopeByLanguageId(registryOptions.GetLanguageByExtension(".log").Id));
//logTextbox.Text = LogText;
//logTextbox.ScrollToLine(logTextbox.Document.LineCount);
logTextbox.TextChanged += LogTextbox_TextChanged;
ProxyService.Current.WhenAnyValue(x => x.ProxyStatus)
.Subscribe(x =>
{
if (x)
{
cancellation = new CancellationTokenSource();
Task2.InBackground(FlushLogsAsync, true);
}
else
{
if (cancellation != null)
{
cancellation.Cancel();
cancellation.Dispose();
}
}
});
}
private void LogTextbox_TextChanged(object? sender, EventArgs e)
{
LogTextbox.ScrollToLine(LogTextbox.Document.LineCount);
}
void FlushLogsAsync()
{
while (!cancellation.IsCancellationRequested)
{
try
{
var isAttachedToVisualTree = this.IsAttachedToVisualTree();
if (isAttachedToVisualTree)
{
var logtext = IReverseProxyService.Constants.Instance.GetLogAllMessage();
if (string.IsNullOrEmpty(logtext))
{
Thread.Sleep(1000);
continue;
}
Dispatcher.UIThread.Post(() =>
{
if (LogTextbox.Text.Length != logtext.Length)
LogTextbox.Text = logtext;
});
}
}
catch
{
}
finally
{
Thread.Sleep(1000);
}
}
}
}
@@ -449,6 +449,7 @@
</Border> </Border>
</WrapPanel>--> </WrapPanel>-->
</TabStripItem> </TabStripItem>
<TabStripItem Content="{x:Static spp:Strings.CommunityFix_ProxyLog}" IsVisible="{Binding Source={x:Static s:ProxyService.Current}, Path=ProxyStatus, Mode=OneWay}" />
<TabStripItem Content="{x:Static spp:Strings.CommunityFix_FlowStatistics}" IsVisible="{Binding Source={x:Static s:ProxyService.Current}, Path=ProxyStatus, Mode=OneWay}" /> <TabStripItem Content="{x:Static spp:Strings.CommunityFix_FlowStatistics}" IsVisible="{Binding Source={x:Static s:ProxyService.Current}, Path=ProxyStatus, Mode=OneWay}" />
<TabStripItem Content="{x:Static spp:Strings.CommunityFix_Services}" IsVisible="{Binding Source={x:Static s:ProxyService.Current}, Path=ProxyStatus, Mode=OneWay}" /> <TabStripItem Content="{x:Static spp:Strings.CommunityFix_Services}" IsVisible="{Binding Source={x:Static s:ProxyService.Current}, Path=ProxyStatus, Mode=OneWay}" />
<TabStripItem Name="GameAccTab"> <TabStripItem Name="GameAccTab">
@@ -557,6 +558,8 @@
</ItemsControl> </ItemsControl>
</spp:ContentLoader> </spp:ContentLoader>
</ScrollViewer> </ScrollViewer>
<!-- 加速日志 -->
<spp:ProxyLog Margin="5,0" />
<!-- 流量统计 --> <!-- 流量统计 -->
<spp:ProxyChartView Margin="5,0" /> <spp:ProxyChartView Margin="5,0" />
@@ -86,6 +86,22 @@ partial interface IApplication
//#elif MACOS || MACCATALYST || (IOS && DEBUG) //#elif MACOS || MACCATALYST || (IOS && DEBUG)
// builder.AddProvider(Logging.OSLogLoggerProvider.Instance); // builder.AddProvider(Logging.OSLogLoggerProvider.Instance);
//#endif //#endif
//#if !ANDROID && !IOS
// var isBackend = false;
// try
// {
// isBackend = Startup.Instance.HasIPCRoot;
// }
// catch
// {
// }
// if (isBackend)
// {
// builder.AddProvider(new LogConsoleService.Utf8StringLoggerProvider(IPlatformService.IPCRoot.moduleName));
// }
//#endif
}; };
} }
+1
View File
@@ -122,6 +122,7 @@
<PackageReference Include="SkiaSharp" /> <PackageReference Include="SkiaSharp" />
<PackageReference Include="SkiaSharp.HarfBuzz" /> <PackageReference Include="SkiaSharp.HarfBuzz" />
<PackageReference Include="HarfBuzzSharp" /> <PackageReference Include="HarfBuzzSharp" />
<PackageReference Include="Utf8StringInterpolation" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) != 'windows'"> <ItemGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) != 'windows'">
+2 -8
View File
@@ -7,14 +7,8 @@ namespace BD.WTTS.Logging;
/// </summary> /// </summary>
public abstract class ClientLogger : ILogger public abstract class ClientLogger : ILogger
{ {
static readonly string _messagePadding; static readonly string _messagePadding = new string(' ', 6);
static readonly string _newLineWithMessagePadding; static readonly string _newLineWithMessagePadding = Environment.NewLine + _messagePadding;
static ClientLogger()
{
_messagePadding = new string(' ', 6);
_newLineWithMessagePadding = Environment.NewLine + _messagePadding;
}
protected readonly string name; protected readonly string name;
+9
View File
@@ -1358,6 +1358,15 @@ namespace BD.WTTS.Client.Resources {
} }
} }
/// <summary>
/// 查找类似 加速日志 的本地化字符串。
/// </summary>
public static string CommunityFix_ProxyLog {
get {
return ResourceManager.GetString("CommunityFix_ProxyLog", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 加速模式 的本地化字符串。 /// 查找类似 加速模式 的本地化字符串。
/// </summary> /// </summary>
@@ -2611,6 +2611,9 @@
<data name="CommunityFix_AlreadyProxy" xml:space="preserve"> <data name="CommunityFix_AlreadyProxy" xml:space="preserve">
<value>已加速 </value> <value>已加速 </value>
<comment>Comment=末尾带空格;</comment> <comment>Comment=末尾带空格;</comment>
</data>
<data name="CommunityFix_ProxyLog" xml:space="preserve">
<value>加速日志</value>
</data> </data>
<data name="CommunityFix_FlowStatistics" xml:space="preserve"> <data name="CommunityFix_FlowStatistics" xml:space="preserve">
<value>流量统计</value> <value>流量统计</value>
@@ -450,6 +450,13 @@ public sealed partial class IPCMainProcessServiceImpl : IPCMainProcessService
return result.All(static x => x.result); return result.All(static x => x.result);
} }
public void WriteMessage(string? moduleName, byte[] bytes)
{
#if !ANDROID && !IOS
LogConsoleService.Current.WriteMessage(moduleName, bytes);
#endif
}
/// <summary> /// <summary>
/// 配置服务 /// 配置服务
/// </summary> /// </summary>
@@ -67,4 +67,6 @@ public interface IPCMainProcessService : IAsyncDisposable
/// <param name="moduleName"></param> /// <param name="moduleName"></param>
/// <returns></returns> /// <returns></returns>
ValueTask<T?> GetServiceAsync<T>(string moduleName) where T : class; ValueTask<T?> GetServiceAsync<T>(string moduleName) where T : class;
void WriteMessage(string? moduleName, byte[] bytes);
} }
@@ -0,0 +1,362 @@
#if !ANDROID && !IOS
using Utf8StringInterpolation;
// ReSharper disable once CheckNamespace
namespace BD.WTTS.Services;
partial class LogConsoleService
{
/// <summary>
/// 控制台显示最大字符数
/// </summary>
const int ConsoleMaxCharCount = 10_0000;
static readonly int ConsoleMaxByteCount = Encoding.UTF8.GetMaxByteCount(ConsoleMaxCharCount);
/// <summary>
/// 日志源
/// </summary>
[DebuggerDisplay("{DebuggerDisplay(),nq}")]
internal sealed partial class Source
{
List<byte[]>? chunk;
(string? str, long byteLength)? message = null;
#if DEBUG
readonly string moduleName;
public Source(string moduleName)
{
this.moduleName = moduleName;
}
string DebuggerDisplay() =>
$"""
moduleName: {moduleName}
ByteLength: {ByteLength}
{ToString()}
""";
#endif
public long ByteLength { get; private set; }
public void BuilderAppend(byte[] value)
{
(chunk ??= new()).Add(value);
ByteLength += value.Length;
}
void BuilderClear()
{
if (chunk != null && chunk.Count > 1)
{
// 保留最后 21.3% 的日志
var keppLength = (int)MathF.Floor(chunk.Count * .213f);
if (keppLength <= 0 || keppLength >= chunk.Count)
{
keppLength = 1;
}
chunk = chunk.TakeLast(keppLength).ToList();
}
message = null;
ByteLength = 0;
}
/// <summary>
/// 追加收到的日志 UTF-8 字节
/// </summary>
public void Append(byte[]? value)
{
if (value != null && value.Length > 0)
{
if (ByteLength > ConsoleMaxByteCount)
{
BuilderClear();
}
// 追加收到的日志 UTF-8 字节
BuilderAppend(value);
}
}
public override string? ToString()
{
if (message != null)
{
if (ByteLength == message.Value.byteLength)
{
// 根据 len 判断是否使用缓存
return message.Value.str;
}
}
if (chunk == null)
{
return null;
}
else if (chunk.Count <= 0)
{
return string.Empty;
}
using var buffer = Utf8String.CreateWriter(out var writer);
foreach (var it in chunk)
{
// 拼接字符串
writer.AppendUtf8(it);
}
writer.Flush();
var u8_bytes = buffer.ToArray();
chunk = [u8_bytes];
var result = Encoding.UTF8.GetString(u8_bytes);
// 设置缓存值
message = new(result, ByteLength);
return result;
}
}
#region 服务进程部分
static readonly string _messagePadding = new string(' ', 6);
static readonly string _newLineWithMessagePadding = Environment.NewLine + _messagePadding;
static byte[] CreateDefaultLogMessage(string logName, int eventId, string? message, Exception? exception)
{
// https://github.com/dotnet/extensions/blob/v3.1.5/src/Logging/Logging.Console/src/ConsoleLogger.cs
// TODO: 对比 SimpleConsoleFormatter 实现带 Color 的版本
// https://github.com/dotnet/runtime/blob/v9.0.0-preview.7.24405.7/src/libraries/Microsoft.Extensions.Logging.Console/src/SimpleConsoleFormatter.cs
if (!string.IsNullOrEmpty(message))
{
StringBuilder builder = new();
CreateDefaultLogMessage(builder, logName, eventId, message, exception);
var str = builder.ToString();
return Encoding.UTF8.GetBytes(str);
}
else
{
using var buffer = Utf8String.CreateWriter(out var logBuilder);
// Example:
// INFO: ConsoleApp.Program[10]
// Request received
// category and event id
logBuilder.Append(logName);
logBuilder.Append('[');
logBuilder.Append(eventId.ToString());
logBuilder.AppendLine("]");
// Example:
// System.InvalidOperationException
// at Namespace.Class.Function() in File:line X
if (exception != null)
// exception message
logBuilder.AppendLine(exception.ToString());
logBuilder.Flush();
var result = buffer.ToArray();
return result;
}
}
static void CreateDefaultLogMessage(StringBuilder logBuilder, string logName, int eventId, string? message, Exception? exception)
{
// Example:
// INFO: ConsoleApp.Program[10]
// Request received
// category and event id
logBuilder.Append(logName);
logBuilder.Append('[');
logBuilder.Append(eventId);
logBuilder.AppendLine("]");
if (!string.IsNullOrEmpty(message))
{
// message
logBuilder.Append(_messagePadding);
var len = logBuilder.Length;
logBuilder.AppendLine(message);
logBuilder.Replace(Environment.NewLine, _newLineWithMessagePadding, len, message.Length);
}
// Example:
// System.InvalidOperationException
// at Namespace.Class.Function() in File:line X
if (exception != null)
// exception message
logBuilder.AppendLine(exception.ToString());
}
static byte[] CreateDefaultLogMessageWithNLogFileLayout(LogLevel logLevel, string logName, int eventId, string? message, Exception? exception)
{
// Layout = "
// ${longdate}|
// ${level}|
// ${logger}|${message} |${all-event-properties} ${exception:format=tostring}",
using var buffer = Utf8String.CreateWriter(out var logBuilder);
logBuilder.Append(DateTime.Now.ToString(
"yyyy-MM-dd HH:mm:ss.fff",
CultureInfo.InvariantCulture));
logBuilder.AppendUtf8("|"u8);
switch (logLevel) // see NLog.LogLevel
{
case LogLevel.Trace:
logBuilder.AppendUtf8("Trace"u8);
break;
case LogLevel.Debug:
logBuilder.AppendUtf8("Debug"u8);
break;
case LogLevel.Information:
logBuilder.AppendUtf8("Info"u8);
break;
case LogLevel.Warning:
logBuilder.AppendUtf8("Warn"u8);
break;
case LogLevel.Error:
logBuilder.AppendUtf8("Error"u8);
break;
case LogLevel.Critical:
logBuilder.AppendUtf8("Fatal"u8);
break;
case LogLevel.None:
logBuilder.AppendUtf8("Off"u8);
break;
default:
logBuilder.Append(logLevel.ToString());
break;
}
logBuilder.AppendUtf8("|"u8);
logBuilder.Append(logName);
logBuilder.AppendUtf8("|"u8);
logBuilder.Append(message);
logBuilder.AppendUtf8("| "u8);
logBuilder.Append(exception?.ToString());
logBuilder.AppendLine();
logBuilder.Flush();
var result = buffer.ToArray();
return result;
}
#if APP_REVERSE_PROXY
internal static readonly Source Builder = new(
#if DEBUG
AssemblyInfo.Accelerator
#endif
);
#endif
internal sealed class Utf8StringLogger(string name) : ILogger
{
readonly string name = name;
public IDisposable? BeginScope<TState>(TState state) where TState : notnull
{
return NullScope.Instance;
}
public bool IsEnabled(LogLevel logLevel)
{
// TODO: 可选加入设置项
#if DEBUG
return true;
#else
return logLevel >= LogLevel.Information;
#endif
}
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
{
if (!IsEnabled(logLevel))
return;
if (formatter == null)
throw new ArgumentNullException(nameof(formatter));
var message = formatter(state, exception);
if (!string.IsNullOrEmpty(message) || exception != null)
{
#if APP_REVERSE_PROXY
var logMessageU8 = CreateDefaultLogMessageWithNLogFileLayout(logLevel, name, eventId.Id, message, exception);
Builder.Append(logMessageU8);
#else
try
{
#if APP_REVERSE_PROXY
//var s = IReverseProxyService.Instance;
// TODO Ipc
#else
var s = IPCMainProcessService.Instance;
#endif
var logMessage = CreateDefaultLogMessage(name, eventId.Id, message, exception);
s.WriteMessage(Utf8StringLoggerProvider.ModuleName, logMessage);
}
catch
{
}
#endif
}
}
}
/// <summary>
/// An empty scope without any logic
/// <para>https://github.com/dotnet/extensions/blob/v3.1.5/src/Logging/shared/NullScope.cs</para>
/// <para>https://github.com/dotnet/runtime/blob/v5.0.0-rtm.20519.4/src/libraries/Common/src/Extensions/Logging/NullScope.cs</para>
/// </summary>
sealed class NullScope : IDisposable
{
public static NullScope Instance { get; } = new();
NullScope()
{
}
public void Dispose()
{
}
}
internal sealed class Utf8StringLoggerProvider : ILoggerProvider
{
internal static string? ModuleName { get; private set; }
public Utf8StringLoggerProvider(string moduleName)
{
ModuleName = moduleName;
}
public ILogger CreateLogger(string name)
{
return new Utf8StringLogger(name);
}
void IDisposable.Dispose()
{
}
}
#endregion
}
#endif
@@ -0,0 +1,98 @@
#if !ANDROID && !IOS
using Avalonia.Threading;
using Utf8StringInterpolation;
// ReSharper disable once CheckNamespace
namespace BD.WTTS.Services;
public sealed partial class LogConsoleService : ReactiveObject
{
static readonly Lazy<LogConsoleService> mCurrent = new(() => new(), LazyThreadSafetyMode.ExecutionAndPublication);
public static LogConsoleService Current => mCurrent.Value;
LogConsoleService()
{
}
#region UI 进程部分
readonly string?[] logMessages = new string?[2];
/// <summary>
/// 加速进程的日志
/// </summary>
public string? LogMessageAccelerator => logMessages[0];
/// <summary>
/// 后端进程的日志
/// </summary>
public string? LogMessageBackEnd => logMessages[1];
/// <summary>
/// 日志源
/// </summary>
readonly Dictionary<string, Source> sources = new();
partial class Source
{
/// <summary>
/// 追加收到的日志 UTF-8 字节并触发属性通知
/// </summary>
public void AppendWithPropertyChanged(IReactiveObject o, byte[]? value,
string propertyName, ref string? propertyValue)
{
if (value != null && value.Length > 0)
{
Append(value);
var logAllMessage = ToString();
propertyValue = logAllMessage;
Dispatcher.UIThread.Invoke(() =>
{
o.RaisePropertyChanged(propertyName);
});
}
}
}
/// <summary>
/// 追加收到的日志 UTF-8 字节
/// </summary>
/// <param name="moduleName"></param>
/// <param name="value"></param>
internal void WriteMessage(string? moduleName, byte[]? value)
{
if (!string.IsNullOrWhiteSpace(moduleName) &&
value != null && value.Length > 0)
{
if (!sources.TryGetValue(moduleName, out var source))
{
source = new(
#if DEBUG
moduleName
#endif
);
sources.Add(moduleName, source);
}
switch (moduleName)
{
case AssemblyInfo.Accelerator:
source.AppendWithPropertyChanged(this, value,
nameof(LogMessageAccelerator), ref logMessages[0]);
break;
case IPlatformService.IPCRoot.moduleName:
source.AppendWithPropertyChanged(this, value,
nameof(LogMessageBackEnd), ref logMessages[1]);
break;
}
}
}
#endregion
}
#endif