Remove Honeycomb from the app (#2366)

* Remove Honeycomb related gems

* Remove Honeycomb related code from controllers

* Remove Honeycomb from RSS Reader

* Remove Honeycomb related files

* Remove Honeycomb related tests

* Remove leftover Honeycomb methods
This commit is contained in:
Andy Zhao 2019-04-11 10:49:35 -04:00 committed by Ben Halpern
parent f637d9e02b
commit 8ef611f879
14 changed files with 35 additions and 240 deletions

View file

@ -52,7 +52,6 @@ gem "front_matter_parser", "~> 0.2"
gem "gemoji", "~> 3.0.0"
gem "gibbon", "~> 3.2"
gem "google-api-client", "~> 0.27"
gem "honeycomb-beeline", "~> 0.7.0"
gem "html_truncator", "~> 0.4"
gem "httparty", "~> 0.16"
gem "inline_svg", "~> 1.3"

View file

@ -59,8 +59,6 @@ GEM
activemodel (= 5.2.3)
activesupport (= 5.2.3)
arel (>= 9.0)
activerecord-honeycomb (0.4.0)
libhoney
activestorage (5.2.3)
actionpack (= 5.2.3)
activerecord (= 5.2.3)
@ -334,8 +332,6 @@ GEM
i18n (>= 0.7)
faraday (0.15.4)
multipart-post (>= 1.2, < 3)
faraday-honeycomb (0.3.1)
libhoney (>= 1.5.0)
faraday_middleware (0.13.1)
faraday (>= 0.7.4, < 1.0)
fastimage (2.1.1)
@ -550,11 +546,6 @@ GEM
hashie (3.6.0)
heapy (0.1.4)
hkdf (0.3.0)
honeycomb-beeline (0.7.0)
activerecord-honeycomb (>= 0.4.0)
faraday-honeycomb (>= 0.3.0)
libhoney (>= 1.8.1)
rack-honeycomb (>= 0.4.0)
html_tokenizer (0.0.7)
html_truncator (0.4.2)
nokogiri (~> 1.5)
@ -609,9 +600,6 @@ GEM
kaminari-core (1.1.1)
launchy (2.4.3)
addressable (~> 2.3)
libhoney (1.12.1)
addressable (~> 2.0)
http (>= 2.0, < 5.0)
liquid (4.0.3)
listen (3.1.5)
rb-fsevent (~> 0.9, >= 0.9.4)
@ -713,8 +701,6 @@ GEM
pusher-signature (0.1.8)
raabro (1.1.6)
rack (2.0.7)
rack-honeycomb (0.5.0)
libhoney (>= 1.5.0)
rack-host-redirect (1.3.0)
rack
rack-protection (2.0.5)
@ -1044,7 +1030,6 @@ DEPENDENCIES
guard (~> 2.15)
guard-livereload (~> 2.5)
guard-rspec (~> 4.7)
honeycomb-beeline (~> 0.7.0)
html_truncator (~> 0.4)
httparty (~> 0.16)
inline_svg (~> 1.3)

View file

@ -2,7 +2,6 @@ class ApplicationController < ActionController::Base
protect_from_forgery with: :exception, prepend: true
include Pundit
include Instrumentation
def require_http_auth
authenticate_or_request_with_http_basic do |username, password|

View file

@ -3,7 +3,6 @@ class ChatChannelsController < ApplicationController
after_action :verify_authorized
def index
add_param_context(:state)
if params[:state] == "unopened"
authorize ChatChannel
render_unopened_json_response
@ -19,13 +18,11 @@ class ChatChannelsController < ApplicationController
def show
@chat_channel = ChatChannel.find_by(id: params[:id]) || not_found
authorize @chat_channel
add_context(chat_channel_id: @chat_channel.id)
end
def create
authorize ChatChannel
@chat_channel = ChatChannelCreationService.new(current_user, params[:chat_channel]).create
add_context(chat_channel_id: @chat_channel.id)
if @chat_channel.valid?
render json: { status: "success",
chat_channel: @chat_channel.to_json(only: %i[channel_name slug]) },
@ -38,7 +35,6 @@ class ChatChannelsController < ApplicationController
def update
@chat_channel = ChatChannel.find(params[:id])
authorize @chat_channel
add_context(chat_channel_id: @chat_channel.id)
ChatChannelUpdateService.new(@chat_channel, chat_channel_params).update
if @chat_channel.valid?
render json: { status: "success",
@ -52,7 +48,6 @@ class ChatChannelsController < ApplicationController
def open
@chat_channel = ChatChannel.find(params[:id])
authorize @chat_channel
add_context(chat_channel_id: @chat_channel.id)
membership = @chat_channel.chat_channel_memberships.where(user_id: current_user.id).first
membership.update(last_opened_at: 1.second.from_now, has_unopened_messages: false)
@chat_channel.index!
@ -62,7 +57,6 @@ class ChatChannelsController < ApplicationController
def moderate
@chat_channel = ChatChannel.find(params[:id])
authorize @chat_channel
add_context(chat_channel_id: @chat_channel.id)
command = chat_channel_params[:command].split
case command[0]
when "/ban"

View file

@ -56,9 +56,6 @@ class CommentsController < ApplicationController
@comment = Comment.new(permitted_attributes(Comment))
@comment.user_id = current_user.id
add_context(commentable_id: @comment.commentable_id,
commentable_type: @comment.commentable_type)
if @comment.save
current_user.update(checked_code_of_conduct: true) if params[:checked_code_of_conduct].present? && !current_user.checked_code_of_conduct

View file

@ -11,6 +11,5 @@ class EventsController < ApplicationController
def show
@event = Event.find_by(slug: params[:id])
add_context(event_id: @event.id)
end
end

View file

@ -28,7 +28,6 @@ class FollowsController < ApplicationController
else
User.find(params[:followable_id])
end
add_param_context(:followable_type, :followable_id, :verb)
need_notification = Follow.need_new_follower_notification_for?(followable.class.name)
@result = if params[:verb] == "unfollow"
follow = current_user.stop_following(followable)

View file

@ -4,7 +4,6 @@ class ReactionsController < ApplicationController
def index
skip_authorization
add_param_context(:article_id)
if params[:article_id]
id = params[:article_id]
reactions = if efficient_current_user_id.present?
@ -22,7 +21,6 @@ class ReactionsController < ApplicationController
reactions: reactions
}.to_json
else
add_param_context(:commentable_id, :commentable_type)
comments = Comment.where(
commentable_id: params[:commentable_id],
commentable_type: params[:commentable_type],
@ -42,7 +40,6 @@ class ReactionsController < ApplicationController
def create
authorize Reaction
add_param_context(:reactable_id, :reactable_type, :category)
Rails.cache.delete "count_for_reactable-#{params[:reactable_type]}-#{params[:reactable_id]}"
category = params[:category] || "like"
reaction = Reaction.where(

View file

@ -3,7 +3,6 @@ class StoriesController < ApplicationController
before_action :set_cache_control_headers, only: %i[index search show]
def index
add_param_context(:username, :tag)
return handle_user_or_organization_or_podcast_index if params[:username]
return handle_tag_index if params[:tag]
@ -19,7 +18,6 @@ class StoriesController < ApplicationController
def show
@story_show = true
add_param_context(:username, :slug)
if (@article = Article.find_by(path: "/#{params[:username].downcase}/#{params[:slug]}")&.decorate)
handle_article_show
elsif (@article = Article.find_by(slug: params[:slug])&.decorate)
@ -82,7 +80,6 @@ class StoriesController < ApplicationController
@page = (params[:page] || 1).to_i
@tag_model = Tag.find_by(name: @tag) || not_found
@moderators = User.with_role(:tag_moderator, @tag_model)
add_param_context(:tag, :page)
if @tag_model.alias_for.present?
redirect_to "/t/#{@tag_model.alias_for}"
return
@ -106,7 +103,6 @@ class StoriesController < ApplicationController
@page = (params[:page] || 1).to_i
num_articles = 35
@stories = article_finder(num_articles)
add_param_context(:page, :timeframe)
if %w[week month year infinity].include?(params[:timeframe])
@stories = @stories.where("published_at > ?", Timeframer.new(params[:timeframe]).datetime).
order("score DESC")

View file

@ -23,7 +23,7 @@ class UsersController < ApplicationController
authorize @user
# raise permitted_attributes(@user).to_s
if @user.update(permitted_attributes(@user))
RssReader.new(request.request_id).delay.fetch_user(@user) if @user.feed_url.present?
RssReader.new.delay.fetch_user(@user) if @user.feed_url.present?
notice = "Your profile was successfully updated."
if @user.export_requested?
notice += " The export will be emailed to you shortly."

View file

@ -3,10 +3,6 @@ class RssReader
new.get_all_articles(force)
end
def initialize(request_id = nil)
@request_id = request_id
end
def get_all_articles(force = true)
User.where.not(feed_url: [nil, ""]).find_each do |user|
# unless forced, fetch sparingly
@ -17,10 +13,6 @@ class RssReader
end
def fetch_user(user)
# add request_id to thread current variables to aid Honeycomb instrumentation
Thread.current[:request_id] = request_id
Thread.current[:span_id] = request_id
create_articles_for_user(user)
end
@ -35,32 +27,32 @@ class RssReader
attr_reader :request_id
def create_articles_for_user(user)
with_span("create_articles_for_user", user_id: user.id, username: user.username) do |metadata|
user.update_column(:feed_fetched_at, Time.current)
feed = fetch_rss(user.feed_url.strip)
user.update_column(:feed_fetched_at, Time.current)
feed = fetch_rss(user.feed_url.strip)
metadata[:feed_length] = feed.entries.length if feed&.entries
feed.entries.reverse_each do |item|
make_from_rss_item(item, user, feed)
rescue StandardError => e
log_error(
"RssReaderError: occurred while creating article",
feed.entries.reverse_each do |item|
make_from_rss_item(item, user, feed)
rescue StandardError => e
log_error(
"RssReaderError: occurred while creating article",
rss_reader_info: {
user: user.username,
feed_url: user.feed_url,
item_count: get_item_count_error(feed),
error: e,
)
end
rescue StandardError => e
log_error(
"RssReaderError: occurred while fetching feed",
error: e
},
)
end
rescue StandardError => e
log_error(
"RssReaderError: occurred while fetching feed",
rss_reader_info: {
user: user.username,
feed_url: user.feed_url,
item_count: get_item_count_error(feed),
error: e,
)
end
error: e
},
)
end
def get_item_count_error(feed)
@ -72,41 +64,25 @@ class RssReader
end
def fetch_rss(url)
with_span("fetch_rss", url: url) do |metadata|
xml = with_timer("http_get", metadata) do
HTTParty.get(url).body
end
with_timer("parse_xml", metadata) do
Feedjira::Feed.parse xml
end
end
xml = HTTParty.get(url).body
Feedjira::Feed.parse xml
end
def make_from_rss_item(item, user, feed)
with_span(
"make_from_rss_item",
item_id: item.entry_id,
item_title: item.title,
item_summary_size: item.summary&.size,
) do |metadata|
return if medium_reply?(item) || article_exists?(user, item)
return if medium_reply?(item) || article_exists?(user, item)
feed_source_url = item.url.strip.split("?source=")[0]
article = Article.create!(
feed_source_url: feed_source_url,
user_id: user.id,
published_at: item.published,
published_from_feed: true,
show_comments: true,
body_markdown: RssReader::Assembler.call(item, user, feed, feed_source_url),
organization_id: user.organization_id.presence,
)
article = with_timer("save_article", metadata) do
feed_source_url = item.url.strip.split("?source=")[0]
Article.create!(
feed_source_url: feed_source_url,
user_id: user.id,
published_at: item.published,
published_from_feed: true,
show_comments: true,
body_markdown: RssReader::Assembler.call(item, user, feed, feed_source_url),
organization_id: user.organization_id.presence,
)
end
send_slack_notification(article)
end
send_slack_notification(article)
end
def get_host_without_www(url)
@ -147,74 +123,6 @@ class RssReader
end
def log_error(error_msg, metadata)
# giving the error message a resemblance of structured logging
parts = metadata.map { |k, v| [k, v].join("=") }
parts = parts.unshift("#{error_msg}:")
Rails.logger.error(parts.join(" "))
# log error to Honeycomb if initialized
return unless Honeycomb.client
ev = Honeycomb.client.event
ev.add(metadata)
ev.add_field("error_msg", error_msg)
ev.add_field("trace.trace_id", request_id)
ev.add_field("trace.parent_id", Thread.current[:span_id] || request_id)
ev.add_field("trace.span_id", SecureRandom.uuid)
ev.send
end
# NOTE: honeycomb-beeline makes "with_span" theoretically obsolete,
# unfortunately there's a bug that prevents the gem to add fields after a
# a span has been initiated, so for now we'll keep this code in.
# see <https://github.com/honeycombio/beeline-ruby/issues/8>
# This wrapper takes a span name, some optional metadata, and a block; then
# emits a "span" to Honeycomb as part of the trace begun in the RequestTracer
# middleware.
#
# The special sauce in this method is the definition / resetting of thread
# local variables in order to correctly propagate "parent" identifiers down
# into the block.
def with_span(name, metadata = nil)
trace_id = Thread.current[:request_id]
return yield({}) unless trace_id && Honeycomb.client
current_span_id = SecureRandom.uuid
start = Time.current
data = {
name: name,
"trace.span_id": current_span_id,
"trace.trace_id": trace_id,
service_name: "rss_reader"
}
# Capture the calling scope's span ID, then restore it at the end of the
# method.
parent_id = Thread.current[:span_id]
data["trace.parent_id"] = parent_id if parent_id
# Set the current span ID before invoking the provided block, then capture
# the return value to return after emitting the Honeycomb event.
Thread.current[:span_id] = span_id
ret = yield data
data[:duration_ms] = (Time.current - start) * 1000
data.merge!(metadata) if metadata
# log event to Honeycomb
ev = Honeycomb.client.event
ev.timestamp = start
ev.add(data)
ev.send
ret
ensure
Thread.current[:span_id] = parent_id
end
def with_timer(name, data)
start = Time.current
ret = yield
data[name + "_dur_ms"] = (Time.current - start) * 1000 if data
ret
Rails.logger.error(error_msg, metadata)
end
end

View file

@ -1,4 +0,0 @@
# adding an exit hook to properly shutdown/close third party clients
at_exit do
Honeycomb.shutdown if defined?(Honeycomb) && Honeycomb.respond_to?(:shutdown)
end

View file

@ -1,26 +0,0 @@
require "rack/honeycomb/middleware"
module Instrumentation
def self.included(base)
base.before_action :add_user_info_to_honeycomb_event
end
def add_param_context(*keys)
keys.each do |key|
Rack::Honeycomb.add_field(request.env, key, params[key])
end
end
def add_context(metadata)
metadata.each do |key, value|
Rack::Honeycomb.add_field(request.env, key, value)
end
end
def add_user_info_to_honeycomb_event
return unless current_user
Rack::Honeycomb.add_field(request.env, "user.id", current_user.id)
Rack::Honeycomb.add_field(request.env, "user.username", current_user.username)
end
end

View file

@ -6,36 +6,12 @@ vcr_option = {
allow_playback_repeats: "true"
}
class HoneycombEventStub
attr_reader :data
def initialize
@data = {}
end
def add(metadata)
@data.merge!(metadata)
end
def add_field(key, value)
@data[key] = value
end
end
RSpec.describe RssReader, vcr: vcr_option do
let(:link) { "https://medium.com/feed/@vaidehijoshi" }
let(:nonmedium_link) { "https://circleci.com/blog/feed.xml" }
let(:nonpermanent_link) { "https://medium.com/feed/@macsiri/" }
let(:rss_data) { RSS::Parser.parse(HTTParty.get(link).body, false) }
before do
# Honeycomb.init has too many side effects during testing, so we mock the client
event = HoneycombEventStub.new
allow(event).to receive(:send)
client_double = instance_double("Libhoney::TestClient", event: event, events: [event])
allow(Honeycomb).to receive(:client) { client_double }
end
describe "#get_all_articles" do
before do
[link, nonmedium_link, nonpermanent_link].each do |feed_url|
@ -123,18 +99,6 @@ RSpec.describe RssReader, vcr: vcr_option do
expect(Rails.logger).to have_received(:error).at_least(:once)
end
it "logs an article creation error on the observability tool" do
reader = described_class.new
allow(reader).to receive(:make_from_rss_item).and_raise(StandardError)
reader.fetch_user(User.first)
expected_observable_fields = [
:user, :feed_url, :item_count, :error,
"error_msg", "trace.trace_id", "trace.parent_id", "trace.span_id"
]
expect(Honeycomb.client.events.first.data.keys).to eq(expected_observable_fields)
end
it "logs a fetching error on the standard logger" do
reader = described_class.new
allow(reader).to receive(:fetch_rss).and_raise(StandardError)
@ -142,18 +106,6 @@ RSpec.describe RssReader, vcr: vcr_option do
reader.fetch_user(User.first)
expect(Rails.logger).to have_received(:error).at_least(:once)
end
it "logs a fetching error on the observability tool" do
reader = described_class.new
allow(reader).to receive(:fetch_rss).and_raise(StandardError)
reader.fetch_user(User.first)
expected_observable_fields = [
:user, :feed_url, :item_count, :error,
"error_msg", "trace.trace_id", "trace.parent_id", "trace.span_id"
]
expect(Honeycomb.client.events.first.data.keys).to eq(expected_observable_fields)
end
end
describe "#valid_feed_url?" do