From 9de3fd60a012c69070a3371efec9c9fd54d9071a Mon Sep 17 00:00:00 2001
From: David Roetzel <david@roetzel.de>
Date: Tue, 29 Oct 2024 11:10:17 +0100
Subject: [PATCH] Add telemetry for status / bio formatting (#32677)

---
 app/helpers/formatting_helper.rb | 18 ++++++-
 app/lib/text_formatter.rb        | 87 ++++++++++++++++++--------------
 2 files changed, 66 insertions(+), 39 deletions(-)

diff --git a/app/helpers/formatting_helper.rb b/app/helpers/formatting_helper.rb
index 2ef7d362d8..9d5a2e2478 100644
--- a/app/helpers/formatting_helper.rb
+++ b/app/helpers/formatting_helper.rb
@@ -27,7 +27,14 @@ module FormattingHelper
   module_function :extract_status_plain_text
 
   def status_content_format(status)
-    html_aware_format(status.text, status.local?, preloaded_accounts: [status.account] + (status.respond_to?(:active_mentions) ? status.active_mentions.map(&:account) : []))
+    MastodonOTELTracer.in_span('HtmlAwareFormatter rendering') do |span|
+      span.add_attributes(
+        'app.formatter.content.type' => 'status',
+        'app.formatter.content.origin' => status.local? ? 'local' : 'remote'
+      )
+
+      html_aware_format(status.text, status.local?, preloaded_accounts: [status.account] + (status.respond_to?(:active_mentions) ? status.active_mentions.map(&:account) : []))
+    end
   end
 
   def rss_status_content_format(status)
@@ -39,7 +46,14 @@ module FormattingHelper
   end
 
   def account_bio_format(account)
-    html_aware_format(account.note, account.local?)
+    MastodonOTELTracer.in_span('HtmlAwareFormatter rendering') do |span|
+      span.add_attributes(
+        'app.formatter.content.type' => 'account_bio',
+        'app.formatter.content.origin' => account.local? ? 'local' : 'remote'
+      )
+
+      html_aware_format(account.note, account.local?)
+    end
   end
 
   def account_field_value_format(field, with_rel_me: true)
diff --git a/app/lib/text_formatter.rb b/app/lib/text_formatter.rb
index 2b3febc219..5e8e73a217 100644
--- a/app/lib/text_formatter.rb
+++ b/app/lib/text_formatter.rb
@@ -33,17 +33,24 @@ class TextFormatter
   def to_s
     return ''.html_safe if text.blank?
 
-    html = rewrite do |entity|
-      if entity[:url]
-        link_to_url(entity)
-      elsif entity[:hashtag]
-        link_to_hashtag(entity)
-      elsif entity[:screen_name]
-        link_to_mention(entity)
+    html = nil
+    MastodonOTELTracer.in_span('TextFormatter#to_s extract_and_rewrite') do
+      html = rewrite do |entity|
+        if entity[:url]
+          link_to_url(entity)
+        elsif entity[:hashtag]
+          link_to_hashtag(entity)
+        elsif entity[:screen_name]
+          link_to_mention(entity)
+        end
       end
     end
 
-    html = simple_format(html, {}, sanitize: false).delete("\n") if multiline?
+    if multiline?
+      MastodonOTELTracer.in_span('TextFormatter#to_s simple_format') do
+        html = simple_format(html, {}, sanitize: false).delete("\n")
+      end
+    end
 
     html.html_safe # rubocop:disable Rails/OutputSafety
   end
@@ -93,48 +100,54 @@ class TextFormatter
   end
 
   def link_to_url(entity)
-    TextFormatter.shortened_link(entity[:url], rel_me: with_rel_me?)
+    MastodonOTELTracer.in_span('TextFormatter#link_to_url') do
+      TextFormatter.shortened_link(entity[:url], rel_me: with_rel_me?)
+    end
   end
 
   def link_to_hashtag(entity)
-    hashtag = entity[:hashtag]
-    url     = tag_url(hashtag)
+    MastodonOTELTracer.in_span('TextFormatter#link_to_hashtag') do
+      hashtag = entity[:hashtag]
+      url     = tag_url(hashtag)
 
-    <<~HTML.squish
-      <a href="#{h(url)}" class="mention hashtag" rel="tag">#<span>#{h(hashtag)}</span></a>
-    HTML
+      <<~HTML.squish
+        <a href="#{h(url)}" class="mention hashtag" rel="tag">#<span>#{h(hashtag)}</span></a>
+      HTML
+    end
   end
 
   def link_to_mention(entity)
-    username, domain = entity[:screen_name].split('@')
-    domain           = nil if local_domain?(domain)
-    account          = nil
+    MastodonOTELTracer.in_span('TextFormatter#link_to_mention') do
+      username, domain = entity[:screen_name].split('@')
+      domain           = nil if local_domain?(domain)
+      account          = nil
 
-    if preloaded_accounts?
-      same_username_hits = 0
+      if preloaded_accounts?
+        same_username_hits = 0
 
-      preloaded_accounts.each do |other_account|
-        same_username = other_account.username.casecmp(username).zero?
-        same_domain   = other_account.domain.nil? ? domain.nil? : other_account.domain.casecmp(domain)&.zero?
+        preloaded_accounts.each do |other_account|
+          same_username = other_account.username.casecmp(username).zero?
+          same_domain   = other_account.domain.nil? ? domain.nil? : other_account.domain.casecmp(domain)&.zero?
 
-        if same_username && !same_domain
-          same_username_hits += 1
-        elsif same_username && same_domain
-          account = other_account
+          if same_username && !same_domain
+            same_username_hits += 1
+          elsif same_username && same_domain
+            account = other_account
+          end
         end
+      else
+        account = entity_cache.mention(username, domain)
       end
-    else
-      account = entity_cache.mention(username, domain)
+
+      return "@#{h(entity[:screen_name])}" if account.nil?
+
+      url = ActivityPub::TagManager.instance.url_for(account)
+      display_username = same_username_hits&.positive? || with_domains? ? account.pretty_acct : account.username
+
+      <<~HTML.squish
+        <span class="h-card" translate="no"><a href="#{h(url)}" class="u-url mention">@<span>#{h(display_username)}</span></a></span>
+      HTML
     end
-
-    return "@#{h(entity[:screen_name])}" if account.nil?
-
-    url = ActivityPub::TagManager.instance.url_for(account)
-    display_username = same_username_hits&.positive? || with_domains? ? account.pretty_acct : account.username
-
-    <<~HTML.squish
-      <span class="h-card" translate="no"><a href="#{h(url)}" class="u-url mention">@<span>#{h(display_username)}</span></a></span>
-    HTML
   end
 
   def entity_cache