From 50e9247da4340a53f19b16e72d3505cb52ba65d4 Mon Sep 17 00:00:00 2001 From: Sean Molenaar Date: Fri, 10 Feb 2023 16:00:51 +0100 Subject: [PATCH 1/2] fix: add better keys and fuller values to influxDB analytics --- Library/Homebrew/cask/installer.rb | 2 +- .../Homebrew/extend/os/mac/utils/analytics.rb | 12 ++---- Library/Homebrew/formula_installer.rb | 2 +- Library/Homebrew/test/utils/analytics_spec.rb | 27 ++++++++---- Library/Homebrew/utils/analytics.rb | 41 +++++++++++++------ 5 files changed, 54 insertions(+), 30 deletions(-) diff --git a/Library/Homebrew/cask/installer.rb b/Library/Homebrew/cask/installer.rb index 30dac0afc6..ceb2c528e5 100644 --- a/Library/Homebrew/cask/installer.rb +++ b/Library/Homebrew/cask/installer.rb @@ -111,7 +111,7 @@ module Cask install_artifacts if @cask.tap&.should_report_analytics? - ::Utils::Analytics.report_event("cask_install", @cask.token, on_request: true) + ::Utils::Analytics.report_event(:cask_install, @cask.token, on_request: true) end purge_backed_up_versioned_files diff --git a/Library/Homebrew/extend/os/mac/utils/analytics.rb b/Library/Homebrew/extend/os/mac/utils/analytics.rb index 6c96066994..f5cf12b1b5 100644 --- a/Library/Homebrew/extend/os/mac/utils/analytics.rb +++ b/Library/Homebrew/extend/os/mac/utils/analytics.rb @@ -8,20 +8,16 @@ module Utils sig { params(verbose: T::Boolean).returns(String) } def custom_prefix_label(verbose: false) - return generic_custom_prefix_label if Hardware::CPU.arm? + return generic_custom_prefix_label(verbose: verbose) if Hardware::CPU.arm? "non-/usr/local" end sig { params(verbose: T::Boolean).returns(String) } def arch_label(verbose: false) - if Hardware::CPU.arm? - "ARM" - elsif Hardware::CPU.in_rosetta2? - "Rosetta" - else - "" - end + return "Rosetta" if Hardware::CPU.in_rosetta2? + + generic_arch_label(verbose: verbose) end end end diff --git a/Library/Homebrew/formula_installer.rb b/Library/Homebrew/formula_installer.rb index 8965f08f7c..6b5bd8a471 100644 --- a/Library/Homebrew/formula_installer.rb +++ b/Library/Homebrew/formula_installer.rb @@ -416,7 +416,7 @@ class FormulaInstaller if formula.tap&.should_report_analytics? action = "#{formula.full_name} #{options}".strip - Utils::Analytics.report_event("install", action, on_request: installed_on_request?) + Utils::Analytics.report_event(:formula_install, action, on_request: installed_on_request?) end self.class.attempted << formula diff --git a/Library/Homebrew/test/utils/analytics_spec.rb b/Library/Homebrew/test/utils/analytics_spec.rb index 3f1731d3e9..cb4c24179e 100644 --- a/Library/Homebrew/test/utils/analytics_spec.rb +++ b/Library/Homebrew/test/utils/analytics_spec.rb @@ -17,8 +17,10 @@ describe Utils::Analytics do allow(Hardware::CPU).to receive(:type).and_return(:intel) allow(Hardware::CPU).to receive(:in_rosetta2?).and_return(false) allow(Homebrew).to receive(:default_prefix?).and_return(false) + expect(described_class.os_arch_prefix_ci).to have_key(:google_prefix) + expect(described_class.os_arch_prefix_ci[:google_prefix]).to eq described_class.custom_prefix_label expect(described_class.os_arch_prefix_ci).to have_key(:prefix) - expect(described_class.os_arch_prefix_ci[:prefix]).to eq described_class.custom_prefix_label + expect(described_class.os_arch_prefix_ci[:prefix]).to eq HOMEBREW_PREFIX.to_s end it "returns OS_VERSION, ARM and prefix when HOMEBREW_PREFIX is a custom prefix on arm" do @@ -27,27 +29,38 @@ describe Utils::Analytics do allow(Homebrew).to receive(:default_prefix?).and_return(false) expect(described_class.os_arch_prefix_ci).to have_key(:arch) expect(described_class.os_arch_prefix_ci[:arch]).to eq described_class.arch_label + expect(described_class.os_arch_prefix_ci).to have_key(:google_prefix) + expect(described_class.os_arch_prefix_ci[:google_prefix]).to eq described_class.custom_prefix_label expect(described_class.os_arch_prefix_ci).to have_key(:prefix) - expect(described_class.os_arch_prefix_ci[:prefix]).to eq described_class.custom_prefix_label + expect(described_class.os_arch_prefix_ci[:prefix]).to eq HOMEBREW_PREFIX.to_s end it "returns OS_VERSION, Rosetta and prefix when HOMEBREW_PREFIX is a custom prefix on Rosetta", :needs_macos do allow(Hardware::CPU).to receive(:type).and_return(:intel) allow(Hardware::CPU).to receive(:in_rosetta2?).and_return(true) allow(Homebrew).to receive(:default_prefix?).and_return(false) + expect(described_class.os_arch_prefix_ci).to have_key(:google_prefix) + expect(described_class.os_arch_prefix_ci[:google_prefix]).to eq described_class.custom_prefix_label expect(described_class.os_arch_prefix_ci).to have_key(:prefix) - expect(described_class.os_arch_prefix_ci[:prefix]).to eq described_class.custom_prefix_label + expect(described_class.os_arch_prefix_ci[:prefix]).to eq HOMEBREW_PREFIX.to_s end it "does not include prefix when HOMEBREW_PREFIX is the default prefix" do allow(Homebrew).to receive(:default_prefix?).and_return(true) - expect(described_class.os_arch_prefix_ci).not_to have_key(:prefix) + expect(described_class.os_arch_prefix_ci).not_to have_key(:google_prefix) + expect(described_class.os_arch_prefix_ci).to have_key(:prefix) + expect(described_class.os_arch_prefix_ci[:prefix]).to eq :default end it "includes CI when ENV['CI'] is set" do ENV["CI"] = "true" expect(described_class.os_arch_prefix_ci).to have_key(:ci) end + + it "includes developer when ENV['CI'] is set" do + allow(Homebrew::EnvConfig).to receive(:developer?).and_return(true) + expect(described_class.os_arch_prefix_ci).to have_key(:developer) + end end end @@ -61,14 +74,14 @@ describe Utils::Analytics do ENV["HOMEBREW_NO_ANALYTICS"] = "true" expect(described_class).not_to receive(:report_google) expect(described_class).not_to receive(:report_influx) - described_class.report_event("install", action) + described_class.report_event(:install, action) end it "returns nil when HOMEBREW_NO_ANALYTICS_THIS_RUN is true" do ENV["HOMEBREW_NO_ANALYTICS_THIS_RUN"] = "true" expect(described_class).not_to receive(:report_google) expect(described_class).not_to receive(:report_influx) - described_class.report_event("install", action) + described_class.report_event(:install, action) end it "returns nil when HOMEBREW_ANALYTICS_DEBUG is true" do @@ -78,7 +91,7 @@ describe Utils::Analytics do expect(described_class).to receive(:report_google) expect(described_class).to receive(:report_influx) - described_class.report_event("install", action) + described_class.report_event(:install, action) end end diff --git a/Library/Homebrew/utils/analytics.rb b/Library/Homebrew/utils/analytics.rb index 3b0c395cc3..73e49681de 100644 --- a/Library/Homebrew/utils/analytics.rb +++ b/Library/Homebrew/utils/analytics.rb @@ -24,7 +24,7 @@ module Utils def report_google(type, metadata = {}) os = metadata[:el][:os] arch = ", #{metadata[:el][:arch]}" if metadata[:el][:arch].present? - prefix = ", #{metadata[:el][:prefix]}" if metadata[:el][:prefix].present? + prefix = ", #{metadata[:el][:google_prefix]}" if metadata[:el][:google_prefix].present? ci = ", CI" if metadata[:el][:CI] == true metadata[:el] = "#{os}#{arch}#{prefix}#{ci}" @@ -86,7 +86,7 @@ module Utils return unless ENV["HOMEBREW_ANALYTICS_ENABLE_INFLUX"] # Append general information to device information - tags = additional_tags.merge(action: action, on_request: !on_request.nil?) + tags = additional_tags.merge(package_and_options: action, on_request: !on_request.nil?) .compact_blank .map { |k, v| "#{k}=#{v.to_s.sub(" ", "\\ ")}" } # convert to key/value parameters .join(",") @@ -117,15 +117,16 @@ module Utils end end - sig { params(category: T.any(String, Symbol), action: String, on_request: T::Boolean).void } + sig { params(category: Symbol, action: String, on_request: T::Boolean).void } def report_event(category, action, on_request: false) return if not_this_run? return if disabled? google_label = os_arch_prefix_ci(verbose: false) + google_category = (category == :formula_install) ? "install" : category report_google(:event, - ec: category, + ec: google_category, ea: action, el: google_label, ev: nil) @@ -150,7 +151,7 @@ module Utils if (options = exception.options.to_a.map(&:to_s).join(" ").presence) action = "#{action} #{options}".strip end - report_event("BuildError", action) + report_event(:build_error, action) end def messages_displayed? @@ -290,10 +291,21 @@ module Utils def arch_label(verbose: false) if Hardware::CPU.arm? "ARM" + elsif verbose + "x86_64" else "" end end + alias generic_arch_label arch_label + + sig { returns(String) } + def homebrew_version + version = HOMEBREW_VERSION.match(/^([\d.]*)-?/)[1] + return "#{version}-dev" if HOMEBREW_VERSION.include?("-") + + version + end def clear_os_arch_prefix_ci return unless instance_variable_defined?(:@os_arch_prefix_ci) @@ -305,17 +317,20 @@ module Utils def os_arch_prefix_ci(verbose: false) @os_arch_prefix_ci ||= begin data = { - os: OS_VERSION, - developer: Homebrew::EnvConfig.developer?, - version: HOMEBREW_VERSION, - system: HOMEBREW_SYSTEM, - ci: ENV["CI"].present?, - arch: arch_label(verbose: verbose), - prefix: custom_prefix_label(verbose: verbose), + version: homebrew_version, + google_prefix: custom_prefix_label(verbose: verbose), + prefix: HOMEBREW_PREFIX.to_s, + ci: ENV["CI"].present?, + developer: Homebrew::EnvConfig.developer?, + arch: arch_label(verbose: verbose), + os: HOMEBREW_SYSTEM, + os_name_and_version: OS_VERSION, } + unless verbose data.delete(:arch) if data[:arch].blank? - data.delete(:prefix) if Homebrew.default_prefix? + data.delete(:google_prefix) if Homebrew.default_prefix? + data[:prefix] = :default if Homebrew.default_prefix? end data From c5252817c21ed56cf31d4e0a8ed5211bf732210e Mon Sep 17 00:00:00 2001 From: Mike McQuaid Date: Wed, 15 Feb 2023 16:34:50 +0000 Subject: [PATCH 2/2] analytics: refactor InfluxDB/Google handling. --- Library/Homebrew/.rubocop.yml | 2 +- Library/Homebrew/brew.sh | 1 + Library/Homebrew/env_config.rb | 5 + .../Homebrew/extend/os/mac/utils/analytics.rb | 12 +- Library/Homebrew/global.rb | 1 + Library/Homebrew/test/utils/analytics_spec.rb | 132 +++++++++----- Library/Homebrew/utils/analytics.rb | 172 ++++++++++-------- docs/Analytics.md | 8 +- 8 files changed, 202 insertions(+), 131 deletions(-) diff --git a/Library/Homebrew/.rubocop.yml b/Library/Homebrew/.rubocop.yml index 70a3193f3b..d63d82e1e0 100644 --- a/Library/Homebrew/.rubocop.yml +++ b/Library/Homebrew/.rubocop.yml @@ -41,7 +41,7 @@ Metrics/PerceivedComplexity: Metrics/MethodLength: Max: 232 Metrics/ModuleLength: - Max: 475 + Max: 480 Exclude: # TODO: extract more of the bottling logic - "dev-cmd/bottle.rb" diff --git a/Library/Homebrew/brew.sh b/Library/Homebrew/brew.sh index 123fc7fab4..601ced299c 100644 --- a/Library/Homebrew/brew.sh +++ b/Library/Homebrew/brew.sh @@ -651,6 +651,7 @@ export HOMEBREW_GIT export HOMEBREW_GIT_WARNING export HOMEBREW_MINIMUM_GIT_VERSION export HOMEBREW_LINUX_MINIMUM_GLIBC_VERSION +export HOMEBREW_PHYSICAL_PROCESSOR export HOMEBREW_PROCESSOR export HOMEBREW_PRODUCT export HOMEBREW_OS_VERSION diff --git a/Library/Homebrew/env_config.rb b/Library/Homebrew/env_config.rb index 7b33ed4ac3..490352bc32 100644 --- a/Library/Homebrew/env_config.rb +++ b/Library/Homebrew/env_config.rb @@ -297,6 +297,11 @@ module Homebrew description: "If set, do not print any hints about changing Homebrew's behaviour with environment variables.", boolean: true, }, + HOMEBREW_NO_GOOGLE_ANALYTICS: { + description: "If set, do not send analytics to Google Analytics but allow sending to Homebrew's InfluxDB " \ + "analytics server. For more information, see: ", + boolean: true, + }, HOMEBREW_NO_GITHUB_API: { description: "If set, do not use the GitHub API, e.g. for searches or fetching relevant issues " \ "after a failed install.", diff --git a/Library/Homebrew/extend/os/mac/utils/analytics.rb b/Library/Homebrew/extend/os/mac/utils/analytics.rb index f5cf12b1b5..4d104e6dce 100644 --- a/Library/Homebrew/extend/os/mac/utils/analytics.rb +++ b/Library/Homebrew/extend/os/mac/utils/analytics.rb @@ -6,18 +6,18 @@ module Utils class << self extend T::Sig - sig { params(verbose: T::Boolean).returns(String) } - def custom_prefix_label(verbose: false) - return generic_custom_prefix_label(verbose: verbose) if Hardware::CPU.arm? + sig { returns(String) } + def custom_prefix_label_google + return generic_custom_prefix_label_google if Hardware::CPU.arm? "non-/usr/local" end - sig { params(verbose: T::Boolean).returns(String) } - def arch_label(verbose: false) + sig { returns(String) } + def arch_label_google return "Rosetta" if Hardware::CPU.in_rosetta2? - generic_arch_label(verbose: verbose) + generic_arch_label_google end end end diff --git a/Library/Homebrew/global.rb b/Library/Homebrew/global.rb index 011ec9def1..75583b6254 100644 --- a/Library/Homebrew/global.rb +++ b/Library/Homebrew/global.rb @@ -48,6 +48,7 @@ HOMEBREW_WWW = "https://brew.sh" HOMEBREW_DOCS_WWW = "https://docs.brew.sh" HOMEBREW_SYSTEM = ENV.fetch("HOMEBREW_SYSTEM").freeze HOMEBREW_PROCESSOR = ENV.fetch("HOMEBREW_PROCESSOR").freeze +HOMEBREW_PHYSICAL_PROCESSOR = ENV.fetch("HOMEBREW_PHYSICAL_PROCESSOR").freeze HOMEBREW_BREWED_CURL_PATH = Pathname(ENV.fetch("HOMEBREW_BREWED_CURL_PATH")).freeze HOMEBREW_USER_AGENT_CURL = ENV.fetch("HOMEBREW_USER_AGENT_CURL").freeze diff --git a/Library/Homebrew/test/utils/analytics_spec.rb b/Library/Homebrew/test/utils/analytics_spec.rb index cb4c24179e..6fca5195b8 100644 --- a/Library/Homebrew/test/utils/analytics_spec.rb +++ b/Library/Homebrew/test/utils/analytics_spec.rb @@ -5,62 +5,93 @@ require "utils/analytics" require "formula_installer" describe Utils::Analytics do - describe "::os_arch_prefix_ci" do - context "when os_arch_prefix_ci is not set" do - before do - described_class.clear_os_arch_prefix_ci - end + describe "::label_google" do + before do + described_class.clear_additional_tags_cache + end - let(:ci) { ", CI" if ENV["CI"] } + let(:ci) { ", CI" if ENV["CI"] } - it "returns OS_VERSION and prefix when HOMEBREW_PREFIX is a custom prefix on intel" do - allow(Hardware::CPU).to receive(:type).and_return(:intel) - allow(Hardware::CPU).to receive(:in_rosetta2?).and_return(false) - allow(Homebrew).to receive(:default_prefix?).and_return(false) - expect(described_class.os_arch_prefix_ci).to have_key(:google_prefix) - expect(described_class.os_arch_prefix_ci[:google_prefix]).to eq described_class.custom_prefix_label - expect(described_class.os_arch_prefix_ci).to have_key(:prefix) - expect(described_class.os_arch_prefix_ci[:prefix]).to eq HOMEBREW_PREFIX.to_s - end + it "returns OS_VERSION and prefix when HOMEBREW_PREFIX is a custom prefix on intel" do + allow(Hardware::CPU).to receive(:type).and_return(:intel) + allow(Hardware::CPU).to receive(:in_rosetta2?).and_return(false) + allow(Homebrew).to receive(:default_prefix?).and_return(false) + expect(described_class.label_google).to include described_class.custom_prefix_label_google + end - it "returns OS_VERSION, ARM and prefix when HOMEBREW_PREFIX is a custom prefix on arm" do - allow(Hardware::CPU).to receive(:type).and_return(:arm) - allow(Hardware::CPU).to receive(:in_rosetta2?).and_return(false) - allow(Homebrew).to receive(:default_prefix?).and_return(false) - expect(described_class.os_arch_prefix_ci).to have_key(:arch) - expect(described_class.os_arch_prefix_ci[:arch]).to eq described_class.arch_label - expect(described_class.os_arch_prefix_ci).to have_key(:google_prefix) - expect(described_class.os_arch_prefix_ci[:google_prefix]).to eq described_class.custom_prefix_label - expect(described_class.os_arch_prefix_ci).to have_key(:prefix) - expect(described_class.os_arch_prefix_ci[:prefix]).to eq HOMEBREW_PREFIX.to_s - end + it "returns OS_VERSION, ARM and prefix when HOMEBREW_PREFIX is a custom prefix on arm" do + allow(Hardware::CPU).to receive(:type).and_return(:arm) + allow(Hardware::CPU).to receive(:in_rosetta2?).and_return(false) + allow(Homebrew).to receive(:default_prefix?).and_return(false) + expect(described_class.label_google).to include described_class.arch_label_google + expect(described_class.label_google).to include described_class.custom_prefix_label_google + end - it "returns OS_VERSION, Rosetta and prefix when HOMEBREW_PREFIX is a custom prefix on Rosetta", :needs_macos do - allow(Hardware::CPU).to receive(:type).and_return(:intel) - allow(Hardware::CPU).to receive(:in_rosetta2?).and_return(true) - allow(Homebrew).to receive(:default_prefix?).and_return(false) - expect(described_class.os_arch_prefix_ci).to have_key(:google_prefix) - expect(described_class.os_arch_prefix_ci[:google_prefix]).to eq described_class.custom_prefix_label - expect(described_class.os_arch_prefix_ci).to have_key(:prefix) - expect(described_class.os_arch_prefix_ci[:prefix]).to eq HOMEBREW_PREFIX.to_s - end + it "returns OS_VERSION, Rosetta and prefix when HOMEBREW_PREFIX is a custom prefix on Rosetta", :needs_macos do + allow(Hardware::CPU).to receive(:type).and_return(:intel) + allow(Hardware::CPU).to receive(:in_rosetta2?).and_return(true) + allow(Homebrew).to receive(:default_prefix?).and_return(false) + expect(described_class.label_google).to include described_class.custom_prefix_label_google + end - it "does not include prefix when HOMEBREW_PREFIX is the default prefix" do - allow(Homebrew).to receive(:default_prefix?).and_return(true) - expect(described_class.os_arch_prefix_ci).not_to have_key(:google_prefix) - expect(described_class.os_arch_prefix_ci).to have_key(:prefix) - expect(described_class.os_arch_prefix_ci[:prefix]).to eq :default - end + it "does not include prefix when HOMEBREW_PREFIX is the default prefix" do + allow(Homebrew).to receive(:default_prefix?).and_return(true) + expect(described_class.label_google).not_to include HOMEBREW_PREFIX.to_s + end - it "includes CI when ENV['CI'] is set" do - ENV["CI"] = "true" - expect(described_class.os_arch_prefix_ci).to have_key(:ci) - end + it "includes CI when ENV['CI'] is set" do + ENV["CI"] = "1" + expect(described_class.label_google).to include "CI" + end + end - it "includes developer when ENV['CI'] is set" do - allow(Homebrew::EnvConfig).to receive(:developer?).and_return(true) - expect(described_class.os_arch_prefix_ci).to have_key(:developer) - end + describe "::additional_tags_influx" do + before do + described_class.clear_additional_tags_cache + end + + let(:ci) { ", CI" if ENV["CI"] } + + it "returns OS_VERSION and prefix when HOMEBREW_PREFIX is a custom prefix on intel" do + allow(Hardware::CPU).to receive(:type).and_return(:intel) + allow(Hardware::CPU).to receive(:in_rosetta2?).and_return(false) + allow(Homebrew).to receive(:default_prefix?).and_return(false) + expect(described_class.additional_tags_influx).to have_key(:prefix) + expect(described_class.additional_tags_influx[:prefix]).to eq "custom-prefix" + end + + it "returns OS_VERSION, ARM and prefix when HOMEBREW_PREFIX is a custom prefix on arm" do + allow(Hardware::CPU).to receive(:type).and_return(:arm) + allow(Hardware::CPU).to receive(:in_rosetta2?).and_return(false) + allow(Homebrew).to receive(:default_prefix?).and_return(false) + expect(described_class.additional_tags_influx).to have_key(:arch) + expect(described_class.additional_tags_influx[:arch]).to eq HOMEBREW_PHYSICAL_PROCESSOR + expect(described_class.additional_tags_influx).to have_key(:prefix) + expect(described_class.additional_tags_influx[:prefix]).to eq "custom-prefix" + end + + it "returns OS_VERSION, Rosetta and prefix when HOMEBREW_PREFIX is a custom prefix on Rosetta", :needs_macos do + allow(Hardware::CPU).to receive(:type).and_return(:intel) + allow(Hardware::CPU).to receive(:in_rosetta2?).and_return(true) + allow(Homebrew).to receive(:default_prefix?).and_return(false) + expect(described_class.additional_tags_influx).to have_key(:prefix) + expect(described_class.additional_tags_influx[:prefix]).to eq "custom-prefix" + end + + it "does not include prefix when HOMEBREW_PREFIX is the default prefix" do + allow(Homebrew).to receive(:default_prefix?).and_return(true) + expect(described_class.additional_tags_influx).to have_key(:prefix) + expect(described_class.additional_tags_influx[:prefix]).to eq HOMEBREW_PREFIX.to_s + end + + it "includes CI when ENV['CI'] is set" do + ENV["CI"] = "1" + expect(described_class.additional_tags_influx).to have_key(:ci) + end + + it "includes developer when ENV['HOMEBREW_DEVELOPER'] is set" do + ENV["HOMEBREW_DEVELOPER"] = "1" + expect(described_class.additional_tags_influx).to have_key(:developer) end end @@ -98,6 +129,7 @@ describe Utils::Analytics do it "passes to the influxdb and google methods" do ENV.delete("HOMEBREW_NO_ANALYTICS_THIS_RUN") ENV.delete("HOMEBREW_NO_ANALYTICS") + ENV.delete("HOMEBREW_DEVELOPER") ENV["HOMEBREW_ANALYTICS_DEBUG"] = "true" expect(described_class).to receive(:report_google).with(:event, hash_including(ea: action)).once expect(described_class).to receive(:report_influx).with(:install, "formula_name --head", false, @@ -108,6 +140,7 @@ describe Utils::Analytics do it "sends to google twice on request" do ENV.delete("HOMEBREW_NO_ANALYTICS_THIS_RUN") ENV.delete("HOMEBREW_NO_ANALYTICS") + ENV.delete("HOMEBREW_DEVELOPER") ENV["HOMEBREW_ANALYTICS_DEBUG"] = "true" expect(described_class).to receive(:report_google).with(:event, hash_including(ea: action, ec: :install)).once expect(described_class).to receive(:report_google).with(:event, @@ -130,6 +163,7 @@ describe Utils::Analytics do ENV.delete("HOMEBREW_NO_ANALYTICS") ENV["HOMEBREW_ANALYTICS_DEBUG"] = "true" ENV["HOMEBREW_ANALYTICS_ENABLE_INFLUX"] = "true" + ENV["HOMEBREW_DEVELOPER"] = "1" expect(described_class).to receive(:deferred_curl).once described_class.report_influx(:install, action, true, developer: true, CI: true) end diff --git a/Library/Homebrew/utils/analytics.rb b/Library/Homebrew/utils/analytics.rb index 73e49681de..c429f8ecbe 100644 --- a/Library/Homebrew/utils/analytics.rb +++ b/Library/Homebrew/utils/analytics.rb @@ -20,15 +20,8 @@ module Utils INFLUX_TOKEN = "y2JZsgE7glWT9V-S-nElETLp8oyH9PGh9JVa-kCdOdp7mEHIOws4BtdjsIe3HHpCBty7IQHLnmh0prqK2vBj9A==" INFLUX_HOST = "europe-west1-1.gcp.cloud2.influxdata.com" - sig { params(type: T.any(String, Symbol), metadata: T::Hash[Symbol, T.untyped]).void } + sig { params(type: Symbol, metadata: T::Hash[Symbol, T.untyped]).void } def report_google(type, metadata = {}) - os = metadata[:el][:os] - arch = ", #{metadata[:el][:arch]}" if metadata[:el][:arch].present? - prefix = ", #{metadata[:el][:google_prefix]}" if metadata[:el][:google_prefix].present? - ci = ", CI" if metadata[:el][:CI] == true - - metadata[:el] = "#{os}#{arch}#{prefix}#{ci}" - analytics_ids = ENV.fetch("HOMEBREW_ANALYTICS_IDS", "").split(",") analytics_ids.each do |analytics_id| args = [] @@ -79,14 +72,14 @@ module Utils end sig { - params(category: T.any(String, Symbol), action: T.any(String, Symbol), on_request: T::Boolean, + params(measurement: Symbol, package_and_options: String, on_request: T::Boolean, additional_tags: T::Hash[Symbol, T.untyped]).void } - def report_influx(category, action, on_request, additional_tags = {}) + def report_influx(measurement, package_and_options, on_request, additional_tags = {}) return unless ENV["HOMEBREW_ANALYTICS_ENABLE_INFLUX"] # Append general information to device information - tags = additional_tags.merge(package_and_options: action, on_request: !on_request.nil?) + tags = additional_tags.merge(package_and_options: package_and_options, on_request: !on_request.nil?) .compact_blank .map { |k, v| "#{k}=#{v.to_s.sub(" ", "\\ ")}" } # convert to key/value parameters .join(",") @@ -96,7 +89,7 @@ module Utils "--header", "Content-Type: text/plain; charset=utf-8", "--header", "Accept: application/json", "--header", "Authorization: Token #{INFLUX_TOKEN}", - "--data-raw", "#{category},#{tags} count=1i #{Time.now.to_i}" + "--data-raw", "#{measurement},#{tags} count=1i #{Time.now.to_i}" ] url = "https://#{INFLUX_HOST}/api/v2/write?bucket=#{INFLUX_BUCKET}&precision=s" @@ -117,41 +110,76 @@ module Utils end end - sig { params(category: Symbol, action: String, on_request: T::Boolean).void } - def report_event(category, action, on_request: false) - return if not_this_run? - return if disabled? - - google_label = os_arch_prefix_ci(verbose: false) - google_category = (category == :formula_install) ? "install" : category - - report_google(:event, - ec: google_category, - ea: action, - el: google_label, - ev: nil) - - if on_request - report_google(:event, - ec: :install_on_request, - ea: action, - el: google_label, - ev: nil) - end - - influx_additional_data = os_arch_prefix_ci(verbose: true) - report_influx(category, action, on_request, influx_additional_data) + sig { params(measurement: Symbol, package_and_options: String, on_request: T::Boolean).void } + def report_event(measurement, package_and_options, on_request: false) + report_google_event(measurement, package_and_options, on_request: on_request) + report_influx_event(measurement, package_and_options, on_request: on_request) end + sig { params(category: Symbol, action: String, on_request: T::Boolean).void } + def report_google_event(category, action, on_request: false) + return if not_this_run? || disabled? || Homebrew::EnvConfig.no_google_analytics? + + category = "install" if category == :formula_install + + report_google(:event, + ec: category, + ea: action, + el: label_google, + ev: nil) + + return unless on_request + + report_google(:event, + ec: :install_on_request, + ea: action, + el: label_google, + ev: nil) + end + + sig { params(measurement: Symbol, package_and_options: String, on_request: T::Boolean).void } + def report_influx_event(measurement, package_and_options, on_request: false) + return if not_this_run? || disabled? + + report_influx(measurement, package_and_options, on_request, additional_tags_influx) + end + + sig { params(exception: Exception).void } def report_build_error(exception) + report_google_build_error(exception) + report_influx_error(exception) + end + + sig { params(exception: Exception).void } + def report_google_build_error(exception) + return if not_this_run? || disabled? + return unless exception.formula.tap return unless exception.formula.tap.should_report_analytics? - action = exception.formula.full_name - if (options = exception.options.to_a.map(&:to_s).join(" ").presence) - action = "#{action} #{options}".strip + formula_full_name = exception.formula.full_name + package_and_options = if (options = exception.options.to_a.map(&:to_s).join(" ").presence) + "#{formula_full_name} #{options}".strip + else + formula_full_name end - report_event(:build_error, action) + report_event("BuildError", package_and_options) + end + + sig { params(exception: Exception).void } + def report_influx_error(exception) + return if not_this_run? || disabled? + + return unless exception.formula.tap + return unless exception.formula.tap.should_report_analytics? + + formula_full_name = exception.formula.full_name + package_and_options = if (options = exception.options.to_a.map(&:to_s).join(" ").presence) + "#{formula_full_name} #{options}".strip + else + formula_full_name + end + report_event(:build_error, package_and_options) end def messages_displayed? @@ -281,59 +309,55 @@ module Utils nil end - sig { params(verbose: T::Boolean).returns(String) } - def custom_prefix_label(verbose: false) + sig { returns(String) } + def custom_prefix_label_google "custom-prefix" end - alias generic_custom_prefix_label custom_prefix_label + alias generic_custom_prefix_label_google custom_prefix_label_google - sig { params(verbose: T::Boolean).returns(String) } - def arch_label(verbose: false) + sig { returns(String) } + def arch_label_google if Hardware::CPU.arm? "ARM" - elsif verbose - "x86_64" else "" end end - alias generic_arch_label arch_label + alias generic_arch_label_google arch_label_google + + def clear_additional_tags_cache + remove_instance_variable(:@label_google) if instance_variable_defined?(:@label_google) + remove_instance_variable(:@additional_tags_influx) if instance_variable_defined?(:@additional_tags_influx) + end sig { returns(String) } - def homebrew_version - version = HOMEBREW_VERSION.match(/^([\d.]*)-?/)[1] - return "#{version}-dev" if HOMEBREW_VERSION.include?("-") - - version + def label_google + @label_google ||= begin + os = OS_VERSION + arch = ", #{arch_label_google}" if arch_label_google.present? + prefix = ", #{custom_prefix_label_google}" unless Homebrew.default_prefix? + ci = ", CI" if ENV["CI"] + "#{os}#{arch}#{prefix}#{ci}" + end end - def clear_os_arch_prefix_ci - return unless instance_variable_defined?(:@os_arch_prefix_ci) + sig { returns(T::Hash[Symbol, String]) } + def additional_tags_influx + @additional_tags_influx ||= begin + version = HOMEBREW_VERSION.match(/^[\d.]+/)[0] + version = "#{version}-dev" if HOMEBREW_VERSION.include?("-") + prefix = Homebrew.default_prefix? ? HOMEBREW_PREFIX.to_s : "custom-prefix" - remove_instance_variable(:@os_arch_prefix_ci) - end - - sig { params(verbose: T::Boolean).returns(T::Hash[Symbol, String]) } - def os_arch_prefix_ci(verbose: false) - @os_arch_prefix_ci ||= begin - data = { - version: homebrew_version, - google_prefix: custom_prefix_label(verbose: verbose), - prefix: HOMEBREW_PREFIX.to_s, + { + version: version, + prefix: prefix, + default_prefix: Homebrew.default_prefix?, ci: ENV["CI"].present?, developer: Homebrew::EnvConfig.developer?, - arch: arch_label(verbose: verbose), + arch: HOMEBREW_PHYSICAL_PROCESSOR, os: HOMEBREW_SYSTEM, os_name_and_version: OS_VERSION, } - - unless verbose - data.delete(:arch) if data[:arch].blank? - data.delete(:google_prefix) if Homebrew.default_prefix? - data[:prefix] = :default if Homebrew.default_prefix? - end - - data end end diff --git a/docs/Analytics.md b/docs/Analytics.md index 558da53b59..de18d272bd 100644 --- a/docs/Analytics.md +++ b/docs/Analytics.md @@ -1,6 +1,6 @@ # Anonymous Aggregate User Behaviour Analytics -Homebrew gathers anonymous aggregate user behaviour analytics using Google Analytics. You will be notified the first time you run `brew update` or install Homebrew. Analytics are not enabled until after this notice is shown, to ensure that you can [opt out](Analytics.md#opting-out) without ever sending analytics data. +Homebrew gathers anonymous aggregate user behaviour analytics using Google Analytics (until our in-progress migration to our own InfluxDB). You will be notified the first time you run `brew update` or install Homebrew. Analytics are not enabled until after this notice is shown, to ensure that you can [opt out](Analytics.md#opting-out) without ever sending analytics data. ## Why? @@ -61,6 +61,12 @@ Homebrew analytics helps us maintainers and leaving it on is appreciated. Howeve export HOMEBREW_NO_ANALYTICS=1 ``` +If you are fine with analytics being sent to Homebrew's InfluxDB but not to Google Analytics, you can set: + +```sh +export HOMEBREW_NO_GOOGLE_ANALYTICS=1 +``` + Alternatively, this will prevent analytics from ever being sent: ```sh