diff --git a/src/Aspire.Dashboard/Aspire.Dashboard.csproj b/src/Aspire.Dashboard/Aspire.Dashboard.csproj index 4080271b4a..7ff7e519d0 100644 --- a/src/Aspire.Dashboard/Aspire.Dashboard.csproj +++ b/src/Aspire.Dashboard/Aspire.Dashboard.csproj @@ -95,6 +95,11 @@ True Routes.resx + + True + True + Logs.resx + @@ -182,6 +187,13 @@ PublicResXFileCodeGenerator Routes.Designer.cs + + Resx + EmbeddedResource + Designer + PublicResXFileCodeGenerator + Logs.Designer.cs + diff --git a/src/Aspire.Dashboard/Components/Dialogs/FilterDialog.razor b/src/Aspire.Dashboard/Components/Dialogs/FilterDialog.razor index ae0e67a95d..cd6e0122ba 100644 --- a/src/Aspire.Dashboard/Components/Dialogs/FilterDialog.razor +++ b/src/Aspire.Dashboard/Components/Dialogs/FilterDialog.razor @@ -1,10 +1,10 @@ @using Aspire.Dashboard.Model @using Aspire.Dashboard.Model.Otlp -@using Aspire.Dashboard.Otlp.Storage @using Aspire.Dashboard.Resources -@using Microsoft.FluentUI.AspNetCore.Components; @implements IDialogContentComponent + @inject IStringLocalizer Loc +@inject IStringLocalizer LogsLoc @@ -12,7 +12,7 @@ - + diff --git a/src/Aspire.Dashboard/Components/Dialogs/FilterDialog.razor.cs b/src/Aspire.Dashboard/Components/Dialogs/FilterDialog.razor.cs index fbb80f819f..1252d6791f 100644 --- a/src/Aspire.Dashboard/Components/Dialogs/FilterDialog.razor.cs +++ b/src/Aspire.Dashboard/Components/Dialogs/FilterDialog.razor.cs @@ -12,16 +12,10 @@ namespace Aspire.Dashboard.Components.Dialogs; public partial class FilterDialog { - private static readonly List> s_filterConditions = new() - { - CreateFilterSelectViewModel(FilterCondition.Equals), - CreateFilterSelectViewModel(FilterCondition.Contains), - CreateFilterSelectViewModel(FilterCondition.NotEqual), - CreateFilterSelectViewModel(FilterCondition.NotContains), - }; + private List> _filterConditions = null!; - private static SelectViewModel CreateFilterSelectViewModel(FilterCondition condition) => - new SelectViewModel { Id = condition, Name = LogFilter.ConditionToString(condition) }; + private SelectViewModel CreateFilterSelectViewModel(FilterCondition condition) => + new SelectViewModel { Id = condition, Name = LogFilter.ConditionToString(condition, LogsLoc) }; [CascadingParameter] public FluentDialog? Dialog { get; set; } @@ -37,19 +31,27 @@ private static SelectViewModel CreateFilterSelectViewModel(Filt protected override void OnInitialized() { + _filterConditions = + [ + CreateFilterSelectViewModel(FilterCondition.Equals), + CreateFilterSelectViewModel(FilterCondition.Contains), + CreateFilterSelectViewModel(FilterCondition.NotEqual), + CreateFilterSelectViewModel(FilterCondition.NotContains) + ]; + _formModel = new LogDialogFormModel(); EditContext = new EditContext(_formModel); if (Content.Filter is { } logFilter) { _formModel.Parameter = logFilter.Field; - _formModel.Condition = s_filterConditions.Single(c => c.Id == logFilter.Condition); + _formModel.Condition = _filterConditions.Single(c => c.Id == logFilter.Condition); _formModel.Value = logFilter.Value; } else { _formModel.Parameter = "Message"; - _formModel.Condition = s_filterConditions.Single(c => c.Id == FilterCondition.Contains); + _formModel.Condition = _filterConditions.Single(c => c.Id == FilterCondition.Contains); _formModel.Value = ""; } } diff --git a/src/Aspire.Dashboard/Components/Pages/StructuredLogs.razor b/src/Aspire.Dashboard/Components/Pages/StructuredLogs.razor index 1595823114..6c5e42d988 100644 --- a/src/Aspire.Dashboard/Components/Pages/StructuredLogs.razor +++ b/src/Aspire.Dashboard/Components/Pages/StructuredLogs.razor @@ -5,13 +5,14 @@ @using Aspire.Dashboard.Otlp.Model @using Aspire.Dashboard.Utils @using Microsoft.Extensions.Logging -@using System.Web @using Aspire.Dashboard.Resources @using System.Globalization @inject IJSRuntime JS @implements IDisposable + @inject IStringLocalizer Loc @inject IStringLocalizer ControlsStringsLoc +@inject IStringLocalizer LogsLoc @@ -30,7 +31,7 @@ slot="end" />
- Level: + @Loc[nameof(Dashboard.Resources.StructuredLogs.StructuredLogsLevels)] @filter.FilterText + @filter.GetDisplayText(LogsLoc) } } diff --git a/src/Aspire.Dashboard/Model/Otlp/LogFilter.cs b/src/Aspire.Dashboard/Model/Otlp/LogFilter.cs index fe1566e8de..4f3432edf2 100644 --- a/src/Aspire.Dashboard/Model/Otlp/LogFilter.cs +++ b/src/Aspire.Dashboard/Model/Otlp/LogFilter.cs @@ -4,6 +4,8 @@ using System.Diagnostics; using System.Globalization; using Aspire.Dashboard.Otlp.Model; +using Aspire.Dashboard.Resources; +using Microsoft.Extensions.Localization; namespace Aspire.Dashboard.Model.Otlp; @@ -14,7 +16,9 @@ public class LogFilter public FilterCondition Condition { get; set; } public string Value { get; set; } = default!; - public string FilterText => $"{Field} {ConditionToString(Condition)} {Value}"; + public string DebuggerDisplayText => $"{Field} {ConditionToString(Condition, null)} {Value}"; + + public string GetDisplayText(IStringLocalizer loc) => $"{Field} {ConditionToString(Condition, loc)} {Value}"; public static List GetAllPropertyNames(List propertyKeys) { @@ -23,17 +27,17 @@ public static List GetAllPropertyNames(List propertyKeys) return result; } - public static string ConditionToString(FilterCondition c) => + public static string ConditionToString(FilterCondition c, IStringLocalizer? loc) => c switch { FilterCondition.Equals => "==", - FilterCondition.Contains => "contains", + FilterCondition.Contains => loc?[nameof(Logs.LogContains)] ?? "contains", FilterCondition.GreaterThan => ">", FilterCondition.LessThan => "<", FilterCondition.GreaterThanOrEqual => ">=", FilterCondition.LessThanOrEqual => "<=", FilterCondition.NotEqual => "!=", - FilterCondition.NotContains => "not contains", + FilterCondition.NotContains => loc?[nameof(Logs.LogNotContains)] ?? "not contains", _ => throw new ArgumentOutOfRangeException(nameof(c), c, null) }; diff --git a/src/Aspire.Dashboard/Resources/Logs.Designer.cs b/src/Aspire.Dashboard/Resources/Logs.Designer.cs new file mode 100644 index 0000000000..96e82b6ee1 --- /dev/null +++ b/src/Aspire.Dashboard/Resources/Logs.Designer.cs @@ -0,0 +1,80 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Aspire.Dashboard.Resources { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + public class Logs { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Logs() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Aspire.Dashboard.Resources.Logs", typeof(Logs).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to contains. + /// + public static string LogContains { + get { + return ResourceManager.GetString("LogContains", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to not contains. + /// + public static string LogNotContains { + get { + return ResourceManager.GetString("LogNotContains", resourceCulture); + } + } + } +} diff --git a/src/Aspire.Dashboard/Resources/Logs.resx b/src/Aspire.Dashboard/Resources/Logs.resx new file mode 100644 index 0000000000..90d4b30c95 --- /dev/null +++ b/src/Aspire.Dashboard/Resources/Logs.resx @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + contains + + + not contains + corresponds to A !contains B + + diff --git a/src/Aspire.Dashboard/Resources/StructuredLogs.Designer.cs b/src/Aspire.Dashboard/Resources/StructuredLogs.Designer.cs index b8588de2e1..ccd6c167fb 100644 --- a/src/Aspire.Dashboard/Resources/StructuredLogs.Designer.cs +++ b/src/Aspire.Dashboard/Resources/StructuredLogs.Designer.cs @@ -131,6 +131,15 @@ public static string StructuredLogsLevelColumnHeader { } } + /// + /// Looks up a localized string similar to Level:. + /// + public static string StructuredLogsLevels { + get { + return ResourceManager.GetString("StructuredLogsLevels", resourceCulture); + } + } + /// /// Looks up a localized string similar to Message. /// diff --git a/src/Aspire.Dashboard/Resources/StructuredLogs.resx b/src/Aspire.Dashboard/Resources/StructuredLogs.resx index 543934dfdf..adcdb4b1d3 100644 --- a/src/Aspire.Dashboard/Resources/StructuredLogs.resx +++ b/src/Aspire.Dashboard/Resources/StructuredLogs.resx @@ -73,4 +73,7 @@ (All) + + Level: + diff --git a/src/Aspire.Dashboard/Resources/xlf/Logs.cs.xlf b/src/Aspire.Dashboard/Resources/xlf/Logs.cs.xlf new file mode 100644 index 0000000000..f7c4035bec --- /dev/null +++ b/src/Aspire.Dashboard/Resources/xlf/Logs.cs.xlf @@ -0,0 +1,17 @@ + + + + + + contains + obsahuje + + + + not contains + neobsahuje + corresponds to A !contains B + + + + \ No newline at end of file diff --git a/src/Aspire.Dashboard/Resources/xlf/Logs.de.xlf b/src/Aspire.Dashboard/Resources/xlf/Logs.de.xlf new file mode 100644 index 0000000000..dce36e8c7f --- /dev/null +++ b/src/Aspire.Dashboard/Resources/xlf/Logs.de.xlf @@ -0,0 +1,17 @@ + + + + + + contains + enthält + + + + not contains + enthält nicht + corresponds to A !contains B + + + + \ No newline at end of file diff --git a/src/Aspire.Dashboard/Resources/xlf/Logs.es.xlf b/src/Aspire.Dashboard/Resources/xlf/Logs.es.xlf new file mode 100644 index 0000000000..8ff0cae84c --- /dev/null +++ b/src/Aspire.Dashboard/Resources/xlf/Logs.es.xlf @@ -0,0 +1,17 @@ + + + + + + contains + contiene + + + + not contains + no contiene + corresponds to A !contains B + + + + \ No newline at end of file diff --git a/src/Aspire.Dashboard/Resources/xlf/Logs.fr.xlf b/src/Aspire.Dashboard/Resources/xlf/Logs.fr.xlf new file mode 100644 index 0000000000..9bce208de7 --- /dev/null +++ b/src/Aspire.Dashboard/Resources/xlf/Logs.fr.xlf @@ -0,0 +1,17 @@ + + + + + + contains + contient + + + + not contains + ne contient pas + corresponds to A !contains B + + + + \ No newline at end of file diff --git a/src/Aspire.Dashboard/Resources/xlf/Logs.it.xlf b/src/Aspire.Dashboard/Resources/xlf/Logs.it.xlf new file mode 100644 index 0000000000..0f9a9f0337 --- /dev/null +++ b/src/Aspire.Dashboard/Resources/xlf/Logs.it.xlf @@ -0,0 +1,17 @@ + + + + + + contains + contiene + + + + not contains + non contiene + corresponds to A !contains B + + + + \ No newline at end of file diff --git a/src/Aspire.Dashboard/Resources/xlf/Logs.ja.xlf b/src/Aspire.Dashboard/Resources/xlf/Logs.ja.xlf new file mode 100644 index 0000000000..4e14bc9313 --- /dev/null +++ b/src/Aspire.Dashboard/Resources/xlf/Logs.ja.xlf @@ -0,0 +1,17 @@ + + + + + + contains + 次の値を含む + + + + not contains + 次の値を含まない + corresponds to A !contains B + + + + \ No newline at end of file diff --git a/src/Aspire.Dashboard/Resources/xlf/Logs.ko.xlf b/src/Aspire.Dashboard/Resources/xlf/Logs.ko.xlf new file mode 100644 index 0000000000..af58420311 --- /dev/null +++ b/src/Aspire.Dashboard/Resources/xlf/Logs.ko.xlf @@ -0,0 +1,17 @@ + + + + + + contains + 포함 + + + + not contains + 을(를) 포함하지 않음 + corresponds to A !contains B + + + + \ No newline at end of file diff --git a/src/Aspire.Dashboard/Resources/xlf/Logs.pl.xlf b/src/Aspire.Dashboard/Resources/xlf/Logs.pl.xlf new file mode 100644 index 0000000000..11b202f5ae --- /dev/null +++ b/src/Aspire.Dashboard/Resources/xlf/Logs.pl.xlf @@ -0,0 +1,17 @@ + + + + + + contains + zawiera + + + + not contains + nie zawiera + corresponds to A !contains B + + + + \ No newline at end of file diff --git a/src/Aspire.Dashboard/Resources/xlf/Logs.pt-BR.xlf b/src/Aspire.Dashboard/Resources/xlf/Logs.pt-BR.xlf new file mode 100644 index 0000000000..849b16f92f --- /dev/null +++ b/src/Aspire.Dashboard/Resources/xlf/Logs.pt-BR.xlf @@ -0,0 +1,17 @@ + + + + + + contains + contém + + + + not contains + não contém + corresponds to A !contains B + + + + \ No newline at end of file diff --git a/src/Aspire.Dashboard/Resources/xlf/Logs.ru.xlf b/src/Aspire.Dashboard/Resources/xlf/Logs.ru.xlf new file mode 100644 index 0000000000..722341e8d9 --- /dev/null +++ b/src/Aspire.Dashboard/Resources/xlf/Logs.ru.xlf @@ -0,0 +1,17 @@ + + + + + + contains + содержит + + + + not contains + не содержит + corresponds to A !contains B + + + + \ No newline at end of file diff --git a/src/Aspire.Dashboard/Resources/xlf/Logs.tr.xlf b/src/Aspire.Dashboard/Resources/xlf/Logs.tr.xlf new file mode 100644 index 0000000000..37656c24af --- /dev/null +++ b/src/Aspire.Dashboard/Resources/xlf/Logs.tr.xlf @@ -0,0 +1,17 @@ + + + + + + contains + içerir + + + + not contains + içermez + corresponds to A !contains B + + + + \ No newline at end of file diff --git a/src/Aspire.Dashboard/Resources/xlf/Logs.zh-Hans.xlf b/src/Aspire.Dashboard/Resources/xlf/Logs.zh-Hans.xlf new file mode 100644 index 0000000000..1a568d994e --- /dev/null +++ b/src/Aspire.Dashboard/Resources/xlf/Logs.zh-Hans.xlf @@ -0,0 +1,17 @@ + + + + + + contains + 包含 + + + + not contains + 不包含 + corresponds to A !contains B + + + + \ No newline at end of file diff --git a/src/Aspire.Dashboard/Resources/xlf/Logs.zh-Hant.xlf b/src/Aspire.Dashboard/Resources/xlf/Logs.zh-Hant.xlf new file mode 100644 index 0000000000..055d568b98 --- /dev/null +++ b/src/Aspire.Dashboard/Resources/xlf/Logs.zh-Hant.xlf @@ -0,0 +1,17 @@ + + + + + + contains + 包含 + + + + not contains + 不包含 + corresponds to A !contains B + + + + \ No newline at end of file diff --git a/src/Aspire.Dashboard/Resources/xlf/StructuredLogs.cs.xlf b/src/Aspire.Dashboard/Resources/xlf/StructuredLogs.cs.xlf index 851625ef81..921e19a8cf 100644 --- a/src/Aspire.Dashboard/Resources/xlf/StructuredLogs.cs.xlf +++ b/src/Aspire.Dashboard/Resources/xlf/StructuredLogs.cs.xlf @@ -42,6 +42,11 @@ Úroveň + + Level: + Úroveň: + + Message Zpráva diff --git a/src/Aspire.Dashboard/Resources/xlf/StructuredLogs.de.xlf b/src/Aspire.Dashboard/Resources/xlf/StructuredLogs.de.xlf index 6367eac952..5317605f3f 100644 --- a/src/Aspire.Dashboard/Resources/xlf/StructuredLogs.de.xlf +++ b/src/Aspire.Dashboard/Resources/xlf/StructuredLogs.de.xlf @@ -42,6 +42,11 @@ Ebene + + Level: + Ebene: + + Message Nachricht diff --git a/src/Aspire.Dashboard/Resources/xlf/StructuredLogs.es.xlf b/src/Aspire.Dashboard/Resources/xlf/StructuredLogs.es.xlf index 2d29e343e9..7e4636ef95 100644 --- a/src/Aspire.Dashboard/Resources/xlf/StructuredLogs.es.xlf +++ b/src/Aspire.Dashboard/Resources/xlf/StructuredLogs.es.xlf @@ -42,6 +42,11 @@ Nivel + + Level: + Nivel: + + Message Mensaje diff --git a/src/Aspire.Dashboard/Resources/xlf/StructuredLogs.fr.xlf b/src/Aspire.Dashboard/Resources/xlf/StructuredLogs.fr.xlf index fd3e8758c6..2af1b627e4 100644 --- a/src/Aspire.Dashboard/Resources/xlf/StructuredLogs.fr.xlf +++ b/src/Aspire.Dashboard/Resources/xlf/StructuredLogs.fr.xlf @@ -42,6 +42,11 @@ Niveau + + Level: + Niveau : + + Message Message diff --git a/src/Aspire.Dashboard/Resources/xlf/StructuredLogs.it.xlf b/src/Aspire.Dashboard/Resources/xlf/StructuredLogs.it.xlf index add543e2f1..d8efbaccac 100644 --- a/src/Aspire.Dashboard/Resources/xlf/StructuredLogs.it.xlf +++ b/src/Aspire.Dashboard/Resources/xlf/StructuredLogs.it.xlf @@ -42,6 +42,11 @@ Livello + + Level: + Livello: + + Message Messaggio diff --git a/src/Aspire.Dashboard/Resources/xlf/StructuredLogs.ja.xlf b/src/Aspire.Dashboard/Resources/xlf/StructuredLogs.ja.xlf index aeb0dbcd97..2c2e88cfa2 100644 --- a/src/Aspire.Dashboard/Resources/xlf/StructuredLogs.ja.xlf +++ b/src/Aspire.Dashboard/Resources/xlf/StructuredLogs.ja.xlf @@ -42,6 +42,11 @@ レベル + + Level: + レベル: + + Message メッセージ diff --git a/src/Aspire.Dashboard/Resources/xlf/StructuredLogs.ko.xlf b/src/Aspire.Dashboard/Resources/xlf/StructuredLogs.ko.xlf index 3111e14fa9..6309d72e74 100644 --- a/src/Aspire.Dashboard/Resources/xlf/StructuredLogs.ko.xlf +++ b/src/Aspire.Dashboard/Resources/xlf/StructuredLogs.ko.xlf @@ -42,6 +42,11 @@ 수준 + + Level: + 수준: + + Message 메시지 diff --git a/src/Aspire.Dashboard/Resources/xlf/StructuredLogs.pl.xlf b/src/Aspire.Dashboard/Resources/xlf/StructuredLogs.pl.xlf index 475c0d5286..e2b6b66a9e 100644 --- a/src/Aspire.Dashboard/Resources/xlf/StructuredLogs.pl.xlf +++ b/src/Aspire.Dashboard/Resources/xlf/StructuredLogs.pl.xlf @@ -42,6 +42,11 @@ Poziom + + Level: + Poziom: + + Message Komunikat diff --git a/src/Aspire.Dashboard/Resources/xlf/StructuredLogs.pt-BR.xlf b/src/Aspire.Dashboard/Resources/xlf/StructuredLogs.pt-BR.xlf index 615ea4d4ca..904e2ba94a 100644 --- a/src/Aspire.Dashboard/Resources/xlf/StructuredLogs.pt-BR.xlf +++ b/src/Aspire.Dashboard/Resources/xlf/StructuredLogs.pt-BR.xlf @@ -42,6 +42,11 @@ Nível + + Level: + Nível: + + Message Mensagem diff --git a/src/Aspire.Dashboard/Resources/xlf/StructuredLogs.ru.xlf b/src/Aspire.Dashboard/Resources/xlf/StructuredLogs.ru.xlf index 2c6ca82250..4a6a8122bd 100644 --- a/src/Aspire.Dashboard/Resources/xlf/StructuredLogs.ru.xlf +++ b/src/Aspire.Dashboard/Resources/xlf/StructuredLogs.ru.xlf @@ -42,6 +42,11 @@ Уровень + + Level: + Уровень: + + Message Сообщение diff --git a/src/Aspire.Dashboard/Resources/xlf/StructuredLogs.tr.xlf b/src/Aspire.Dashboard/Resources/xlf/StructuredLogs.tr.xlf index ee244a2e58..c5d4a5e58a 100644 --- a/src/Aspire.Dashboard/Resources/xlf/StructuredLogs.tr.xlf +++ b/src/Aspire.Dashboard/Resources/xlf/StructuredLogs.tr.xlf @@ -42,6 +42,11 @@ Düzey + + Level: + Düzey: + + Message İleti diff --git a/src/Aspire.Dashboard/Resources/xlf/StructuredLogs.zh-Hans.xlf b/src/Aspire.Dashboard/Resources/xlf/StructuredLogs.zh-Hans.xlf index 273cbe095a..d831f2a50c 100644 --- a/src/Aspire.Dashboard/Resources/xlf/StructuredLogs.zh-Hans.xlf +++ b/src/Aspire.Dashboard/Resources/xlf/StructuredLogs.zh-Hans.xlf @@ -42,6 +42,11 @@ 级别 + + Level: + 级别: + + Message 消息 diff --git a/src/Aspire.Dashboard/Resources/xlf/StructuredLogs.zh-Hant.xlf b/src/Aspire.Dashboard/Resources/xlf/StructuredLogs.zh-Hant.xlf index 67573cb254..aabd6bca33 100644 --- a/src/Aspire.Dashboard/Resources/xlf/StructuredLogs.zh-Hant.xlf +++ b/src/Aspire.Dashboard/Resources/xlf/StructuredLogs.zh-Hant.xlf @@ -42,6 +42,11 @@ 層級 + + Level: + 層級: + + Message 訊息