diff --git a/Chaos.Messaging/ChannelService.cs b/Chaos.Messaging/ChannelService.cs index 38b73b2b86..b4313ee545 100644 --- a/Chaos.Messaging/ChannelService.cs +++ b/Chaos.Messaging/ChannelService.cs @@ -4,6 +4,8 @@ using Chaos.Extensions.Common; using Chaos.IO.Memory; using Chaos.Messaging.Abstractions; +using Chaos.NLog.Logging.Definitions; +using Chaos.NLog.Logging.Extensions; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -62,7 +64,9 @@ public void JoinChannel(IChannelSubscriber subscriber, string channelName, bool if (channelDetails.AddSubscriber(subscriber)) { - Logger.LogDebug("{@SubscriberName} has joined channel {@ChannelName}", subscriber.Name, channelName); + Logger.WithTopics(Topics.Entities.Channel, Topics.Actions.Join) + .WithProperty(subscriber) + .LogDebug("{@SubscriberName} has joined channel {@ChannelName}", subscriber.Name, channelName); subscriber.SendMessage($"You have joined channel {channelDetails.ChannelNameOverride ?? channelName}"); } else @@ -81,7 +85,9 @@ public void LeaveChannel(IChannelSubscriber subscriber, string channelName) if (channelDetails.RemoveSubscriber(subscriber)) { - Logger.LogDebug("{@SubscriberName} has left channel {@ChannelName}", subscriber.Name, channelName); + Logger.WithTopics(Topics.Entities.Channel, Topics.Actions.Leave) + .WithProperty(subscriber) + .LogDebug("{@SubscriberName} has left channel {@ChannelName}", subscriber.Name, channelName); subscriber.SendMessage($"You have left channel {channelDetails.ChannelNameOverride ?? channelName}"); } else @@ -140,7 +146,9 @@ public void RegisterChannel( } Channels.TryAdd(channelName, new ChannelDetails(defaultMessageColor, sendMessageAction, channelNameOverride)); - Logger.LogInformation("Channel {@ChannelName} has been registered", channelName); + + Logger.WithTopics(Topics.Entities.Channel, Topics.Actions.Create) + .LogInformation("Channel {@ChannelName} has been registered", channelName); if (subscriber is not null) JoinChannel(subscriber, channelName, bypassValidation); @@ -190,11 +198,13 @@ public void SendMessage(IChannelSubscriber subscriber, string channelName, strin var defaultMessage = Encoding.Default.GetString(buffer); - Logger.LogInformation( - "Subscriber {@SubscriberName} sent message {@Message} to channel {@ChannelName}", - subscriber.Name, - message, - channelName); + Logger.WithTopics(Topics.Entities.Channel, Topics.Entities.Message, Topics.Actions.Send) + .WithProperty(subscriber) + .LogInformation( + "Subscriber {@SubscriberName} sent message {@Message} to channel {@ChannelName}", + subscriber.Name, + message, + channelName); foreach (var subDetails in channelDetails.Subscribers.Values) { @@ -239,7 +249,8 @@ public bool UnregisterChannel(string channelName) foreach (var subDetails in channel.Subscribers.Values) LeaveChannel(subDetails.Subscriber, channelName); - Logger.LogInformation("Channel {@ChannelName} has been unregistered", channelName); + Logger.WithTopics(Topics.Entities.Channel, Topics.Actions.Delete) + .LogInformation("Channel {@ChannelName} has been unregistered", channelName); return Channels.TryRemove(channelName, out _); } diff --git a/Chaos.Messaging/Chaos.Messaging.csproj b/Chaos.Messaging/Chaos.Messaging.csproj index a84d8d8469..9d9938d442 100644 --- a/Chaos.Messaging/Chaos.Messaging.csproj +++ b/Chaos.Messaging/Chaos.Messaging.csproj @@ -6,6 +6,7 @@ + diff --git a/Chaos.Messaging/CommandInterceptor.cs b/Chaos.Messaging/CommandInterceptor.cs index ebd7b1afe2..85d30750dd 100644 --- a/Chaos.Messaging/CommandInterceptor.cs +++ b/Chaos.Messaging/CommandInterceptor.cs @@ -4,6 +4,8 @@ using Chaos.Common.Definitions; using Chaos.Extensions.Common; using Chaos.Messaging.Abstractions; +using Chaos.NLog.Logging.Definitions; +using Chaos.NLog.Logging.Extensions; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -72,15 +74,19 @@ public async ValueTask HandleCommandAsync(T source, string commandStr) if (!Commands.TryGetValue(commandName, out var descriptor)) return; - Logger.LogDebug("Handling command {@CommandStr}", commandStr); + Logger.WithTopics(Topics.Entities.Command, Topics.Actions.Execute) + .WithProperty(source) + .LogDebug("Handling command {@CommandStr}", commandStr); if (descriptor.Details.RequiresAdmin && !source.IsAdmin) { - Logger.LogWarning( - "Non-Admin {@SourceType} {@SourceName} tried to execute admin command {@CommandStr}", - source.GetType().Name, - source.Name, - commandStr); + Logger.WithTopics(Topics.Entities.Command, Topics.Actions.Execute) + .WithProperty(source) + .LogWarning( + "Non-Admin {@SourceType} {@SourceName} tried to execute admin command {@CommandStr}", + source.GetType().Name, + source.Name, + commandStr); return; } @@ -89,7 +95,8 @@ public async ValueTask HandleCommandAsync(T source, string commandStr) { var commandInstance = (ICommand)ActivatorUtilities.CreateInstance(ServiceProvider, descriptor.Type); - Logger.LogTrace("Successfully created command {@CommandName}", commandName); + Logger.WithTopics(Topics.Entities.Command, Topics.Actions.Create) + .LogTrace("Successfully created command {@CommandName}", commandName); if (commandName.EqualsI("help") || commandName.EqualsI("commands")) { @@ -103,22 +110,26 @@ async ValueTask InnerExecute() { await commandInstance.ExecuteAsync(source, new ArgumentCollection(commandArgs)); - Logger.LogInformation( - "{@SourceType} {@SourceName} executed {@CommandStr}", - source.GetType().Name, - source.Name, - commandStr); + Logger.WithTopics(Topics.Entities.Command, Topics.Actions.Execute) + .WithProperty(source) + .LogInformation( + "{@SourceType} {@SourceName} executed {@CommandStr}", + source.GetType().Name, + source.Name, + commandStr); } await InnerExecute(); } catch (Exception e) { - Logger.LogError( - e, - "{@SourceType} {@SourceName} failed to execute {@Command}", - source.GetType().Name, - source.Name, - commandStr); + Logger.WithTopics(Topics.Entities.Command, Topics.Actions.Execute) + .WithProperty(source) + .LogError( + e, + "{@SourceType} {@SourceName} failed to execute {@Command}", + source.GetType().Name, + source.Name, + commandStr); } } diff --git a/Chaos.NLog.Logging/Chaos.NLog.Logging.csproj b/Chaos.NLog.Logging/Chaos.NLog.Logging.csproj index eb70ae6cc2..c2196952d9 100644 --- a/Chaos.NLog.Logging/Chaos.NLog.Logging.csproj +++ b/Chaos.NLog.Logging/Chaos.NLog.Logging.csproj @@ -1,6 +1,6 @@ - + diff --git a/Chaos.NLog.Logging/Definitions/Topics.cs b/Chaos.NLog.Logging/Definitions/Topics.cs index 6e6ada52fa..a7ef466aef 100644 --- a/Chaos.NLog.Logging/Definitions/Topics.cs +++ b/Chaos.NLog.Logging/Definitions/Topics.cs @@ -5,76 +5,62 @@ public static class Topics { public static class Actions { - #region Bank Actions - public static string Deposit => nameof(Deposit); - public static string Withdraw => nameof(Withdraw); - #endregion - - #region Shop Actions + public static string Accepted => nameof(Accepted); + public static string Add => nameof(Add); + public static string Admit => nameof(Admit); public static string Buy => nameof(Buy); - public static string Sell => nameof(Sell); - #endregion - - #region Exchange Actions public static string Canceled => nameof(Canceled); - public static string Accepted => nameof(Accepted); - #endregion - - #region Guild / Group Actions - public static string Promote => nameof(Promote); + public static string Connect => nameof(Connect); + public static string Create => nameof(Create); + public static string Death => nameof(Death); + public static string Delete => nameof(Delete); public static string Demote => nameof(Demote); - public static string Kick => nameof(Kick); - public static string Admit => nameof(Admit); - public static string Invite => nameof(Invite); - public static string Leave => nameof(Leave); + public static string Deposit => nameof(Deposit); public static string Disband => nameof(Disband); - #endregion - - #region Creature Actions - public static string Walk => nameof(Walk); - public static string Traverse => nameof(Traverse); - public static string Death => nameof(Death); + public static string Disconnect => nameof(Disconnect); public static string Drop => nameof(Drop); + public static string Execute => nameof(Execute); + public static string Forget => nameof(Forget); + public static string Highlight => nameof(Highlight); + public static string Invite => nameof(Invite); + public static string Join => nameof(Join); + public static string Kick => nameof(Kick); + public static string Learn => nameof(Learn); + public static string Leave => nameof(Leave); + public static string Listening => nameof(Listening); + public static string Load => nameof(Load); public static string Login => nameof(Login); public static string Logout => nameof(Logout); + public static string Penalty => nameof(Penalty); public static string Pickup => nameof(Pickup); - public static string Reward => nameof(Reward); - public static string Message => nameof(Message); - public static string Command => nameof(Command); - public static string Learn => nameof(Learn); - public static string Forget => nameof(Forget); - #endregion - - #region Storage Actions - public static string Load => nameof(Load); - public static string Reload => nameof(Reload); - public static string Save => nameof(Save); - public static string Add => nameof(Add); - public static string Remove => nameof(Remove); - public static string Create => nameof(Create); - public static string Read => nameof(Read); - public static string Update => nameof(Update); - public static string Delete => nameof(Delete); - public static string Highlight => nameof(Highlight); - #endregion - - #region Server Actions public static string Processing => nameof(Processing); + public static string Promote => nameof(Promote); + public static string Read => nameof(Read); public static string Receive => nameof(Receive); - public static string Send => nameof(Send); public static string Redirect => nameof(Redirect); - public static string Connect => nameof(Connect); - public static string Disconnect => nameof(Disconnect); + public static string Reload => nameof(Reload); + public static string Remove => nameof(Remove); + public static string Reward => nameof(Reward); + public static string Save => nameof(Save); + public static string Sell => nameof(Sell); + public static string Send => nameof(Send); + public static string Traverse => nameof(Traverse); + public static string Update => nameof(Update); public static string Validation => nameof(Validation); - #endregion + public static string Walk => nameof(Walk); + public static string Withdraw => nameof(Withdraw); } public static class Entities { public static string Aisling => nameof(Aisling); + public static string Backup => nameof(Backup); public static string BulletinBoard => nameof(BulletinBoard); + public static string Channel => nameof(Channel); public static string Client => nameof(Client); + public static string Command => nameof(Command); public static string Creature => nameof(Creature); + public static string DeltaMonitor => nameof(DeltaMonitor); public static string Dialog => nameof(Dialog); public static string Effect => nameof(Effect); public static string Exchange => nameof(Exchange); @@ -89,11 +75,14 @@ public static class Entities public static string MapInstance => nameof(MapInstance); public static string MapTemplate => nameof(MapTemplate); public static string Merchant => nameof(Merchant); + public static string Message => nameof(Message); public static string MetaData => nameof(MetaData); public static string Monster => nameof(Monster); public static string Options => nameof(Options); public static string Packet => nameof(Packet); public static string Post => nameof(Post); + public static string Quest => nameof(Quest); + public static string Script => nameof(Script); public static string Skill => nameof(Skill); public static string Spell => nameof(Spell); public static string WorldMap => nameof(WorldMap); diff --git a/Chaos.Networking/Abstractions/ServerBase.cs b/Chaos.Networking/Abstractions/ServerBase.cs index 43c82223bf..da758f559e 100644 --- a/Chaos.Networking/Abstractions/ServerBase.cs +++ b/Chaos.Networking/Abstractions/ServerBase.cs @@ -4,6 +4,8 @@ using Chaos.Extensions.Common; using Chaos.Networking.Entities.Client; using Chaos.Networking.Options; +using Chaos.NLog.Logging.Definitions; +using Chaos.NLog.Logging.Extensions; using Chaos.Packets; using Chaos.Packets.Abstractions; using Chaos.Packets.Abstractions.Definitions; @@ -97,7 +99,9 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) Socket.Bind(endPoint); Socket.Listen(20); Socket.BeginAccept(OnConnection, Socket); - Logger.LogInformation("Listening on {@EndPoint}", endPoint.ToString()); + + Logger.WithTopics(Topics.Actions.Listening) + .LogInformation("Listening on {@EndPoint}", endPoint.ToString()); await stoppingToken.WaitTillCanceled(); @@ -165,12 +169,13 @@ public virtual async ValueTask ExecuteHandler(T client, TArgs args, Func< await action(client, args); } catch (Exception e) { - Logger.LogError( - e, - "{@ClientType} failed to execute inner handler with args type {@ArgsType} ({@Args})", - client.GetType().Name, - args!.GetType().Name, - args); + Logger.WithTopics(Topics.Entities.Packet, Topics.Actions.Processing) + .LogError( + e, + "{@ClientType} failed to execute inner handler with args type {@ArgsType} ({@Args})", + client.GetType().Name, + args!.GetType().Name, + args); } } @@ -188,7 +193,8 @@ public virtual async ValueTask ExecuteHandler(T client, Func actio await action(client); } catch (Exception e) { - Logger.LogError(e, "{@ClientType} failed to execute inner handler", client.GetType().Name); + Logger.WithTopics(Topics.Entities.Packet, Topics.Actions.Processing) + .LogError(e, "{@ClientType} failed to execute inner handler", client.GetType().Name); } } diff --git a/Chaos.Networking/Abstractions/SocketClientBase.cs b/Chaos.Networking/Abstractions/SocketClientBase.cs index 90c7b96df9..e19786787d 100644 --- a/Chaos.Networking/Abstractions/SocketClientBase.cs +++ b/Chaos.Networking/Abstractions/SocketClientBase.cs @@ -7,6 +7,8 @@ using Chaos.Extensions.Networking; using Chaos.IO.Memory; using Chaos.Networking.Entities.Server; +using Chaos.NLog.Logging.Definitions; +using Chaos.NLog.Logging.Extensions; using Chaos.Packets; using Chaos.Packets.Abstractions; using Chaos.Packets.Abstractions.Definitions; @@ -199,14 +201,16 @@ void InnerCatch() var hex = BitConverter.ToString(buffer.ToArray()).Replace("-", " "); var ascii = Encoding.ASCII.GetString(buffer); - Logger.LogCritical( - ex, - "Exception while handling a packet for {@ClientType}. (Count: {Count}, Offset: {Offset}, BufferHex: {BufferHex}, BufferAscii: {BufferAscii})", - GetType().Name, - Count, - offset, - hex, - ascii); + Logger.WithTopics(Topics.Entities.Client, Topics.Entities.Packet, Topics.Actions.Processing) + .WithProperty(this) + .LogError( + ex, + "Exception while handling a packet for {@ClientType}. (Count: {Count}, Offset: {Offset}, BufferHex: {BufferHex}, BufferAscii: {BufferAscii})", + GetType().Name, + Count, + offset, + hex, + ascii); } InnerCatch(); @@ -256,7 +260,13 @@ public virtual void Send(ref ServerPacket packet) //no way to pass the packet in because its a ref struct //but we still want to avoid serializing the packet to a string if we aren't actually going to log it if (LogRawPackets) - Logger.LogTrace("[Snd] {Packet}", packet.ToString()); + Logger.WithTopics( + Topics.Qualifiers.Raw, + Topics.Entities.Client, + Topics.Entities.Packet, + Topics.Actions.Send) + .WithProperty(this) + .LogTrace("[Snd] {Packet}", packet.ToString()); packet.ShouldEncrypt = Crypto.ShouldEncrypt((byte)packet.OpCode); diff --git a/Chaos.Networking/Chaos.Networking.csproj b/Chaos.Networking/Chaos.Networking.csproj index 72970fd863..f029a8898e 100644 --- a/Chaos.Networking/Chaos.Networking.csproj +++ b/Chaos.Networking/Chaos.Networking.csproj @@ -4,6 +4,7 @@ + diff --git a/Chaos.Networking/Entities/RedirectManager.cs b/Chaos.Networking/Entities/RedirectManager.cs index d95eb482f0..a72ebfdab4 100644 --- a/Chaos.Networking/Entities/RedirectManager.cs +++ b/Chaos.Networking/Entities/RedirectManager.cs @@ -1,4 +1,6 @@ using Chaos.Networking.Abstractions; +using Chaos.NLog.Logging.Definitions; +using Chaos.NLog.Logging.Extensions; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; @@ -21,7 +23,8 @@ public sealed class RedirectManager : BackgroundService, IRedirectManager /// public void Add(IRedirect redirect) { - Logger.LogTrace("Now tracking redirect {@RedirectId}", redirect.Id); + Logger.WithTopics(Topics.Actions.Redirect) + .LogTrace("Now tracking redirect {@RedirectId}", redirect.Id); Redirects.TryAdd(redirect.Id, redirect); } @@ -31,7 +34,8 @@ public bool TryGetRemove(uint id, [MaybeNullWhen(false)] out IRedirect redirect) { if (Redirects.TryRemove(id, out redirect)) { - Logger.LogTrace("Redirect {@RedirectId} has been consumed", redirect.Id); + Logger.WithTopics(Topics.Actions.Redirect) + .LogTrace("Redirect {@RedirectId} has been consumed", redirect.Id); return true; } @@ -53,7 +57,8 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) foreach (var redirect in Redirects.Values) if (now.Subtract(redirect.Created) > Timeout) { - Logger.LogTrace("Redirect {@RedirectId} has timed out", redirect.Id); + Logger.WithTopics(Topics.Actions.Redirect) + .LogTrace("Redirect {@RedirectId} has timed out", redirect.Id); Redirects.TryRemove(redirect.Id, out _); } diff --git a/Chaos.Packets/Chaos.Packets.csproj b/Chaos.Packets/Chaos.Packets.csproj index 4a9d8b94aa..a26d53a1a6 100644 --- a/Chaos.Packets/Chaos.Packets.csproj +++ b/Chaos.Packets/Chaos.Packets.csproj @@ -2,4 +2,7 @@ + + + \ No newline at end of file diff --git a/Chaos.Packets/ClientPacket.cs b/Chaos.Packets/ClientPacket.cs index 5a30bdb082..bf2d29a541 100644 --- a/Chaos.Packets/ClientPacket.cs +++ b/Chaos.Packets/ClientPacket.cs @@ -1,6 +1,5 @@ using System.Text; using Chaos.Packets.Abstractions.Definitions; -using Chaos.Packets.Definitions; namespace Chaos.Packets; @@ -67,7 +66,7 @@ public readonly string GetAsciiString(bool replaceNewline = true) /// Returns the packet data as a hexadecimal string. /// /// The packet data as a hexadecimal string. - public string GetHexString() => $"{OpCode}: {RegexCache.DOUBLE_BYTE_REGEX.Replace(Convert.ToHexString(Buffer), "$1 ")}"; + public string GetHexString() => $"{OpCode}: {BitConverter.ToString(Buffer.ToArray()).Replace("-", " ")}"; /// /// Converts the packet data to a byte array. diff --git a/Chaos.Packets/Definitions/RegexCache.cs b/Chaos.Packets/Definitions/RegexCache.cs deleted file mode 100644 index 8e718fa6e9..0000000000 --- a/Chaos.Packets/Definitions/RegexCache.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Text.RegularExpressions; - -namespace Chaos.Packets.Definitions; - -internal static partial class RegexCache -{ - internal static readonly Regex DOUBLE_BYTE_REGEX = GenerateDoubleByteRegex(); - - [GeneratedRegex("(.{2})", RegexOptions.Compiled)] - private static partial Regex GenerateDoubleByteRegex(); -} \ No newline at end of file diff --git a/Chaos.Packets/ServerPacket.cs b/Chaos.Packets/ServerPacket.cs index b1fa0a6a84..37d6c5fd0e 100644 --- a/Chaos.Packets/ServerPacket.cs +++ b/Chaos.Packets/ServerPacket.cs @@ -1,6 +1,5 @@ using System.Text; using Chaos.Packets.Abstractions.Definitions; -using Chaos.Packets.Definitions; namespace Chaos.Packets; @@ -75,7 +74,7 @@ public readonly string GetAsciiString(bool replaceNewline = true) /// Gets the hexadecimal string representation of the packet buffer. /// /// The hexadecimal string representation of the packet buffer. - public string GetHexString() => $"{OpCode}: {RegexCache.DOUBLE_BYTE_REGEX.Replace(Convert.ToHexString(Buffer), "$1 ")}"; + public string GetHexString() => $"{OpCode}: {BitConverter.ToString(Buffer.ToArray()).Replace("-", " ")}"; /// /// Converts the packet data to an array of bytes. diff --git a/Chaos.Schemas/Aisling/AislingSchema.cs b/Chaos.Schemas/Aisling/AislingSchema.cs index 80ca5fc723..17eeaed214 100644 --- a/Chaos.Schemas/Aisling/AislingSchema.cs +++ b/Chaos.Schemas/Aisling/AislingSchema.cs @@ -30,10 +30,6 @@ public sealed record AislingSchema [JsonIgnore(Condition = JsonIgnoreCondition.Never)] public Direction Direction { get; set; } /// - /// A collection of effects that are currently active on the aisling - /// - public ICollection Effects { get; set; } = Array.Empty(); - /// /// The sprite id of the aisling's face /// public int FaceSprite { get; set; } diff --git a/Chaos.Scripting/Chaos.Scripting.csproj b/Chaos.Scripting/Chaos.Scripting.csproj index 4c145cfb8f..8609c68355 100644 --- a/Chaos.Scripting/Chaos.Scripting.csproj +++ b/Chaos.Scripting/Chaos.Scripting.csproj @@ -5,6 +5,7 @@ + \ No newline at end of file diff --git a/Chaos.Scripting/ScriptFactory.cs b/Chaos.Scripting/ScriptFactory.cs index d1f4839751..eb1faf7eab 100644 --- a/Chaos.Scripting/ScriptFactory.cs +++ b/Chaos.Scripting/ScriptFactory.cs @@ -1,5 +1,7 @@ using System.Collections.Concurrent; using Chaos.Extensions.Common; +using Chaos.NLog.Logging.Definitions; +using Chaos.NLog.Logging.Extensions; using Chaos.Scripting.Abstractions; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -73,7 +75,9 @@ public TScript CreateScript(ICollection scriptKeys, TScripted subject) if (instance is not TScript tScript) { - Logger.LogError("Script obtained from key {@ScriptKey} is not of type {@TypeName}", scriptKey, TypeName); + Logger.WithTopics(Topics.Entities.Script, Topics.Actions.Create) + .WithProperty(subject) + .LogError("Script obtained from key {@ScriptKey} is not of type {@TypeName}", scriptKey, TypeName); continue; } @@ -97,13 +101,15 @@ private void LoadScriptTypes() var scriptKey = ScriptBase.GetScriptKey(type); ScriptTypeCache[scriptKey] = type; - Logger.LogTrace( - "Cached {@TypeName} of type {@Type} with key {@ScriptKey}", - TypeName, - type.Name, - scriptKey); + Logger.WithTopics(Topics.Entities.Script, Topics.Actions.Load) + .LogTrace( + "Cached {@TypeName} of type {@Type} with key {@ScriptKey}", + TypeName, + type.Name, + scriptKey); } - Logger.LogInformation("{Count} {@TScriptName}s loaded", ScriptTypeCache.Count, TypeName); + Logger.WithTopics(Topics.Entities.Script, Topics.Actions.Load) + .LogInformation("{Count} {@TScriptName}s loaded", ScriptTypeCache.Count, TypeName); } } \ No newline at end of file diff --git a/Chaos.Security/AccessManager.cs b/Chaos.Security/AccessManager.cs index b673913f56..b690a2847f 100644 --- a/Chaos.Security/AccessManager.cs +++ b/Chaos.Security/AccessManager.cs @@ -4,6 +4,8 @@ using System.Text; using Chaos.Common.Synchronization; using Chaos.Extensions.Common; +using Chaos.NLog.Logging.Definitions; +using Chaos.NLog.Logging.Extensions; using Chaos.Security.Abstractions; using Chaos.Security.Definitions; using Chaos.Security.Options; @@ -91,9 +93,10 @@ string newPassword if (!result.Success) { if (result.Code == CredentialValidationResult.FailureCode.TooManyAttempts) - Logger.LogWarning( - "{@ClientIp} has exceeded the maximum number of credential attempts while attempting to change password", - ipAddress.ToString()); + Logger.WithTopics(Topics.Entities.Client, Topics.Actions.Validation) + .LogWarning( + "{@ClientIp} has exceeded the maximum number of credential attempts while attempting to change password", + ipAddress.ToString()); return result; } @@ -165,9 +168,10 @@ public async Task ValidateCredentialsAsync(IPAddress var result = await InnerValidateCredentialsAsync(ipAddress, name, password); if (result is { Success: false, Code: CredentialValidationResult.FailureCode.TooManyAttempts }) - Logger.LogWarning( - "{@ClientIp} has exceeded the maximum number of credential attempts while attempting to log in", - ipAddress.ToString()); + Logger.WithTopics(Topics.Entities.Client, Topics.Actions.Validation) + .LogWarning( + "{@ClientIp} has exceeded the maximum number of credential attempts while attempting to log in", + ipAddress.ToString()); return result; } diff --git a/Chaos.Security/Chaos.Security.csproj b/Chaos.Security/Chaos.Security.csproj index fbf13dc7bd..ec6cc922da 100644 --- a/Chaos.Security/Chaos.Security.csproj +++ b/Chaos.Security/Chaos.Security.csproj @@ -3,6 +3,7 @@ + diff --git a/Chaos.Storage.Abstractions/Chaos.Storage.Abstractions.csproj b/Chaos.Storage.Abstractions/Chaos.Storage.Abstractions.csproj index 02926c3c1a..ed73b0d95a 100644 --- a/Chaos.Storage.Abstractions/Chaos.Storage.Abstractions.csproj +++ b/Chaos.Storage.Abstractions/Chaos.Storage.Abstractions.csproj @@ -10,6 +10,8 @@ + + \ No newline at end of file diff --git a/Chaos.Storage.Abstractions/DirectoryBackupService.cs b/Chaos.Storage.Abstractions/DirectoryBackupService.cs index 8b3cd8e1d1..7a02c9342b 100644 --- a/Chaos.Storage.Abstractions/DirectoryBackupService.cs +++ b/Chaos.Storage.Abstractions/DirectoryBackupService.cs @@ -1,6 +1,7 @@ -using System.Diagnostics; using System.IO.Compression; using Chaos.IO.FileSystem; +using Chaos.NLog.Logging.Definitions; +using Chaos.NLog.Logging.Extensions; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -36,7 +37,8 @@ public virtual ValueTask HandleBackupRetentionAsync(string directory, Cancellati if (!directoryInfo.Exists) { - Logger.LogError("Failed to handle backup retention for path {@Directory} because it doesn't exist", directory); + Logger.WithTopics(Topics.Entities.Backup, Topics.Actions.Save) + .LogError("Failed to handle backup retention for path {@Directory} because it doesn't exist", directory); return default; } @@ -47,7 +49,9 @@ public virtual ValueTask HandleBackupRetentionAsync(string directory, Cancellati if (fileInfo.CreationTimeUtc < deleteTime) try { - Logger.LogTrace("Deleting backup {@Backup}", fileInfo.FullName); + Logger.WithTopics(Topics.Entities.Backup, Topics.Actions.Delete) + .LogTrace("Deleting backup {@Backup}", fileInfo.FullName); + fileInfo.Delete(); } catch { @@ -66,7 +70,8 @@ public virtual ValueTask TakeBackupAsync(string saveDirectory, CancellationToken if (!directoryInfo.Exists) { - Logger.LogError("Failed to take backup for path {@SaveDir} because it doesn't exist", saveDirectory); + Logger.WithTopics(Topics.Entities.Backup, Topics.Actions.Save) + .LogError("Failed to take backup for path {@SaveDir} because it doesn't exist", saveDirectory); return default; } @@ -89,12 +94,15 @@ public virtual ValueTask TakeBackupAsync(string saveDirectory, CancellationToken saveDirectory.SafeExecute( saveDir => { - Logger.LogTrace("Backing up directory {@SaveDir}", saveDirectory); + Logger.WithTopics(Topics.Entities.Backup, Topics.Actions.Save) + .LogTrace("Backing up directory {@SaveDir}", saveDirectory); + ZipFile.CreateFromDirectory(saveDir, backupPath); }); } catch (Exception e) { - Logger.LogError(e, "Failed to take backup for path {@SaveDir}", saveDirectory); + Logger.WithTopics(Topics.Entities.Backup, Topics.Actions.Save) + .LogError(e, "Failed to take backup for path {@SaveDir}", saveDirectory); } return default; @@ -114,9 +122,12 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) try { await backupTimer.WaitForNextTickAsync(stoppingToken); - var start = Stopwatch.GetTimestamp(); - Logger.LogDebug("Performing backup"); + Logger.WithTopics(Topics.Entities.Backup, Topics.Actions.Save) + .LogDebug("Performing backup"); + + var metricsLogger = Logger.WithTopics(Topics.Entities.Backup, Topics.Actions.Save) + .WithMetrics(); await Parallel.ForEachAsync( Directory.EnumerateDirectories(Options.Directory), @@ -125,14 +136,16 @@ await Parallel.ForEachAsync( await Parallel.ForEachAsync(Directory.EnumerateDirectories(Options.BackupDirectory), pOptions, HandleBackupRetentionAsync); - Logger.LogDebug("Backup completed, took {@Elapsed}", Stopwatch.GetElapsedTime(start)); + metricsLogger.WithTopics(Topics.Entities.Backup, Topics.Actions.Save) + .LogDebug("Backup completed"); } catch (OperationCanceledException) { //ignore return; } catch (Exception e) { - Logger.LogCritical(e, "Exception occurred while performing backup"); + Logger.WithTopics(Topics.Entities.Backup, Topics.Actions.Save) + .LogError(e, "Exception occurred while performing backup"); } } } \ No newline at end of file diff --git a/Chaos.Storage/EntityRepository.cs b/Chaos.Storage/EntityRepository.cs index a7b0685fdc..05e2061ee0 100644 --- a/Chaos.Storage/EntityRepository.cs +++ b/Chaos.Storage/EntityRepository.cs @@ -1,6 +1,8 @@ using System.Runtime.Serialization; using System.Text.Json; using Chaos.Common.Utilities; +using Chaos.NLog.Logging.Definitions; +using Chaos.NLog.Logging.Extensions; using Chaos.Storage.Abstractions; using Chaos.TypeMapper.Abstractions; using Microsoft.Extensions.Logging; @@ -32,7 +34,8 @@ public TSchema Load(string path) { ArgumentException.ThrowIfNullOrEmpty(path); - Logger.LogTrace("Loading new unmapped {@TypeName} object from path {@Path}", typeof(TSchema).Name, path); + Logger.WithTopics(Topics.Actions.Load) + .LogTrace("Loading new unmapped {@TypeName} object from path {@Path}", typeof(TSchema).Name, path); var schema = JsonSerializerEx.Deserialize(path, JsonSerializerOptions); @@ -47,7 +50,8 @@ public T LoadAndMap(string path, Action? preMapAction = nul { ArgumentException.ThrowIfNullOrEmpty(path); - Logger.LogTrace("Loading new {@TypeName} object from path {@Path}", typeof(T).Name, path); + Logger.WithTopics(Topics.Actions.Load) + .LogTrace("Loading new {@TypeName} object from path {@Path}", typeof(T).Name, path); var schema = JsonSerializerEx.Deserialize(path, JsonSerializerOptions); @@ -64,7 +68,8 @@ public async Task LoadAndMapAsync(string path, Func(path, JsonSerializerOptions); @@ -82,7 +87,8 @@ public IEnumerable LoadAndMapMany(string path, Action? p { ArgumentException.ThrowIfNullOrEmpty(path); - Logger.LogTrace("Loading collection of {@TypeName} objects from path {@Path}", typeof(T).Name, path); + Logger.WithTopics(Topics.Actions.Load) + .LogTrace("Loading collection of {@TypeName} objects from path {@Path}", typeof(T).Name, path); var schemas = JsonSerializerEx.Deserialize>(path, JsonSerializerOptions)!; @@ -100,7 +106,8 @@ public async IAsyncEnumerable LoadAndMapManyAsync(string path, Fu { ArgumentException.ThrowIfNullOrEmpty(path); - Logger.LogTrace("Loading collection of {@TypeName} objects from path {@Path}", typeof(T).Name, path); + Logger.WithTopics(Topics.Actions.Load) + .LogTrace("Loading collection of {@TypeName} objects from path {@Path}", typeof(T).Name, path); var schemas = await JsonSerializerEx.DeserializeAsync>(path, JsonSerializerOptions); @@ -124,7 +131,8 @@ public async Task LoadAsync(string path) { ArgumentException.ThrowIfNullOrEmpty(path); - Logger.LogTrace("Loading new unmapped {@TypeName} object from path {@Path}", typeof(TSchema).Name, path); + Logger.WithTopics(Topics.Actions.Load) + .LogTrace("Loading new unmapped {@TypeName} object from path {@Path}", typeof(TSchema).Name, path); var schema = await JsonSerializerEx.DeserializeAsync(path, JsonSerializerOptions); @@ -139,7 +147,8 @@ public IEnumerable LoadMany(string path) { ArgumentException.ThrowIfNullOrEmpty(path); - Logger.LogTrace("Loading collection of unmapped {@TypeName} objects from path {@Path}", typeof(TSchema).Name, path); + Logger.WithTopics(Topics.Actions.Load) + .LogTrace("Loading collection of unmapped {@TypeName} objects from path {@Path}", typeof(TSchema).Name, path); var schemas = JsonSerializerEx.Deserialize>(path, JsonSerializerOptions); @@ -154,7 +163,8 @@ public async IAsyncEnumerable LoadManyAsync(string path) { ArgumentException.ThrowIfNullOrEmpty(path); - Logger.LogTrace("Loading collection of unmapped {@TypeName} objects from path {@Path}", typeof(TSchema).Name, path); + Logger.WithTopics(Topics.Actions.Load) + .LogTrace("Loading collection of unmapped {@TypeName} objects from path {@Path}", typeof(TSchema).Name, path); var schemas = await JsonSerializerEx.DeserializeAsync>(path, JsonSerializerOptions); @@ -171,7 +181,8 @@ public void Save(TSchema obj, string path) ArgumentNullException.ThrowIfNull(obj); ArgumentException.ThrowIfNullOrEmpty(path); - Logger.LogTrace("Saving {@TypeName} to path {@Path}", typeof(TSchema).Name, path); + Logger.WithTopics(Topics.Actions.Save) + .LogTrace("Saving {@TypeName} to path {@Path}", typeof(TSchema).Name, path); JsonSerializerEx.Serialize(path, obj, JsonSerializerOptions); } @@ -182,7 +193,8 @@ public void SaveAndMap(T obj, string path) ArgumentNullException.ThrowIfNull(obj); ArgumentException.ThrowIfNullOrEmpty(path); - Logger.LogTrace("Saving {@TypeName} to path {@Path}", typeof(T).Name, path); + Logger.WithTopics(Topics.Actions.Save) + .LogTrace("Saving {@TypeName} to path {@Path}", typeof(T).Name, path); var schema = Mapper.Map(obj)!; @@ -195,7 +207,8 @@ public Task SaveAndMapAsync(T obj, string path) ArgumentNullException.ThrowIfNull(obj); ArgumentException.ThrowIfNullOrEmpty(path); - Logger.LogTrace("Saving {@TypeName} to path {@Path}", typeof(T).Name, path); + Logger.WithTopics(Topics.Actions.Save) + .LogTrace("Saving {@TypeName} to path {@Path}", typeof(T).Name, path); var schema = Mapper.Map(obj)!; @@ -208,7 +221,8 @@ public void SaveAndMapMany(IEnumerable obj, string path) ArgumentNullException.ThrowIfNull(obj); ArgumentException.ThrowIfNullOrEmpty(path); - Logger.LogTrace("Saving many {@TypeName} to path {@Path}", typeof(T).Name, path); + Logger.WithTopics(Topics.Actions.Save) + .LogTrace("Saving many {@TypeName} to path {@Path}", typeof(T).Name, path); var schema = Mapper.MapMany(obj); @@ -221,7 +235,8 @@ public Task SaveAndMapManyAsync(IEnumerable obj, string path) ArgumentNullException.ThrowIfNull(obj); ArgumentException.ThrowIfNullOrEmpty(path); - Logger.LogTrace("Saving many {@TypeName} to path {@Path}", typeof(T).Name, path); + Logger.WithTopics(Topics.Actions.Save) + .LogTrace("Saving many {@TypeName} to path {@Path}", typeof(T).Name, path); var schema = Mapper.MapMany(obj); @@ -234,7 +249,8 @@ public Task SaveAsync(TSchema obj, string path) ArgumentNullException.ThrowIfNull(obj); ArgumentException.ThrowIfNullOrEmpty(path); - Logger.LogTrace("Saving {@TypeName} to path {@Path}", typeof(TSchema).Name, path); + Logger.WithTopics(Topics.Actions.Save) + .LogTrace("Saving {@TypeName} to path {@Path}", typeof(TSchema).Name, path); return JsonSerializerEx.SerializeAsync(path, obj, JsonSerializerOptions); } @@ -245,7 +261,8 @@ public void SaveMany(IEnumerable obj, string path) ArgumentNullException.ThrowIfNull(obj); ArgumentException.ThrowIfNullOrEmpty(path); - Logger.LogTrace("Saving many {@TypeName} to path {@Path}", typeof(TSchema).Name, path); + Logger.WithTopics(Topics.Actions.Save) + .LogTrace("Saving many {@TypeName} to path {@Path}", typeof(TSchema).Name, path); JsonSerializerEx.Serialize(path, obj, JsonSerializerOptions); } @@ -256,7 +273,8 @@ public Task SaveManyAsync(IEnumerable obj, string path) ArgumentNullException.ThrowIfNull(obj); ArgumentException.ThrowIfNullOrEmpty(path); - Logger.LogTrace("Saving many {@TypeName} to path {@Path}", typeof(TSchema).Name, path); + Logger.WithTopics(Topics.Actions.Save) + .LogTrace("Saving many {@TypeName} to path {@Path}", typeof(TSchema).Name, path); return JsonSerializerEx.SerializeAsync(path, obj, JsonSerializerOptions); } diff --git a/Chaos.Storage/ExpiringFileCache.cs b/Chaos.Storage/ExpiringFileCache.cs index c8120f0add..b7e69ef937 100644 --- a/Chaos.Storage/ExpiringFileCache.cs +++ b/Chaos.Storage/ExpiringFileCache.cs @@ -1,9 +1,10 @@ using System.Collections; using System.Collections.Concurrent; -using System.Diagnostics; using Chaos.Common.Collections.Synchronized; using Chaos.Common.Synchronization; using Chaos.Extensions.Common; +using Chaos.NLog.Logging.Definitions; +using Chaos.NLog.Logging.Extensions; using Chaos.Storage.Abstractions; using Chaos.Storage.Abstractions.Definitions; using Microsoft.Extensions.Caching.Memory; @@ -86,7 +87,8 @@ ILogger> logger /// public void ForceLoad() { - Logger.LogInformation("Force loading {@TypeName} cache", typeof(T).Name); + Logger.WithTopics(Topics.Qualifiers.Forced, Topics.Actions.Load) + .LogInformation("Force loading {@TypeName} cache", typeof(T).Name); using var @lock = Sync.Enter(); @@ -143,11 +145,12 @@ public virtual Task ReloadAsync() entry.Value = CreateFromEntry(entry); } catch (Exception e) { - Logger.LogError( - e, - "Failed to reload {@TypeName} with key {@Key}", - typeof(T).Name, - key); + Logger.WithTopics(Topics.Qualifiers.Forced, Topics.Actions.Load) + .LogError( + e, + "Failed to reload {@TypeName} with key {@Key}", + typeof(T).Name, + key); //otherwise ignored } @@ -175,8 +178,11 @@ protected virtual T CreateFromEntry(ICacheEntry entry) var key = entry.Key.ToString(); var keyActual = DeconstructKeyForType(key!); - Logger.LogDebug("Creating new {@TypeName} entry with key {@Key}", typeof(T).Name, key); - var start = Stopwatch.GetTimestamp(); + Logger.WithTopics(Topics.Actions.Create) + .LogDebug("Creating new {@TypeName} entry with key {@Key}", typeof(T).Name, key); + + var metricsLogger = Logger.WithTopics(Topics.Actions.Load) + .WithMetrics(); entry.SetSlidingExpiration(TimeSpan.FromMinutes(Options.ExpirationMins)); entry.RegisterPostEvictionCallback(RemoveValueCallback); @@ -187,11 +193,10 @@ protected virtual T CreateFromEntry(ICacheEntry entry) LocalLookup[key!] = entity; - Logger.LogDebug( - "Created new {@TypeName} entry with key {@Key}, took {@Elapsed}", + metricsLogger.LogDebug( + "Created new {@TypeName} entry with key {@Key}", typeof(T).Name, - key, - Stopwatch.GetElapsedTime(start)); + key); return entity; } diff --git a/Chaos.Time/Chaos.Time.csproj b/Chaos.Time/Chaos.Time.csproj index 909fc1e7fd..4b708db147 100644 --- a/Chaos.Time/Chaos.Time.csproj +++ b/Chaos.Time/Chaos.Time.csproj @@ -1,6 +1,7 @@ + diff --git a/Chaos.Time/DeltaMonitor.cs b/Chaos.Time/DeltaMonitor.cs index 0ff00cd857..3a0a4f40b3 100644 --- a/Chaos.Time/DeltaMonitor.cs +++ b/Chaos.Time/DeltaMonitor.cs @@ -1,3 +1,5 @@ +using Chaos.NLog.Logging.Definitions; +using Chaos.NLog.Logging.Extensions; using Chaos.Time.Abstractions; using Microsoft.Extensions.Logging; @@ -96,32 +98,35 @@ private void CheckStatistics(List deltas) => _ = Task.Run( //depending on how the loop is performing, log the output at different levels if ((average > MaxDelta) || (max > 250)) - Logger.LogError( - FORMAT, - Name, - average, - median, - upperPct, - max, - deltas.Count); + Logger.WithTopics(Topics.Entities.DeltaMonitor, Topics.Actions.Update) + .LogError( + FORMAT, + Name, + average, + median, + upperPct, + max, + deltas.Count); else if ((upperPct > MaxDelta / 2) || (max > 100)) - Logger.LogWarning( - FORMAT, - Name, - average, - median, - upperPct, - max, - deltas.Count); + Logger.WithTopics(Topics.Entities.DeltaMonitor, Topics.Actions.Update) + .LogWarning( + FORMAT, + Name, + average, + median, + upperPct, + max, + deltas.Count); else - Logger.LogTrace( - FORMAT, - Name, - average, - median, - upperPct, - max, - deltas.Count); + Logger.WithTopics(Topics.Entities.DeltaMonitor, Topics.Actions.Update) + .LogTrace( + FORMAT, + Name, + average, + median, + upperPct, + max, + deltas.Count); return Task.CompletedTask; }); diff --git a/Chaos/Collections/TitleList.cs b/Chaos/Collections/TitleList.cs index e85666a6df..8b1ecb8664 100644 --- a/Chaos/Collections/TitleList.cs +++ b/Chaos/Collections/TitleList.cs @@ -6,7 +6,7 @@ namespace Chaos.Collections; public sealed class TitleList : SynchronizedList { public TitleList(IEnumerable? items = null) - : base(items?.Distinct(StringComparer.OrdinalIgnoreCase) ?? Array.Empty()) { } + : base(items?.Distinct(StringComparer.OrdinalIgnoreCase) ?? Enumerable.Empty()) { } /// public override void Add(string item) diff --git a/Chaos/Models/World/Abstractions/Creature.cs b/Chaos/Models/World/Abstractions/Creature.cs index 1c929e0ea0..07bb86cd02 100644 --- a/Chaos/Models/World/Abstractions/Creature.cs +++ b/Chaos/Models/World/Abstractions/Creature.cs @@ -406,7 +406,7 @@ public void TraverseMap( .WithProperty(this) .WithProperty(currentMap) .WithProperty(destinationMap) - .LogCritical( + .LogError( e, "Exception thrown while creature {@CreatureName} attempted to traverse from map {@FromMapInstanceId} to map {@ToMapInstanceId}", Name, diff --git a/Chaos/Models/World/Aisling.cs b/Chaos/Models/World/Aisling.cs index d1a79421c0..2cc228438d 100644 --- a/Chaos/Models/World/Aisling.cs +++ b/Chaos/Models/World/Aisling.cs @@ -550,7 +550,7 @@ public override void ShowPublicMessage(PublicMessageType publicMessageType, stri if (!Script.CanTalk()) return; - Logger.WithTopics(Topics.Entities.Aisling, Topics.Actions.Message) + Logger.WithTopics(Topics.Entities.Aisling, Topics.Entities.Message, Topics.Actions.Send) .WithProperty(this) .LogInformation( "Aisling {@AislingName} sent {@Type} message {@Message}", diff --git a/Chaos/Scripting/DialogScripts/GuildScripts/GuildMemberAdmitScript.cs b/Chaos/Scripting/DialogScripts/GuildScripts/GuildMemberAdmitScript.cs index b9730b7e50..4d5cd84858 100644 --- a/Chaos/Scripting/DialogScripts/GuildScripts/GuildMemberAdmitScript.cs +++ b/Chaos/Scripting/DialogScripts/GuildScripts/GuildMemberAdmitScript.cs @@ -95,7 +95,7 @@ private void OnDisplayingAccepted(Aisling source) guild.AddMember(aislingToAdmit, source); - Logger.WithTopics(Topics.Entities.Guild, Topics.Actions.Admit) + Logger.WithTopics(Topics.Entities.Guild, Topics.Actions.Join) .WithProperty(Subject) .WithProperty(Subject.DialogSource) .WithProperty(source) diff --git a/Chaos/Services/MapperProfiles/AislingMapperProfile.cs b/Chaos/Services/MapperProfiles/AislingMapperProfile.cs index 2dc80b18a4..d10168b929 100644 --- a/Chaos/Services/MapperProfiles/AislingMapperProfile.cs +++ b/Chaos/Services/MapperProfiles/AislingMapperProfile.cs @@ -145,7 +145,6 @@ AislingSchema IMapperProfile.Map(Aisling obj) Titles = obj.Titles.ToList(), UserOptions = Mapper.Map(obj.Options), IgnoreList = obj.IgnoreList.ToList(), - Effects = Array.Empty(), ChannelSettings = Mapper.MapMany(obj.ChannelSettings).ToList() }; diff --git a/Chaos/Services/Servers/WorldServer.cs b/Chaos/Services/Servers/WorldServer.cs index 2a0fe52c8b..c1eaec52cb 100644 --- a/Chaos/Services/Servers/WorldServer.cs +++ b/Chaos/Services/Servers/WorldServer.cs @@ -990,8 +990,10 @@ async ValueTask InnerOnPublicMessage(IWorldClient localClient, PublicMessageArgs { Logger.WithTopics( Topics.Entities.Aisling, - Topics.Actions.Message, - Topics.Actions.Command) + Topics.Entities.Message, + Topics.Actions.Send, + Topics.Entities.Command, + Topics.Actions.Execute) .WithProperty(localClient) .LogDebug("Aisling {@AislingName} sent command {@Command}", localClient.Aisling, message); @@ -1398,7 +1400,11 @@ ValueTask InnerOnWhisper(IWorldClient localClient, WhisperArgs localArgs) //let them waste their time typing for no reason if (targetAisling.IgnoreList.ContainsI(fromAisling.Name)) { - Logger.WithTopics(Topics.Entities.Aisling, Topics.Actions.Message, Topics.Qualifiers.Harassment) + Logger.WithTopics( + Topics.Entities.Aisling, + Topics.Entities.Message, + Topics.Actions.Send, + Topics.Qualifiers.Harassment) .WithProperty(fromAisling) .WithProperty(targetAisling) .LogWarning( @@ -1410,7 +1416,7 @@ ValueTask InnerOnWhisper(IWorldClient localClient, WhisperArgs localArgs) return default; } - Logger.WithTopics(Topics.Entities.Aisling, Topics.Actions.Message) + Logger.WithTopics(Topics.Entities.Aisling, Topics.Entities.Message, Topics.Actions.Send) .WithProperty(fromAisling) .WithProperty(targetAisling) .LogInformation( diff --git a/Chaos/Services/Storage/Abstractions/PeriodicSaveStoreBase.cs b/Chaos/Services/Storage/Abstractions/PeriodicSaveStoreBase.cs index 6ddb099147..4eb601fb6e 100644 --- a/Chaos/Services/Storage/Abstractions/PeriodicSaveStoreBase.cs +++ b/Chaos/Services/Storage/Abstractions/PeriodicSaveStoreBase.cs @@ -110,7 +110,7 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) } catch (Exception e) { Logger.WithTopics(Topics.Actions.Save) - .LogCritical(e, "Exception while performing save"); + .LogError(e, "Exception while performing save"); } Logger.WithTopics(Topics.Actions.Save) diff --git a/Directory.Build.props b/Directory.Build.props index 6c5d4070bc..a8be0a3d7c 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 11 + latest net7.0 true true diff --git a/Tools/SeqConfigurator/Builders/ChartBuilder.cs b/Tools/SeqConfigurator/Builders/ChartBuilder.cs new file mode 100644 index 0000000000..cf0275e75f --- /dev/null +++ b/Tools/SeqConfigurator/Builders/ChartBuilder.cs @@ -0,0 +1,81 @@ +using Seq.Api; +using Seq.Api.Model.Dashboarding; +using Seq.Api.Model.Signals; + +namespace SeqConfigurator.Builders; + +public class ChartBuilder +{ + private readonly TaskCompletionSource Promise; + private readonly SeqConnection SeqConnection; + + private ChartBuilder(SeqConnection seqConnection) + { + SeqConnection = seqConnection; + Promise = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + + Promise.SetResult(new ChartPart()); + } + + public Task BuildAsync() => Promise.Task; + + public static ChartBuilder Create(SeqConnection seqConnection) => new(seqConnection); + + public ChartBuilder WithDimensions(int columnCount = 6, int rowCount = 1) + { + Promise.Task.ContinueWith( + async creation => (await creation).DisplayStyle = new ChartDisplayStylePart + { + HeightRows = rowCount, + WidthColumns = columnCount + }); + + return this; + } + + public ChartBuilder WithQuery(params Action[] builderActions) + { + var buildTasks = builderActions.Select( + action => + { + var builder = ChartQueryBuilder.Create(SeqConnection); + action(builder); + + return builder.BuildAsync(); + }); + + var chartQueriesTask = Task.WhenAll(buildTasks); + + Promise.Task.ContinueWith( + async creation => + { + var chartPart = await creation; + var chartQueries = await chartQueriesTask; + chartPart.Queries.AddRange(chartQueries); + }); + + return this; + } + + public ChartBuilder WithSignalExpression(string signalId) => WithSignalExpression( + builder => + builder.WithKind(SignalExpressionKind.Signal) + .WithSignal(signalId)); + + public ChartBuilder WithSignalExpression(Action builderAction) + { + var signalExpressionBuilder = SignalExpressionBuilder.Create(SeqConnection); + builderAction(signalExpressionBuilder); + + Promise.Task.ContinueWith(async creation => (await creation).SignalExpression = await signalExpressionBuilder.BuildAsync()); + + return this; + } + + public ChartBuilder WithTitle(string title) + { + Promise.Task.ContinueWith(async creation => (await creation).Title = title); + + return this; + } +} \ No newline at end of file diff --git a/Tools/SeqConfigurator/Builders/ChartQueryBuilder.cs b/Tools/SeqConfigurator/Builders/ChartQueryBuilder.cs new file mode 100644 index 0000000000..9525e714c9 --- /dev/null +++ b/Tools/SeqConfigurator/Builders/ChartQueryBuilder.cs @@ -0,0 +1,107 @@ +using Seq.Api; +using Seq.Api.Model.Dashboarding; +using Seq.Api.Model.Shared; + +namespace SeqConfigurator.Builders; + +public class ChartQueryBuilder +{ + private readonly TaskCompletionSource Promise; + private readonly SeqConnection SeqConnection; + + private ChartQueryBuilder(SeqConnection seqConnection) + { + SeqConnection = seqConnection; + Promise = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + + Promise.SetResult(new ChartQueryPart()); + } + + public Task BuildAsync() => Promise.Task; + + public static ChartQueryBuilder Create(SeqConnection seqConnection) => new(seqConnection); + + public ChartQueryBuilder WithDisplayStyle( + MeasurementDisplayType type, + bool lineShowMarkers = true, + bool lineFillToZeroY = false, + bool barOverlaySum = false, + bool suppressLegend = false, + MeasurementDisplayPalette palette = MeasurementDisplayPalette.Default + ) + { + Promise.Task.ContinueWith( + async creation => + { + var chartQuery = await creation; + + chartQuery.DisplayStyle = new MeasurementDisplayStylePart + { + Type = type, + LineShowMarkers = lineShowMarkers, + LineFillToZeroY = lineFillToZeroY, + BarOverlaySum = barOverlaySum, + SuppressLegend = suppressLegend, + Palette = palette + }; + }); + + return this; + } + + public ChartQueryBuilder WithGroupBy(params string[] groupByClauses) + { + Promise.Task.ContinueWith(async creation => (await creation).GroupBy.AddRange(groupByClauses)); + + return this; + } + + public ChartQueryBuilder WithLimit(int limit) + { + Promise.Task.ContinueWith(async creation => (await creation).Limit = limit); + + return this; + } + + public ChartQueryBuilder WithOrderBy(params string[] orderByClauses) + { + Promise.Task.ContinueWith(async creation => (await creation).OrderBy.AddRange(orderByClauses)); + + return this; + } + + public ChartQueryBuilder WithSelect(params (string Label, string SelectClause)[] columns) + { + Promise.Task.ContinueWith( + async creation => + { + var measurements = columns.Select( + col => new ColumnPart + { + Label = col.Label, + Value = col.SelectClause + }); + + (await creation).Measurements.AddRange(measurements); + }); + + return this; + } + + public ChartQueryBuilder WithSignalExpression(Action builder) + { + var signalExpressionBuilder = SignalExpressionBuilder.Create(SeqConnection); + builder(signalExpressionBuilder); + + Promise.Task.ContinueWith(async creation => (await creation).SignalExpression = await signalExpressionBuilder.BuildAsync()); + + return this; + } + + public ChartQueryBuilder WithWhere(string whereClause) + { + Promise.Task.ContinueWith(async creation => (await creation).Where = whereClause); + + return this; + } +} \ No newline at end of file diff --git a/Tools/SeqConfigurator/Builders/DashboardBuilder.cs b/Tools/SeqConfigurator/Builders/DashboardBuilder.cs new file mode 100644 index 0000000000..45a49384dd --- /dev/null +++ b/Tools/SeqConfigurator/Builders/DashboardBuilder.cs @@ -0,0 +1,84 @@ +using Seq.Api; +using Seq.Api.Model.Dashboarding; +using Seq.Api.Model.Signals; + +namespace SeqConfigurator.Builders; + +public class DashboardBuilder +{ + private readonly TaskCompletionSource Promise; + private readonly SeqConnection SeqConnection; + + private DashboardBuilder(SeqConnection seqConnection) + { + SeqConnection = seqConnection; + Promise = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + + SeqConnection.Dashboards.TemplateAsync() + .ContinueWith(async task => Promise.SetResult(await task)); + } + + public static DashboardBuilder Create(SeqConnection seqConnection) => new(seqConnection); + + public DashboardBuilder IsProtected() + { + Promise.Task.ContinueWith(async creation => (await creation).IsProtected = true); + + return this; + } + + public DashboardBuilder IsShared() + { + Promise.Task.ContinueWith(async creation => (await creation).OwnerId = null); + + return this; + } + + public async Task SaveAsync() => await SeqConnection.Dashboards.AddAsync(await Promise.Task); + + public DashboardBuilder WithCharts(params Action[] builderActions) + { + var buildTasks = builderActions.Select( + builderAction => + { + var chartBuilder = ChartBuilder.Create(SeqConnection); + builderAction(chartBuilder); + + return chartBuilder.BuildAsync(); + }); + + var chartTasks = Task.WhenAll(buildTasks); + + Promise.Task.ContinueWith( + async creation => + { + var dashboard = await creation; + var charts = await chartTasks; + dashboard.Charts.AddRange(charts); + }); + + return this; + } + + public DashboardBuilder WithSignalExpression(string signalTitle) => WithSignalExpression( + builder => builder.WithKind(SignalExpressionKind.Signal) + .WithSignal(signalTitle)); + + public DashboardBuilder WithSignalExpression(Action builderAction) + { + var signalBuilder = SignalExpressionBuilder.Create(SeqConnection); + builderAction(signalBuilder); + var signalTask = signalBuilder.BuildAsync(); + + Promise.Task.ContinueWith(async creation => (await creation).SignalExpression = await signalTask); + + return this; + } + + public DashboardBuilder WithTitle(string title) + { + Promise.Task.ContinueWith(async creation => (await creation).Title = title); + + return this; + } +} \ No newline at end of file diff --git a/Tools/SeqConfigurator/Builders/SignalBuilder.cs b/Tools/SeqConfigurator/Builders/SignalBuilder.cs new file mode 100644 index 0000000000..00307608f5 --- /dev/null +++ b/Tools/SeqConfigurator/Builders/SignalBuilder.cs @@ -0,0 +1,72 @@ +using Seq.Api; +using Seq.Api.Model.Shared; +using Seq.Api.Model.Signals; + +namespace SeqConfigurator.Builders; + +public sealed class SignalBuilder +{ + private readonly TaskCompletionSource Promise; + private readonly SeqConnection SeqConnection; + + private SignalBuilder(SeqConnection seqConnection) + { + SeqConnection = seqConnection; + Promise = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + + SeqConnection.Signals.TemplateAsync() + .ContinueWith(async task => Promise.SetResult(await task)); + } + + public static SignalBuilder Create(SeqConnection seqConnection) => new(seqConnection); + + public SignalBuilder IsShared() + { + Promise.Task.ContinueWith(async creation => (await creation).OwnerId = null); + + return this; + } + + public async Task SaveAsync() => await SeqConnection.Signals.AddAsync(await Promise.Task); + + public SignalBuilder WithDescription(string description) + { + Promise.Task.ContinueWith(async creation => (await creation).Description = description); + + return this; + } + + public SignalBuilder WithFilter(string filter, string? filterNonStrict = null, string? filterDescription = null) + { + var filterDescriptor = new DescriptiveFilterPart + { + Filter = filter, + FilterNonStrict = filterNonStrict, + Description = filterDescription + }; + + Promise.Task.ContinueWith(async creation => (await creation).Filters.Add(filterDescriptor)); + + return this; + } + + public SignalBuilder WithGrouping(SignalGrouping grouping, string? groupName = null) + { + Promise.Task.ContinueWith( + async creation => + { + var signal = await creation; + signal.Grouping = grouping; + signal.ExplicitGroupName = groupName; + }); + + return this; + } + + public SignalBuilder WithTitle(string title) + { + Promise.Task.ContinueWith(async creation => (await creation).Title = title); + + return this; + } +} \ No newline at end of file diff --git a/Tools/SeqConfigurator/Builders/SignalExpressionBuilder.cs b/Tools/SeqConfigurator/Builders/SignalExpressionBuilder.cs new file mode 100644 index 0000000000..924a98e4c9 --- /dev/null +++ b/Tools/SeqConfigurator/Builders/SignalExpressionBuilder.cs @@ -0,0 +1,102 @@ +using Chaos.Extensions.Common; +using Seq.Api; +using Seq.Api.Model.Signals; + +namespace SeqConfigurator.Builders; + +public class SignalExpressionBuilder +{ + private readonly TaskCompletionSource> AllSignals; + private readonly TaskCompletionSource Promise; + private readonly SeqConnection SeqConnection; + + private SignalExpressionBuilder(SeqConnection seqConnection) + { + SeqConnection = seqConnection; + Promise = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + AllSignals = new TaskCompletionSource>(); + + Promise.TrySetResult(new SignalExpressionPart()); + + SeqConnection.Signals.ListAsync(shared: true) + .ContinueWith(async task => AllSignals.SetResult(await task)); + } + + public Task BuildAsync() => Promise.Task; + + public static SignalExpressionBuilder Create(SeqConnection seqConnection) => new(seqConnection); + + public SignalExpressionBuilder WithKind(SignalExpressionKind kind) + { + Promise.Task.ContinueWith(async creation => (await creation).Kind = kind); + + return this; + } + + public SignalExpressionBuilder WithLeft(SignalExpressionPart left) + { + Promise.Task.ContinueWith(async creation => (await creation).Left = left); + + return this; + } + + public SignalExpressionBuilder WithLeftSignal(string signalTitle) + { + Promise.Task.ContinueWith( + async creation => + { + var allSignals = await AllSignals.Task; + var leftSignal = allSignals.First(s => s.Title.EqualsI(signalTitle)); + var expression = await creation; + + expression.Left = new SignalExpressionPart + { + Kind = SignalExpressionKind.Signal, + SignalId = leftSignal.Id + }; + }); + + return this; + } + + public SignalExpressionBuilder WithRight(SignalExpressionPart right) + { + Promise.Task.ContinueWith(async creation => (await creation).Right = right); + + return this; + } + + public SignalExpressionBuilder WithRightSignal(string signalTitle) + { + Promise.Task.ContinueWith( + async creation => + { + var allSignals = await AllSignals.Task; + var rightSignal = allSignals.First(s => s.Title.EqualsI(signalTitle)); + var expression = await creation; + + expression.Right = new SignalExpressionPart + { + Kind = SignalExpressionKind.Signal, + SignalId = rightSignal.Id + }; + }); + + return this; + } + + public SignalExpressionBuilder WithSignal(string signalTitle) + { + Promise.Task.ContinueWith( + async creation => + { + var allSignals = await AllSignals.Task; + var signal = allSignals.First(s => s.Title.EqualsI(signalTitle)); + var expression = await creation; + + expression.SignalId = signal.Id; + }); + + return this; + } +} \ No newline at end of file diff --git a/Tools/SeqConfigurator/Program.cs b/Tools/SeqConfigurator/Program.cs index a8ed2d102a..2a89ca3f90 100644 --- a/Tools/SeqConfigurator/Program.cs +++ b/Tools/SeqConfigurator/Program.cs @@ -4,8 +4,8 @@ using NLog; using Seq.Api; using Seq.Api.Model.Dashboarding; -using Seq.Api.Model.Shared; using Seq.Api.Model.Signals; +using SeqConfigurator.Builders; using var seq = new SeqConnection("http://localhost:5341", "AdminGuestSeqToken"); await seq.EnsureConnectedAsync(TimeSpan.FromSeconds(30)); @@ -44,144 +44,59 @@ foreach (var logLevel in logLevels) { var level = logLevel.ToString()!.ToUpper(); - var template = await seq.Signals.TemplateAsync(); - template.Title = level; - template.Description = level; - - template.Filters = new List - { - new() - { - Description = $"Filter by LogLevel = {level}", - Filter = $"@Level = '{level}'", - FilterNonStrict = $@"@Level = ""{level}""" - } - }; - - template.Grouping = SignalGrouping.Explicit; - template.ExplicitGroupName = "LogLevels"; - - await seq.Signals.AddAsync(template); + + await SignalBuilder.Create(seq) + .IsShared() + .WithTitle(level) + .WithDescription(level) + .WithFilter($"@Level = '{level}'", filterDescription: $"Filter by LogLevel = {level}") + .WithGrouping(SignalGrouping.Explicit, "LogLevels") + .SaveAsync(); } Console.WriteLine("Creating Topics.Servers signals"); foreach (var topic in serverTopics) -{ - var template = await seq.Signals.TemplateAsync(); - template.Title = topic.Name; - template.Description = topic.Name; - - template.Filters = new List - { - new() - { - Description = $"Filter by Topics.Servers.{topic.Name}", - Filter = $"'{topic.Name}' in Topics", - FilterNonStrict = $@"""{topic.Name}"" in Topics" - } - }; - - template.Grouping = SignalGrouping.Explicit; - template.ExplicitGroupName = "Servers"; - - await seq.Signals.AddAsync(template); -} + await SignalBuilder.Create(seq) + .IsShared() + .WithTitle(topic.Name) + .WithDescription(topic.Name) + .WithFilter($"'{topic.Name}' in Topics", filterDescription: $"Filter by Topics.Servers.{topic.Name}") + .WithGrouping(SignalGrouping.Explicit, "Servers") + .SaveAsync(); Console.WriteLine("Creating Topics.Entities signals"); foreach (var topic in entityTopics) -{ - var template = await seq.Signals.TemplateAsync(); - template.Title = topic.Name; - template.Description = topic.Name; - - template.Filters = new List - { - new() - { - Description = $"Filter by Topics.Entities.{topic.Name}", - Filter = $"'{topic.Name}' in Topics", - FilterNonStrict = $@"""{topic.Name}"" in Topics" - } - }; - - template.Grouping = SignalGrouping.Explicit; - template.ExplicitGroupName = "Entities"; - - await seq.Signals.AddAsync(template); -} + await SignalBuilder.Create(seq) + .IsShared() + .WithTitle(topic.Name) + .WithDescription(topic.Name) + .WithFilter($"'{topic.Name}' in Topics", filterDescription: $"Filter by Topics.Entities.{topic.Name}") + .WithGrouping(SignalGrouping.Explicit, "Entities") + .SaveAsync(); Console.WriteLine("Creating Topics.Actions signals"); foreach (var topic in actionTopics) -{ - var template = await seq.Signals.TemplateAsync(); - template.Title = topic.Name; - template.Description = topic.Name; - - template.Filters = new List - { - new() - { - Description = $"Filter by Topics.Actions.{topic.Name}", - Filter = $"'{topic.Name}' in Topics", - FilterNonStrict = $@"""{topic.Name}"" in Topics" - } - }; - - template.Grouping = SignalGrouping.Explicit; - template.ExplicitGroupName = "Actions"; - - await seq.Signals.AddAsync(template); -} + await SignalBuilder.Create(seq) + .IsShared() + .WithTitle(topic.Name) + .WithDescription(topic.Name) + .WithFilter($"'{topic.Name}' in Topics", filterDescription: $"Filter by Topics.Actions.{topic.Name}") + .WithGrouping(SignalGrouping.Explicit, "Actions") + .SaveAsync(); Console.WriteLine("Creating Topics.Qualifiers signals"); foreach (var topic in qualifierTopics) -{ - var template = await seq.Signals.TemplateAsync(); - template.Title = topic.Name; - template.Description = topic.Name; - - template.Filters = new List - { - new() - { - Description = $"Filter by Topics.Qualifiers.{topic.Name}", - Filter = $"'{topic.Name}' in Topics", - FilterNonStrict = $@"""{topic.Name}"" in Topics" - } - }; - - template.Grouping = SignalGrouping.Explicit; - template.ExplicitGroupName = "Qualifiers"; - - await seq.Signals.AddAsync(template); -} - -Console.WriteLine("Creating Delta Monitor signal"); - -{ - var template = await seq.Signals.TemplateAsync(); - template.Title = "Delta Monitor"; - template.Description = "Delta Monitor"; - - template.Filters = new List - { - new() - { - Description = "Filter by Delta Monitor", - Filter = "@MessageTemplate like 'Delta Monitor%'", - FilterNonStrict = @"@MessageTemplate like ""Delta Monitor%""" - } - }; - - template.Grouping = SignalGrouping.Explicit; - template.ExplicitGroupName = "Delta Monitor"; - - await seq.Signals.AddAsync(template); -} + await SignalBuilder.Create(seq) + .IsShared() + .WithTitle(topic.Name) + .WithDescription(topic.Name) + .WithFilter($"'{topic.Name}' in Topics", filterDescription: $"Filter by Topics.Qualifiers.{topic.Name}") + .WithGrouping(SignalGrouping.Explicit, "Qualifiers") + .SaveAsync(); Console.WriteLine("Clearing existing dashboards"); @@ -193,219 +108,77 @@ Console.WriteLine("Creating Dashboard"); -var signals = await seq.Signals.ListAsync(shared: true); - -{ - var dashboard = await seq.Dashboards.TemplateAsync(); - dashboard.OwnerId = null; - dashboard.Title = "Monitor"; - dashboard.IsProtected = false; - dashboard.SignalExpression = null; - - dashboard.Charts = new List - { - new() - { - Title = "Map Count", - SignalExpression = new SignalExpressionPart - { - Kind = SignalExpressionKind.Signal, - SignalId = signals.First(signal => signal.Title == "Delta Monitor").Id - }, - Queries = new List - { - new() - { - Measurements = new List - { - new() - { - Value = "count(distinct(Name))", - Label = "MapCount" - } - }, - Where = null, - SignalExpression = null, - GroupBy = new List(), - DisplayStyle = new MeasurementDisplayStylePart - { - Type = MeasurementDisplayType.Line, - LineFillToZeroY = false, - LineShowMarkers = true, - BarOverlaySum = false, - SuppressLegend = false, - Palette = MeasurementDisplayPalette.Default - } - } - } - }, - new() - { - Title = "Aisling Count", - SignalExpression = new SignalExpressionPart - { - Kind = SignalExpressionKind.Signal, - SignalId = signals.First(signal => signal.Title == "Aisling").Id - }, - Queries = new List - { - new() - { - Measurements = new List - { - new() - { - Value = "count(distinct(AislingName))", - Label = "AislingCount" - } - }, - Where = "Has(AislingName)", - GroupBy = new List(), - DisplayStyle = new MeasurementDisplayStylePart - { - Type = MeasurementDisplayType.Line, - LineFillToZeroY = false, - LineShowMarkers = true, - BarOverlaySum = false, - SuppressLegend = false, - Palette = MeasurementDisplayPalette.Default - } - } - } - }, - new() - { - Title = "Average Deltas", - SignalExpression = new SignalExpressionPart - { - Kind = SignalExpressionKind.Signal, - SignalId = signals.First(signal => signal.Title == "Delta Monitor").Id - }, - Queries = new List - { - new() - { - Measurements = new List - { - new() - { - Value = "mean(Average)", - Label = "Delta" - } - }, - GroupBy = new List { "Name" }, - DisplayStyle = new MeasurementDisplayStylePart - { - Type = MeasurementDisplayType.Line, - LineFillToZeroY = false, - LineShowMarkers = true, - BarOverlaySum = false, - SuppressLegend = false, - Palette = MeasurementDisplayPalette.Default - } - } - } - }, - new() - { - Title = "Mean Deltas", - SignalExpression = new SignalExpressionPart - { - Kind = SignalExpressionKind.Signal, - SignalId = signals.First(signal => signal.Title == "Delta Monitor").Id - }, - Queries = new List - { - new() - { - Measurements = new List - { - new() - { - Value = "mean(Median)", - Label = "Delta" - } - }, - GroupBy = new List { "Name" }, - DisplayStyle = new MeasurementDisplayStylePart - { - Type = MeasurementDisplayType.Line, - LineFillToZeroY = false, - LineShowMarkers = true, - BarOverlaySum = false, - SuppressLegend = false, - Palette = MeasurementDisplayPalette.Default - } - } - } - }, - new() - { - Title = "95th% Deltas", - SignalExpression = new SignalExpressionPart - { - Kind = SignalExpressionKind.Signal, - SignalId = signals.First(signal => signal.Title == "Delta Monitor").Id - }, - Queries = new List - { - new() - { - Measurements = new List - { - new() - { - Value = "mean(UpperPercentile)", - Label = "Delta" - } - }, - GroupBy = new List { "Name" }, - DisplayStyle = new MeasurementDisplayStylePart - { - Type = MeasurementDisplayType.Line, - LineFillToZeroY = false, - LineShowMarkers = true, - BarOverlaySum = false, - SuppressLegend = false, - Palette = MeasurementDisplayPalette.Default - } - } - } - }, - new() - { - Title = "Max Deltas", - SignalExpression = new SignalExpressionPart - { - Kind = SignalExpressionKind.Signal, - SignalId = signals.First(signal => signal.Title == "Delta Monitor").Id - }, - Queries = new List - { - new() - { - Measurements = new List - { - new() - { - Value = "mean(Max)", - Label = "Delta" - } - }, - GroupBy = new List { "Name" }, - DisplayStyle = new MeasurementDisplayStylePart - { - Type = MeasurementDisplayType.Line, - LineFillToZeroY = false, - LineShowMarkers = true, - BarOverlaySum = false, - SuppressLegend = false, - Palette = MeasurementDisplayPalette.Default - } - } - } - } - }; - - await seq.Dashboards.AddAsync(dashboard); -} \ No newline at end of file +await DashboardBuilder.Create(seq) + .IsShared() + .WithTitle("Monitor") + .WithCharts( + BuildMapCountChart, + BuildAislingCountChart, + BuildAverageDeltasChart, + BuildMedianDeltasChart, + Build95ThPctDeltasChart, + BuildMaxDeltasChart) + .SaveAsync(); + +Console.WriteLine("Done"); + +return; + +static void BuildAverageDeltasChart(ChartBuilder chartBuilder) => + chartBuilder.WithTitle("Average Deltas") + .WithSignalExpression("DeltaMonitor") + .WithDimensions(12, 2) + .WithQuery( + queryBuilder => + queryBuilder.WithSelect(("Delta", "mean(Average)")) + .WithGroupBy("Name") + .WithDisplayStyle(MeasurementDisplayType.Line)); + +static void BuildMedianDeltasChart(ChartBuilder chartBuilder) => + chartBuilder.WithTitle("Median Deltas") + .WithSignalExpression("DeltaMonitor") + .WithDimensions(12, 2) + .WithQuery( + queryBuilder => + queryBuilder.WithSelect(("Delta", "mean(Median)")) + .WithGroupBy("Name") + .WithDisplayStyle(MeasurementDisplayType.Line)); + +static void Build95ThPctDeltasChart(ChartBuilder chartBuilder) => + chartBuilder.WithTitle("95th% Deltas") + .WithSignalExpression("DeltaMonitor") + .WithDimensions(12, 2) + .WithQuery( + queryBuilder => + queryBuilder.WithSelect(("Delta", "mean(UpperPercentile)")) + .WithGroupBy("Name") + .WithDisplayStyle(MeasurementDisplayType.Line)); + +static void BuildMaxDeltasChart(ChartBuilder chartBuilder) => + chartBuilder.WithTitle("Max Deltas") + .WithSignalExpression("DeltaMonitor") + .WithDimensions(12, 2) + .WithQuery( + queryBuilder => + queryBuilder.WithSelect(("Delta", "mean(Max)")) + .WithGroupBy("Name") + .WithDisplayStyle(MeasurementDisplayType.Line)); + +static void BuildAislingCountChart(ChartBuilder chartBuilder) => + chartBuilder.WithTitle("AislingCount") + .WithSignalExpression("Aisling") + .WithDimensions(12, 2) + .WithQuery( + queryBuilder => + queryBuilder.WithSelect(("MapCount", "count(distinct(Name))")) + .WithWhere("Has(AislingName)") + .WithDisplayStyle(MeasurementDisplayType.Line)); + +static void BuildMapCountChart(ChartBuilder chartBuilder) => + chartBuilder.WithTitle("MapCount") + .WithSignalExpression("DeltaMonitor") + .WithDimensions(12, 2) + .WithQuery( + queryBuilder => + queryBuilder.WithSelect(("MapName", "count(distinct(Name))")) + .WithDisplayStyle(MeasurementDisplayType.Line)); \ No newline at end of file diff --git a/Tools/SeqConfigurator/SeqConfigurator.csproj b/Tools/SeqConfigurator/SeqConfigurator.csproj index ed10eb40ec..51508c4007 100644 --- a/Tools/SeqConfigurator/SeqConfigurator.csproj +++ b/Tools/SeqConfigurator/SeqConfigurator.csproj @@ -16,4 +16,5 @@ +