diff --git a/PluralKit.Bot/Commands/MessageEdit.cs b/PluralKit.Bot/Commands/MessageEdit.cs
index c979bc59..782a6f6f 100644
--- a/PluralKit.Bot/Commands/MessageEdit.cs
+++ b/PluralKit.Bot/Commands/MessageEdit.cs
@@ -20,14 +20,16 @@ namespace PluralKit.Bot
         private readonly IClock _clock;
         private readonly DiscordApiClient _rest;
         private readonly WebhookExecutorService _webhookExecutor;
+        private readonly LogChannelService _logChannel;
 
-        public MessageEdit(IDatabase db, ModelRepository repo, IClock clock, DiscordApiClient rest, WebhookExecutorService webhookExecutor)
+        public MessageEdit(IDatabase db, ModelRepository repo, IClock clock, DiscordApiClient rest, WebhookExecutorService webhookExecutor, LogChannelService logChannel)
         {
             _db = db;
             _repo = repo;
             _clock = clock;
             _rest = rest;
             _webhookExecutor = webhookExecutor;
+            _logChannel = logChannel;
         }
 
         public async Task EditMessage(Context ctx)
@@ -41,12 +43,16 @@ namespace PluralKit.Bot
             
             var newContent = ctx.RemainderOrNull();
 
