a fork of iceshrimp.net but a tweaked frontend to my personal liking. waow
fediverse social-media social iceshrimp fedi
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

[backend/federation] OpenTelemetry metrics for ActivityPub

Kopper 1ac2a1d1 1cec9821

+54 -9
+18
Iceshrimp.Backend/Core/Federation/ActivityPub/ActivityFetcherService.cs
··· 1 + using System.Diagnostics.Metrics; 1 2 using System.Net; 2 3 using System.Net.Http.Headers; 3 4 using Iceshrimp.Backend.Core.Configuration; ··· 6 7 using Iceshrimp.Backend.Core.Extensions; 7 8 using Iceshrimp.Backend.Core.Federation.ActivityStreams; 8 9 using Iceshrimp.Backend.Core.Federation.ActivityStreams.Types; 10 + using Iceshrimp.Backend.Core.Helpers; 9 11 using Iceshrimp.Backend.Core.Middleware; 10 12 using Iceshrimp.Backend.Core.Services; 11 13 using Microsoft.EntityFrameworkCore; ··· 25 27 FederationControlService fedCtrlSvc 26 28 ) : IScopedService 27 29 { 30 + private static readonly Counter<long> FetchCounter = 31 + Telemetry.Meter.CreateCounter<long>("activitypub.fetch.count", "object", "number of objects fetched"); 32 + 28 33 private static readonly IReadOnlyCollection<string> AcceptableActivityTypes = 29 34 [ 30 35 "application/activity+json", "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"" ··· 91 96 { 92 97 throw new TimeoutException(e.Message); 93 98 } 99 + catch 100 + { 101 + FetchCounter.Add(1, new("error", true), new("redirect", false)); 102 + throw; 103 + } 94 104 } 95 105 96 106 private async Task<(ASObject? obj, Uri finalUri)> FetchActivityInternalAsync( ··· 112 122 113 123 if (IsRedirect(response)) 114 124 { 125 + FetchCounter.Add(1, new("error", false), new("redirect", true)); 115 126 var location = response.Headers.Location; 116 127 if (location == null) throw new Exception("Redirection requested but no location header found"); 117 128 if (recurse <= 0) throw new Exception("Redirection requested but recurse counter is at zero"); ··· 125 136 { 126 137 if (response.StatusCode == HttpStatusCode.Gone) 127 138 throw AuthFetchException.NotFound("The remote user no longer exists."); 139 + 140 + FetchCounter.Add(1, new("error", true), new("redirect", false)); 128 141 logger.LogDebug("Failed to fetch activity: response status was {code}", response.StatusCode); 129 142 return (null, finalUri); 130 143 } 131 144 132 145 if (!IsValidActivityContentType(response.Content.Headers.ContentType)) 133 146 { 147 + FetchCounter.Add(1, new("error", true), new("redirect", false)); 134 148 logger.LogDebug("Failed to fetch activity: content type {type} is invalid", 135 149 response.Content.Headers.ContentType); 136 150 return (null, finalUri); ··· 151 165 if (finalUri.Host == config.Value.WebDomain || finalUri.Host == config.Value.WebDomain) 152 166 throw GracefulException.UnprocessableEntity("Refusing to process activity from local domain"); 153 167 168 + FetchCounter.Add(1, new("error", false), new("redirect", false)); 154 169 return (activity, finalUri); 155 170 } 156 171 ··· 178 193 179 194 if (!response.IsSuccessStatusCode) 180 195 { 196 + FetchCounter.Add(1, [new("error", true)]); 181 197 if (response.StatusCode == HttpStatusCode.Gone) 182 198 throw AuthFetchException.NotFound("The remote object no longer exists."); 183 199 logger.LogDebug("Failed to fetch activity: response status was {code}", response.StatusCode); ··· 186 202 187 203 if (!IsValidActivityContentType(response.Content.Headers.ContentType)) 188 204 { 205 + FetchCounter.Add(1, [new("error", true)]); 189 206 logger.LogDebug("Failed to fetch activity: content type {type} is invalid", 190 207 response.Content.Headers.ContentType); 191 208 return null; 192 209 } 193 210 211 + FetchCounter.Add(1, [new("error", false)]); 194 212 return await response.Content.ReadAsStringAsync(); 195 213 } 196 214
+4 -6
Iceshrimp.Backend/Core/Federation/Cryptography/HttpSignature.cs
··· 1 - using System.Diagnostics; 2 1 using System.Net; 3 2 using System.Net.Http.Headers; 4 3 using System.Security.Cryptography; ··· 6 5 using Iceshrimp.Backend.Core.Extensions; 7 6 using Iceshrimp.Backend.Core.Helpers; 8 7 using Iceshrimp.Backend.Core.Middleware; 9 - using Iceshrimp.Shared.Helpers; 10 8 using Microsoft.Extensions.Primitives; 11 9 12 10 namespace Iceshrimp.Backend.Core.Federation.Cryptography; ··· 23 21 if (!requiredHeaders.All(signature.Headers.Contains)) 24 22 throw new GracefulException(HttpStatusCode.Forbidden, "Request is missing required headers"); 25 23 26 - // verify signature with "correct" query string behavior 27 - 28 24 // ReSharper disable once ExplicitCallerInfoArgument 29 25 using var activity = Telemetry.ActivitySource.StartActivity("HTTP Signature Verify"); 30 - activity?.AddTag("key", signature.KeyId); 26 + activity?.AddTag("http_signatures.key_id", signature.KeyId); 31 27 28 + // verify signature with "correct" query string behavior 29 + 32 30 var signingString = 33 31 GenerateSigningString(signature.Headers, request.Method, request.Path, request.QueryString.Value, request.Headers, null, signature); 34 32 ··· 112 110 { 113 111 // ReSharper disable once ExplicitCallerInfoArgument 114 112 using var activity = Telemetry.ActivitySource.StartActivity("HTTP Sign"); 115 - activity?.AddTag("key", keyId); 113 + activity?.AddTag("http_signatures.key_id", keyId); 116 114 117 115 ArgumentNullException.ThrowIfNull(request.RequestUri); 118 116
+1 -3
Iceshrimp.Backend/Core/Federation/Cryptography/LdSignature.cs
··· 1 - using System.Diagnostics; 2 1 using System.Net; 3 2 using System.Security.Cryptography; 4 3 using System.Text; ··· 7 6 using Iceshrimp.Backend.Core.Federation.ActivityStreams.Types; 8 7 using Iceshrimp.Backend.Core.Helpers; 9 8 using Iceshrimp.Backend.Core.Middleware; 10 - using Iceshrimp.Shared.Helpers; 11 9 using Newtonsoft.Json; 12 10 using Newtonsoft.Json.Linq; 13 11 using J = Newtonsoft.Json.JsonPropertyAttribute; ··· 31 29 { 32 30 // ReSharper disable once ExplicitCallerInfoArgument 33 31 using var traceActivity = Telemetry.ActivitySource.StartActivity("LD Signature Verify"); 34 - traceActivity?.AddTag("key", keyId); 32 + traceActivity?.AddTag("ld_signatures.key_id", keyId); 35 33 36 34 var options = activity[$"{Constants.W3IdSecurityNs}#signature"]; 37 35 var rawOptions = rawActivity[$"{Constants.W3IdSecurityNs}#signature"];
+7
Iceshrimp.Backend/Core/Federation/WebFinger/WebFingerService.cs
··· 1 1 using System.Collections.Immutable; 2 + using System.Diagnostics.Metrics; 2 3 using System.Net; 3 4 using System.Text.Encodings.Web; 4 5 using System.Xml; 5 6 using System.Xml.Serialization; 6 7 using Iceshrimp.Backend.Core.Configuration; 7 8 using Iceshrimp.Backend.Core.Extensions; 9 + using Iceshrimp.Backend.Core.Helpers; 8 10 using Iceshrimp.Backend.Core.Middleware; 9 11 using Iceshrimp.Backend.Core.Services; 10 12 using Microsoft.Extensions.Options; ··· 41 43 private static readonly XmlSerializer WebFingerXmlSerializer = new(typeof(WebFingerResponse)); 42 44 private static readonly XmlSerializer HostMetaXmlSerializer = new(typeof(HostMetaResponse)); 43 45 46 + private static readonly Counter<long> QueryCounter = 47 + Telemetry.Meter.CreateCounter<long>("activitypub.webfinger.count", "request", "number of webfinger requests sent"); 48 + 44 49 public async Task<WebFingerResponse?> ResolveAsync(string query) 45 50 { 51 + QueryCounter.Add(1); 52 + 46 53 (query, var domain) = ParseQuery(query); 47 54 if (domain == config.Value.WebDomain || domain == config.Value.AccountDomain) 48 55 throw new GracefulException(HttpStatusCode.BadRequest, "Can't run WebFinger for local user");
+6
Iceshrimp.Backend/Core/Queues/DeliverQueue.cs
··· 1 + using System.Diagnostics.Metrics; 1 2 using System.Net; 2 3 using Iceshrimp.Backend.Core.Database; 3 4 using Iceshrimp.Backend.Core.Database.Tables; 4 5 using Iceshrimp.Backend.Core.Extensions; 6 + using Iceshrimp.Backend.Core.Helpers; 5 7 using Iceshrimp.Backend.Core.Services; 6 8 using Microsoft.EntityFrameworkCore; 7 9 using J = System.Text.Json.Serialization.JsonPropertyNameAttribute; ··· 13 15 : PostgresJobQueue<DeliverJobData>("deliver", DeliverQueueProcessorDelegateAsync, 14 16 parallelism, TimeSpan.FromSeconds(60)) 15 17 { 18 + private static readonly Counter<long> ActivityCounter = 19 + Telemetry.Meter.CreateCounter<long>("activitypub.outbox.total", "activity", "number of activities sent (counts every remote inbox individually)"); 20 + 16 21 private static async Task DeliverQueueProcessorDelegateAsync( 17 22 Job job, DeliverJobData jobData, IServiceProvider scope, CancellationToken token 18 23 ) ··· 68 73 }); 69 74 70 75 response.EnsureSuccessStatusCode(true, () => new ClientError(response.StatusCode)); 76 + ActivityCounter.Add(1); 71 77 } 72 78 catch (Exception e) when (e is not ClientError) 73 79 {
+6
Iceshrimp.Backend/Core/Queues/InboxQueue.cs
··· 1 + using System.Diagnostics.Metrics; 1 2 using System.Net; 2 3 using Iceshrimp.Backend.Core.Database.Tables; 3 4 using Iceshrimp.Backend.Core.Federation.ActivityStreams; 4 5 using Iceshrimp.Backend.Core.Federation.ActivityStreams.Types; 6 + using Iceshrimp.Backend.Core.Helpers; 5 7 using Iceshrimp.Backend.Core.Middleware; 6 8 using Iceshrimp.Backend.Core.Services; 7 9 using Newtonsoft.Json.Linq; ··· 13 15 public class InboxQueue(int parallelism) 14 16 : PostgresJobQueue<InboxJobData>("inbox", InboxQueueProcessorDelegateAsync, parallelism, TimeSpan.FromSeconds(120)) 15 17 { 18 + private static readonly Counter<long> ActivityCounter = 19 + Telemetry.Meter.CreateCounter<long>("activitypub.inbox.count", "activity", "number of activities received"); 20 + 16 21 private static async Task InboxQueueProcessorDelegateAsync( 17 22 Job job, 18 23 InboxJobData jobData, ··· 29 34 var apHandler = scope.GetRequiredService<ActivityPub.ActivityHandlerService>(); 30 35 try 31 36 { 37 + ActivityCounter.Add(1, [new("activitypub.activity.type", activity.Type)]); 32 38 await apHandler.PerformActivityAsync(activity, jobData.InboxUserId, jobData.AuthenticatedUserId); 33 39 } 34 40 catch (InstanceBlockedException e)
+6
Iceshrimp.Backend/Core/Queues/PreDeliverQueue.cs
··· 1 1 using System.Diagnostics.CodeAnalysis; 2 + using System.Diagnostics.Metrics; 2 3 using System.Linq.Expressions; 3 4 using Iceshrimp.Backend.Core.Configuration; 4 5 using Iceshrimp.Backend.Core.Database; 5 6 using Iceshrimp.Backend.Core.Database.Tables; 6 7 using Iceshrimp.Backend.Core.Federation.ActivityStreams; 7 8 using Iceshrimp.Backend.Core.Federation.ActivityStreams.Types; 9 + using Iceshrimp.Backend.Core.Helpers; 8 10 using Iceshrimp.Backend.Core.Services; 9 11 using Microsoft.EntityFrameworkCore; 10 12 using Microsoft.Extensions.Options; ··· 18 20 : PostgresJobQueue<PreDeliverJobData>("pre-deliver", PreDeliverQueueProcessorDelegateAsync, 19 21 parallelism, TimeSpan.FromSeconds(60)) 20 22 { 23 + private static readonly Counter<long> ActivityCounter = 24 + Telemetry.Meter.CreateCounter<long>("activitypub.outbox.count", "activity", "number of unique activities sent"); 25 + 21 26 private static async Task PreDeliverQueueProcessorDelegateAsync( 22 27 Job job, PreDeliverJobData jobData, IServiceProvider scope, CancellationToken token 23 28 ) ··· 79 84 payload = activity.CompactToPayload(); 80 85 } 81 86 87 + ActivityCounter.Add(1, [new("activitypub.activity.type", activity.Type)]); 82 88 foreach (var inboxQueryResult in inboxQueryResults) 83 89 { 84 90 // @formatter:off
+6
Iceshrimp.Backend/Core/Services/InstanceService.cs
··· 1 + using System.Diagnostics.Metrics; 1 2 using AngleSharp.Html.Parser; 2 3 using AsyncKeyedLock; 3 4 using Iceshrimp.Backend.Core.Database; ··· 16 17 MetaService meta 17 18 ) : IScopedService 18 19 { 20 + private static readonly Counter<long> UpdateCounter = 21 + Telemetry.Meter.CreateCounter<long>("activitypub.instance_update.count", "request", "number of instance metadata requests sent"); 22 + 19 23 private static readonly AsyncKeyedLocker<string> KeyedLocker = new(o => 20 24 { 21 25 o.PoolSize = 100; ··· 63 67 { 64 68 using (await KeyedLocker.LockAsync(host)) 65 69 { 70 + UpdateCounter.Add(1); 71 + 66 72 instance.InfoUpdatedAt = DateTime.UtcNow; 67 73 var nodeinfo = await GetNodeInfoAsync(webDomain); 68 74 var icons = await GetIconsAsync(webDomain);