Updates to Elastic logging

This commit is contained in:
Ske
2021-06-10 14:21:05 +02:00
parent ae9ed0f4ee
commit ae82bb4168
18 changed files with 150 additions and 97 deletions

View File

@@ -176,14 +176,13 @@ namespace PluralKit.Bot
async Task HandleEventInner()
{
await Task.Yield();
using var _ = LogContext.PushProperty("EventId", Guid.NewGuid());
_logger
.ForContext("Elastic", "yes?")
.Verbose("Gateway event: {@Event}", evt);
await using var serviceScope = _services.BeginLifetimeScope();
using var _ = LogContext.PushProperty("EventId", Guid.NewGuid());
using var __ = LogContext.Push(serviceScope.Resolve<SerilogGatewayEnricherFactory>().GetEnricher(shard, evt));
_logger.Verbose("Received gateway event: {@Event}", evt);
// Also, find a Sentry enricher for the event type (if one is present), and ask it to put some event data in the Sentry scope
var sentryEnricher = serviceScope.ResolveOptional<ISentryEnricher<T>>();
sentryEnricher?.Enrich(serviceScope.Resolve<Scope>(), shard, evt);
@@ -218,9 +217,7 @@ namespace PluralKit.Bot
// Make this beforehand so we can access the event ID for logging
var sentryEvent = new SentryEvent(exc);
_logger
.ForContext("Elastic", "yes?")
.Error(exc, "Exception in event handler: {SentryEventId}", sentryEvent.EventId);
_logger.Error(exc, "Exception in event handler: {SentryEventId}", sentryEvent.EventId);
// If the event is us responding to our own error messages, don't bother logging
if (evt is MessageCreateEvent mc && mc.Author.Id == shard.User?.Id)

View File

@@ -116,6 +116,7 @@ namespace PluralKit.Bot
Timeout = TimeSpan.FromSeconds(5)
}).AsSelf().SingleInstance();
builder.RegisterInstance(SystemClock.Instance).As<IClock>();
builder.RegisterType<SerilogGatewayEnricherFactory>().AsSelf().SingleInstance();
builder.RegisterType<DiscordRequestObserver>().AsSelf().SingleInstance();
}

View File

@@ -22,7 +22,7 @@ namespace PluralKit.Bot
_db = db;
_repo = repo;
_clock = clock;
_logger = logger;
_logger = logger.ForContext<CommandMessageService>();
}
public async Task RegisterMessage(ulong messageId, ulong authorId)

View File