+            var originalMsg = await _rest.GetMessage(msg.Channel, msg.Mid);
+
             try
             {
                 await _webhookExecutor.EditWebhookMessage(msg.Channel, msg.Mid, newContent);
                 
                 if (ctx.BotPermissions.HasFlag(PermissionSet.ManageMessages))
                     await _rest.DeleteMessage(ctx.Channel.Id, ctx.Message.Id);
+
+                await _logChannel.LogEditedMessage(ctx.MessageContext, msg, ctx.Message, originalMsg!, newContent);
             }
             catch (NotFoundException)
             {
diff --git a/PluralKit.Bot/Services/EmbedService.cs b/PluralKit.Bot/Services/EmbedService.cs
index 2e88a81e..6c5111c8 100644
--- a/PluralKit.Bot/Services/EmbedService.cs
+++ b/PluralKit.Bot/Services/EmbedService.cs
@@ -112,6 +112,19 @@ namespace PluralKit.Bot {
                 .Build();
         }
 
+        public Embed CreateEditedMessageEmbed(PKSystem system, PKMember member, ulong messageId, ulong originalMsgId, User sender, string content, string oldContent, Channel channel) {
+            var timestamp = DiscordUtils.SnowflakeToInstant(messageId);
+            var name = member.NameFor(LookupContext.ByNonOwner); 
+            return new EmbedBuilder()
+                .Author(new($"[Edited] #{channel.Name}: {name}", IconUrl: DiscordUtils.WorkaroundForUrlBug(member.AvatarFor(LookupContext.ByNonOwner))))
+                .Thumbnail(new(member.AvatarFor(LookupContext.ByNonOwner)))
+                .Field(new("Old message", oldContent?.NormalizeLineEndSpacing().Truncate(1000)))
+                .Description(content?.NormalizeLineEndSpacing())
+                .Footer(new($"System ID: {system.Hid} | Member ID: {member.Hid} | Sender: {sender.Username}#{sender.Discriminator} ({sender.Id}) | Message ID: {messageId} | Original Message ID: {originalMsgId}"))
+                .Timestamp(timestamp.ToDateTimeOffset().ToString("O"))
+                .Build();
+        }
+
         public async Task<Embed> CreateMemberEmbed(PKSystem system, PKMember member, Guild guild, LookupContext ctx)
         {
 
diff --git a/PluralKit.Bot/Services/LogChannelService.cs b/PluralKit.Bot/Services/LogChannelService.cs
index 6501181f..308d452f 100644
--- a/PluralKit.Bot/Services/LogChannelService.cs
+++ b/PluralKit.Bot/Services/LogChannelService.cs
@@ -1,3 +1,4 @@
+using System.Linq;
 using System.Threading.Tasks;
 
 using Dapper;
@@ -34,24 +35,11 @@ namespace PluralKit.Bot {
 
         public async ValueTask LogMessage(MessageContext ctx, ProxyMatch proxy, Message trigger, ulong hookMessage)
         {
-            if (ctx.SystemId == null || ctx.LogChannel == null || ctx.InLogBlacklist) return;
-            
-            // Find log channel and check if valid
-            var logChannel = await FindLogChannel(trigger.GuildId!.Value, ctx.LogChannel.Value);
-            if (logChannel == null || logChannel.Type != Channel.ChannelType.GuildText) return;
+            var logChannel = await GetAndCheckLogChannel(ctx, trigger);
+            if (logChannel == null) return;
 
             var triggerChannel = _cache.GetChannel(trigger.ChannelId);
             
-            // Check bot permissions
-            var perms = _bot.PermissionsIn(logChannel.Id);
-            if (!perms.HasFlag(PermissionSet.SendMessages | PermissionSet.EmbedLinks))
-            {
-                _logger.Information(
-                    "Does not have permission to proxy log, ignoring (channel: {ChannelId}, guild: {GuildId}, bot permissions: {BotPermissions})", 
-                    ctx.LogChannel.Value, trigger.GuildId!.Value, perms);
-                return;
-            }
-            
             // Send embed!
             await using var conn = await _db.Obtain();
             var embed = _embed.CreateLoggedMessageEmbed(await _repo.GetSystem(conn, ctx.SystemId.Value), 
@@ -61,6 +49,56 @@ namespace PluralKit.Bot {
             await _rest.CreateMessage(logChannel.Id, new() {Content = url, Embed = embed});
         }
 
+        public async ValueTask LogEditedMessage(MessageContext ctx, PKMessage proxy, Message trigger, Message originalMessage, string newContent)
+        {
+            var logChannel = await GetAndCheckLogChannel(ctx, trigger, proxy);
+            if (logChannel == null) return;
+
+            var triggerChannel = _cache.GetChannel(proxy.Channel);
+            
+            // Send embed!
+            await using var conn = await _db.Obtain();
+            var embed = _embed.CreateEditedMessageEmbed(await _repo.GetSystem(conn, ctx.SystemId.Value), 
+                await _repo.GetMember(conn, proxy.Member), originalMessage.Id, trigger.Id, trigger.Author, newContent, originalMessage.Content,
+                triggerChannel);
+            var url = $"https://discord.com/channels/{proxy.Guild.Value}/{proxy.Channel}/{proxy.Mid}";
+            await _rest.CreateMessage(logChannel.Id, new() {Content = url, Embed = embed});
+        }
+
+        private async Task<Channel?> GetAndCheckLogChannel(MessageContext ctx, Message trigger, PKMessage original = null)
+        {
+            var guildId = trigger.GuildId != null ? trigger.GuildId!.Value : original.Guild.Value;
+            var logChannelId = ctx.LogChannel;
+            var isBlacklisted = ctx.InLogBlacklist;
+
+            if (original != null)
+            {
+                // we're editing a message, get log channel info from the database
+                var guild = await _db.Execute(c => _repo.GetGuild(c, original.Guild.Value));
+                logChannelId = guild.LogChannel;
+                isBlacklisted = guild.Blacklist.Any(x => x == logChannelId);
+            }
+
+            if (ctx.SystemId == null || logChannelId == null || isBlacklisted) return null;
+            
+
+            // Find log channel and check if valid
+            var logChannel = await FindLogChannel(guildId, logChannelId.Value);
+            if (logChannel == null || logChannel.Type != Channel.ChannelType.GuildText) return null;
+            
+            // Check bot permissions
+            var perms = _bot.PermissionsIn(logChannel.Id);
+            if (!perms.HasFlag(PermissionSet.SendMessages | PermissionSet.EmbedLinks))
+            {
+                _logger.Information(
+                    "Does not have permission to proxy log, ignoring (channel: {ChannelId}, guild: {GuildId}, bot permissions: {BotPermissions})", 
+                    ctx.LogChannel.Value, trigger.GuildId!.Value, perms);
+                return null;
+            }
+
+            return logChannel;
+        }
+
         private async Task<Channel?> FindLogChannel(ulong guildId, ulong channelId)
         {
             // TODO: fetch it directly on cache miss?