mirror of
https://gitlab.com/gitlab-org/gitlab-foss.git
synced 2025-08-06 10:19:48 +00:00
188 lines
6.1 KiB
Ruby
188 lines
6.1 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require 'snowplow-tracker'
|
|
|
|
module Gitlab
|
|
module Tracking
|
|
module Destinations
|
|
class Snowplow
|
|
SNOWPLOW_NAMESPACE = 'gl'
|
|
DEDICATED_APP_ID = 'gitlab_dedicated'
|
|
SELF_MANAGED_APP_ID = 'gitlab_sm'
|
|
|
|
delegate :hostname, :uri, :protocol, to: :@destination_configuration
|
|
|
|
attr_reader :destination_configuration
|
|
|
|
def initialize(destination_configuration = DestinationConfiguration.snowplow_configuration)
|
|
@destination_configuration = destination_configuration
|
|
@event_eligibility_checker = Gitlab::Tracking::EventEligibilityChecker.new
|
|
|
|
return if batching_disabled?
|
|
|
|
Kernel.at_exit { tracker.flush(async: false) }
|
|
end
|
|
|
|
def event(category, action, label: nil, property: nil, value: nil, context: nil)
|
|
return unless @event_eligibility_checker.eligible?(action)
|
|
|
|
tracker.track_struct_event(
|
|
category: category,
|
|
action: action,
|
|
label: label,
|
|
property: property,
|
|
value: value,
|
|
context: context,
|
|
tstamp: (Time.now.to_f * 1000).to_i
|
|
)
|
|
increment_total_events_counter
|
|
end
|
|
|
|
def emit_event_payload(payload)
|
|
# Using #input as the tracker doesn't have an option to track using a json object
|
|
# https://snowplow.github.io/snowplow-ruby-tracker/SnowplowTracker/Emitter.html#input-instance_method
|
|
emitter.input(payload)
|
|
end
|
|
|
|
def frontend_client_options(group)
|
|
if Gitlab::CurrentSettings.snowplow_enabled?
|
|
snowplow_options(group)
|
|
else
|
|
product_usage_events_options
|
|
end
|
|
end
|
|
|
|
def app_id
|
|
if Gitlab::CurrentSettings.snowplow_enabled?
|
|
Gitlab::CurrentSettings.snowplow_app_id
|
|
else
|
|
product_usage_event_app_id
|
|
end
|
|
end
|
|
|
|
private
|
|
|
|
def snowplow_options(group)
|
|
additional_features = Feature.enabled?(:additional_snowplow_tracking, group, type: :ops)
|
|
|
|
# Using camel case as these keys will be used only in JavaScript
|
|
{
|
|
namespace: SNOWPLOW_NAMESPACE,
|
|
hostname: hostname,
|
|
cookieDomain: cookie_domain,
|
|
appId: app_id,
|
|
formTracking: additional_features,
|
|
linkClickTracking: additional_features
|
|
}
|
|
end
|
|
|
|
def product_usage_events_options
|
|
# Using camel case as these keys will be used only in JavaScript
|
|
{
|
|
namespace: SNOWPLOW_NAMESPACE,
|
|
hostname: Gitlab.host_with_port,
|
|
postPath: Rails.application.routes.url_helpers.event_forwarding_path,
|
|
forceSecureTracker: Gitlab.config.gitlab.https,
|
|
appId: app_id
|
|
}
|
|
end
|
|
|
|
def product_usage_event_app_id
|
|
app_id = if ::Gitlab::CurrentSettings.gitlab_dedicated_instance?
|
|
DEDICATED_APP_ID
|
|
else
|
|
SELF_MANAGED_APP_ID
|
|
end
|
|
|
|
Gitlab::Tracking::Destinations::DestinationConfiguration.non_production_environment? ? "#{app_id}_staging" : app_id
|
|
end
|
|
|
|
def disable_product_usage_event_logging?
|
|
Gitlab::Utils.to_boolean(ENV['GITLAB_DISABLE_PRODUCT_USAGE_EVENT_LOGGING'], default: false)
|
|
end
|
|
|
|
def cookie_domain
|
|
Gitlab::CurrentSettings.snowplow_cookie_domain
|
|
end
|
|
|
|
def tracker
|
|
@tracker ||= SnowplowTracker::Tracker.new(
|
|
emitters: [emitter],
|
|
subject: SnowplowTracker::Subject.new,
|
|
namespace: SNOWPLOW_NAMESPACE,
|
|
app_id: app_id
|
|
)
|
|
end
|
|
|
|
def emitter
|
|
@emitter ||= emitter_class.new(
|
|
endpoint: hostname,
|
|
options: emitter_options
|
|
)
|
|
end
|
|
|
|
def emitter_class
|
|
# Use test emitter in test environment to prevent HTTP requests
|
|
return SnowplowTestEmitter if Rails.env.test?
|
|
|
|
# snowplow_enabled? is true for gitlab.com and customers that configured their own Snowplow collector
|
|
# In both bases we do not want to log the events being sent as the instance is controlled by the same company
|
|
# controlling the Snowplow collector.
|
|
return SnowplowTracker::AsyncEmitter if Gitlab::CurrentSettings.snowplow_enabled?
|
|
return SnowplowTracker::AsyncEmitter if disable_product_usage_event_logging?
|
|
|
|
SnowplowLoggingEmitter
|
|
end
|
|
|
|
def emitter_options
|
|
{
|
|
protocol: protocol,
|
|
on_success: method(:increment_successful_events_emissions),
|
|
on_failure: method(:failure_callback),
|
|
method: 'post',
|
|
buffer_size: batching_disabled? ? 1 : 10
|
|
}
|
|
end
|
|
|
|
def batching_disabled?
|
|
# disable batching to make local development and testing easier
|
|
Rails.env.development? || Rails.env.test?
|
|
end
|
|
|
|
def failure_callback(success_count, failures)
|
|
increment_successful_events_emissions(success_count)
|
|
increment_failed_events_emissions(failures.size)
|
|
log_failures(failures)
|
|
end
|
|
|
|
def increment_failed_events_emissions(value)
|
|
Gitlab::Metrics.counter(
|
|
:gitlab_snowplow_failed_events_total,
|
|
'Number of failed Snowplow events emissions'
|
|
).increment({}, value.to_i)
|
|
end
|
|
|
|
def increment_successful_events_emissions(value)
|
|
Gitlab::Metrics.counter(
|
|
:gitlab_snowplow_successful_events_total,
|
|
'Number of successful Snowplow events emissions'
|
|
).increment({}, value.to_i)
|
|
end
|
|
|
|
def increment_total_events_counter
|
|
Gitlab::Metrics.counter(
|
|
:gitlab_snowplow_events_total,
|
|
'Number of Snowplow events'
|
|
).increment
|
|
end
|
|
|
|
def log_failures(failures)
|
|
failures.each do |failure|
|
|
Gitlab::AppLogger.error("#{failure['se_ca']} #{failure['se_ac']} failed to be reported to collector at #{hostname}")
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|