@@ -71,7 +71,7 @@ namespace PluralKit.Bot
public async Task<Webhook> InvalidateAndRefreshWebhook(ulong channelId, Webhook webhook)
{
// note: webhook.ChannelId may not be the same as channelId >.>
_logger.Information("Refreshing webhook for channel {Channel}", webhook.ChannelId);
_logger.Debug("Refreshing webhook for channel {Channel}", webhook.ChannelId);
_webhooks.TryRemove(webhook.ChannelId, out _);
return await GetWebhook(channelId);

View File

@@ -16,6 +16,7 @@ using Serilog.Context;
namespace PluralKit.Bot
{
// TODO: phase this out; it currently still handles metrics but that needs to be moved to Myriad probably?
public class DiscordRequestObserver: IObserver<KeyValuePair<string, object>>
{
private readonly IMetrics _metrics;
@@ -77,6 +78,8 @@ namespace PluralKit.Bot
{
var endpoint = GetEndpointName(response.RequestMessage);
// (see phase-out notice at top of file)
/*
using (LogContext.PushProperty("Elastic", "yes?"))
{
if ((int) response.StatusCode >= 400)
@@ -101,6 +104,7 @@ namespace PluralKit.Bot
response.ReasonPhrase,
activity.Duration.TotalMilliseconds);
}
*/
if (IsDiscordApiRequest(response))
{

View File

@@ -1,58 +0,0 @@
using System.Collections.Generic;
using Myriad.Gateway;
using Serilog.Core;
using Serilog.Events;
namespace PluralKit.Bot
{
// This class is unused and commented out in Init.cs - it's here from before the lib conversion. Is it needed??
public class EventDestructuring: IDestructuringPolicy
{
public bool TryDestructure(object value, ILogEventPropertyValueFactory propertyValueFactory,
out LogEventPropertyValue result)
{
if (!(value is IGatewayEvent evt))
{
result = null;
return false;
}
var props = new List<LogEventProperty>
{
new("Type", new ScalarValue(evt.EventType())),
};
void AddMessage(ulong id, ulong channelId, ulong? guildId, ulong? author)
{
props.Add(new LogEventProperty("MessageId", new ScalarValue(id)));
props.Add(new LogEventProperty("ChannelId", new ScalarValue(channelId)));
props.Add(new LogEventProperty("GuildId", new ScalarValue(guildId ?? 0)));
if (author != null)
props.Add(new LogEventProperty("AuthorId", new ScalarValue(author)));
}
if (value is MessageCreateEvent mc)
AddMessage(mc.Id, mc.ChannelId, mc.GuildId, mc.Author.Id);
else if (value is MessageUpdateEvent mu)
AddMessage(mu.Id, mu.ChannelId, mu.GuildId.Value, mu.Author.Value?.Id);
else if (value is MessageDeleteEvent md)
AddMessage(md.Id, md.ChannelId, md.GuildId, null);
else if (value is MessageReactionAddEvent mra)
{
AddMessage(mra.MessageId, mra.ChannelId, mra.GuildId, null);
props.Add(new LogEventProperty("ReactingUserId", new ScalarValue(mra.Emoji)));
props.Add(new LogEventProperty("Emoji", new ScalarValue(mra.Emoji.Name)));
}
// Want shard last, just for visual reasons
// TODO: D#+ update means we can't pull shard ID out of this, what do?
// props.Add(new LogEventProperty("Shard", new ScalarValue(dea.Client.ShardId)));
result = new StructureValue(props);
return true;
}
}
}

View File

@@ -0,0 +1,104 @@
using System.Collections.Generic;
using Myriad.Cache;
using Myriad.Extensions;
using Myriad.Gateway;
using Serilog.Core;
using Serilog.Events;
namespace PluralKit.Bot
{
public class SerilogGatewayEnricherFactory
{
private readonly Bot _bot;
private readonly IDiscordCache _cache;
public SerilogGatewayEnricherFactory(Bot bot, IDiscordCache cache)
{
_bot = bot;
_cache = cache;
}
public ILogEventEnricher GetEnricher(Shard shard, IGatewayEvent evt)
{
var props = new List<LogEventProperty>
{
new("ShardId", new ScalarValue(shard.ShardId)),
};
var (guild, channel) = GetGuildChannelId(evt);
var user = GetUserId(evt);
var message = GetMessageId(evt);
if (guild != null)
props.Add(new("GuildId", new ScalarValue(guild.Value)));
if (channel != null)
{
props.Add(new("GuildId", new ScalarValue(channel.Value)));
var botPermissions = _bot.PermissionsIn(channel.Value);
props.Add(new("BotPermissions", new ScalarValue(botPermissions)));
}
if (message != null)
props.Add(new("MessageId", new ScalarValue(message.Value)));
if (user != null)
props.Add(new("UserId", new ScalarValue(user.Value)));
if (evt is MessageCreateEvent mce)
props.Add(new("UserPermissions", new ScalarValue(_cache.PermissionsFor(mce))));
return new Inner(props);
}
private (ulong?, ulong?) GetGuildChannelId(IGatewayEvent evt) => evt switch
{
ChannelCreateEvent e => (e.GuildId, e.Id),
ChannelUpdateEvent e => (e.GuildId, e.Id),
ChannelDeleteEvent e => (e.GuildId, e.Id),
MessageCreateEvent e => (e.GuildId, e.ChannelId),
MessageUpdateEvent e => (e.GuildId.Value, e.ChannelId),
MessageDeleteEvent e => (e.GuildId, e.ChannelId),
MessageDeleteBulkEvent e => (e.GuildId, e.ChannelId),
MessageReactionAddEvent e => (e.GuildId, e.ChannelId),
MessageReactionRemoveEvent e => (e.GuildId, e.ChannelId),
MessageReactionRemoveAllEvent e => (e.GuildId, e.ChannelId),
MessageReactionRemoveEmojiEvent e => (e.GuildId, e.ChannelId),
InteractionCreateEvent e => (e.GuildId, e.ChannelId),
_ => (null, null)
};
private ulong? GetUserId(IGatewayEvent evt) => evt switch
{
MessageCreateEvent e => e.Author.Id,
MessageUpdateEvent e => e.Author.HasValue ? e.Author.Value.Id : null,
MessageReactionAddEvent e => e.UserId,
MessageReactionRemoveEvent e => e.UserId,
InteractionCreateEvent e => e.Member?.User?.Id // todo: these nullable?
};
private ulong? GetMessageId(IGatewayEvent evt) => evt switch
{
MessageCreateEvent e => e.Id,
MessageUpdateEvent e => e.Id,
MessageDeleteEvent e => e.Id,
MessageReactionAddEvent e => e.MessageId,
MessageReactionRemoveEvent e => e.MessageId,
MessageReactionRemoveAllEvent e => e.MessageId,
MessageReactionRemoveEmojiEvent e => e.MessageId,
InteractionCreateEvent e => e.Message?.Id,
};
private record Inner(List<LogEventProperty> Properties): ILogEventEnricher
{
public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
{
foreach (var prop in Properties)
logEvent.AddPropertyIfAbsent(prop);
}
}
}
}