diff --git a/Gemfile b/Gemfile
index f8438271a7b00fe5b78f2a88b1113dc7c22fe641..dbf4fed230be13b5c65f49c47bd4e1f781fc4747 100644
--- a/Gemfile
+++ b/Gemfile
@@ -303,6 +303,9 @@ gem 'peek-pg', '~> 1.3.0', group: :postgres
 gem 'peek-rblineprof', '~> 0.2.0'
 gem 'peek-redis', '~> 1.2.0'
 
+# Snowplow events tracking
+gem 'snowplow-tracker', '~> 0.6.1'
+
 # Metrics
 group :metrics do
   gem 'method_source', '~> 0.8', require: false
diff --git a/Gemfile.lock b/Gemfile.lock
index 92400bc11693ba22a623898287de9d3e3c472bcf..5bc30a2b472aecf3952cd506f972cb4905d39d70 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -144,6 +144,7 @@ GEM
     concurrent-ruby-ext (1.1.3)
       concurrent-ruby (= 1.1.3)
     connection_pool (2.2.2)
+    contracts (0.11.0)
     crack (0.4.3)
       safe_yaml (~> 1.0.0)
     crass (1.0.4)
@@ -873,6 +874,8 @@ GEM
       simplecov-html (~> 0.10.0)
     simplecov-html (0.10.0)
     slack-notifier (1.5.1)
+    snowplow-tracker (0.6.1)
+      contracts (~> 0.7, <= 0.11)
     spring (2.0.2)
       activesupport (>= 4.2)
     spring-commands-rspec (1.0.4)
@@ -1191,6 +1194,7 @@ DEPENDENCIES
   simple_po_parser (~> 1.1.2)
   simplecov (~> 0.14.0)
   slack-notifier (~> 1.5.1)
+  snowplow-tracker (~> 0.6.1)
   spring (~> 2.0.0)
   spring-commands-rspec (~> 1.0.4)
   sprockets (~> 3.7.0)
diff --git a/config/dependency_decisions.yml b/config/dependency_decisions.yml
index af76bace577e0868f8ac469e42983f17f5700461..40a80429afa08711303ab26bddc868d36db639bc 100644
--- a/config/dependency_decisions.yml
+++ b/config/dependency_decisions.yml
@@ -599,3 +599,10 @@
     :why: https://github.com/apache/incubator-echarts/blob/master/LICENSE
     :versions: []
     :when: 2018-12-07 20:46:12.421256000 Z
+- - :license
+  - contracts
+  - BSD
+  - :who: Jarka Košanová
+    :why: https://github.com/egonSchiele/contracts.ruby/blob/master/LICENSE
+    :versions: []
+    :when: 2019-04-01 11:29:39.361015000 Z
diff --git a/ee/app/views/layouts/_snowplow.html.haml b/ee/app/views/layouts/_snowplow.html.haml
index 456f3c3826983682bbfd790d47db3b717ce8a047..5f29b8856c1b15bd1a873d9ede21066fce077364 100644
--- a/ee/app/views/layouts/_snowplow.html.haml
+++ b/ee/app/views/layouts/_snowplow.html.haml
@@ -5,7 +5,7 @@
   };p[i].q=p[i].q||[];n=l.createElement(o);g=l.getElementsByTagName(o)[0];n.async=1;
   n.src=w;g.parentNode.insertBefore(n,g)}}(window,document,"script","#{asset_url('snowplow/sp.js')}","snowplow"));
 
-  window.snowplow('newTracker', 'cf', '#{Gitlab::CurrentSettings.snowplow_collector_uri}', {
+  window.snowplow('newTracker', #{Gitlab::SnowplowTracker::NAMESPACE}, '#{Gitlab::CurrentSettings.snowplow_collector_uri}', {
     appId: '#{Gitlab::CurrentSettings.snowplow_site_id}',
     cookieDomain: '#{Gitlab::CurrentSettings.snowplow_cookie_domain}',
     userFingerprint: false,
diff --git a/ee/lib/gitlab/snowplow_tracker.rb b/ee/lib/gitlab/snowplow_tracker.rb
new file mode 100644
index 0000000000000000000000000000000000000000..9db63ddf248a5ddbc412e33709dcacd6d27d119a
--- /dev/null
+++ b/ee/lib/gitlab/snowplow_tracker.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+require 'snowplow-tracker'
+
+module Gitlab
+  module SnowplowTracker
+    NAMESPACE = 'cf'
+
+    class << self
+      def track_event(category, action, label: nil, property: nil, value: nil, context: nil)
+        return unless enabled?
+
+        tracker.track_struct_event(category, action, label, property, value, context, Time.now.to_i)
+      end
+
+      private
+
+      def tracker
+        return unless enabled?
+
+        @tracker ||= ::SnowplowTracker::Tracker.new(emitter, subject, NAMESPACE, Gitlab::CurrentSettings.snowplow_site_id)
+      end
+
+      def subject
+        ::SnowplowTracker::Subject.new
+      end
+
+      def emitter
+        ::SnowplowTracker::Emitter.new(Gitlab::CurrentSettings.snowplow_collector_uri)
+      end
+
+      def enabled?
+        Gitlab::CurrentSettings.snowplow_enabled?
+      end
+    end
+  end
+end
diff --git a/ee/spec/lib/gitlab/snowplow_tracker_spec.rb b/ee/spec/lib/gitlab/snowplow_tracker_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..b298f7aed01f48bcca4592e3e7c04c0d8e041cc2
--- /dev/null
+++ b/ee/spec/lib/gitlab/snowplow_tracker_spec.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+describe Gitlab::SnowplowTracker do
+  let(:timestamp) { Time.utc(2017, 3, 22) }
+
+  around do |example|
+    Timecop.freeze(timestamp) { example.run }
+  end
+
+  subject { described_class.track_event('category', 'action', property: 'what', value: 'doit') }
+
+  context '.track_event' do
+    context 'when Snowplow tracker is disabled' do
+      it 'does not track the event' do
+        expect(SnowplowTracker::Tracker).not_to receive(:new)
+
+        subject
+      end
+    end
+
+    context 'when Snowplow tracker is enabled' do
+      it 'tracks the event' do
+        stub_application_setting(snowplow_enabled: true)
+        stub_application_setting(snowplow_site_id: 'awesome gitlab')
+        stub_application_setting(snowplow_collector_uri: 'url.com')
+        tracker = double
+
+        expect(::SnowplowTracker::Tracker).to receive(:new)
+          .with(
+            an_instance_of(::SnowplowTracker::Emitter),
+            an_instance_of(::SnowplowTracker::Subject),
+            'cf', 'awesome gitlab'
+          ).and_return(tracker)
+        expect(tracker).to receive(:track_struct_event)
+          .with('category', 'action', nil, 'what', 'doit', nil, timestamp.to_i)
+
+        subject
+      end
+    end
+  end
+end