Skip to content
Snippets Groups Projects
Verified Commit 1d96bdc4 authored by Sarah Yasonik's avatar Sarah Yasonik :thermometer: Committed by GitLab
Browse files

Organize internal events cli text by flow

parent f8c236d2
No related branches found
No related tags found
2 merge requests!171309Draft: Fetch issues / MRs / epics count in the sidebar with GraphQL,!169016Organize internal events cli text by flow
......@@ -10,14 +10,13 @@
require 'delegate'
require_relative './cli/helpers'
require_relative './cli/flows/usage_viewer'
require_relative './cli/flows/metric_definer'
require_relative './cli/flows/event_definer'
require_relative './cli/flows/flow_advisor'
require_relative './cli/flows/metric_definer'
require_relative './cli/flows/usage_viewer'
require_relative './cli/global_state'
require_relative './cli/metric'
require_relative './cli/event'
require_relative './cli/text'
class Cli
include ::InternalEventsCli::Helpers
......@@ -29,8 +28,8 @@ def initialize(cli)
end
def run
cli.say InternalEventsCli::Text::FEEDBACK_NOTICE
cli.say InternalEventsCli::Text::CLI_INSTRUCTIONS
cli.say feedback_notice
cli.say instructions
task = cli.select("What would you like to do?", **select_opts) do |menu|
menu.enum "."
......@@ -54,6 +53,23 @@ def run
InternalEventsCli::Flows::FlowAdvisor.new(cli).run
end
end
def instructions
cli.say <<~TEXT.freeze
#{format_info('INSTRUCTIONS:')}
To start tracking usage of a feature...
1) Define event (using CLI)
2) Trigger event (from code)
3) Define metric (using CLI)
4) View data in Tableau (after merge & deploy)
This CLI will help you create the correct defintion files, then provide code examples for instrumentation and testing.
Learn more: https://docs.gitlab.com/ee/development/internal_analytics/#fundamental-concepts
TEXT
end
end
class GitlabPrompt < SimpleDelegator
......
# frozen_string_literal: true
require_relative '../helpers'
require_relative '../text/event_definer'
# Entrypoint for flow to create an event definition file
module InternalEventsCli
module Flows
class EventDefiner
include Helpers
include Text::EventDefiner
SCHEMA = ::JSONSchemer.schema(Pathname('config/events/schema.json'))
STEPS = [
......@@ -20,23 +22,6 @@ class EventDefiner
'Save files'
].freeze
IDENTIFIER_OPTIONS = {
%w[project namespace user] =>
'Use case: For project-level user actions (ex - issue_assignee_changed) [MOST COMMON]',
%w[namespace user] =>
'Use case: For namespace-level user actions (ex - epic_assigned_to_milestone)',
%w[user] =>
'Use case: For user-only actions (ex - admin_impersonated_user)',
%w[project namespace] =>
'Use case: For project-level events without user interaction (ex - service_desk_request_received)',
%w[namespace] =>
'Use case: For namespace-level events without user interaction (ex - stale_runners_cleaned_up)',
%w[feature_enabled_by_namespace_ids user] =>
'Use case: For user actions attributable to multiple namespaces (ex - Code-Suggestions / Duo Pro)',
%w[] =>
'Use case: For instance-level events without user interaction [LEAST COMMON]'
}.freeze
IDENTIFIER_FORMATTING_BUFFER = "[#{IDENTIFIER_OPTIONS.keys.map { |k| k.join(', ') }.max_by(&:length)}]".length
attr_reader :cli, :event
......@@ -64,18 +49,18 @@ def run
def prompt_for_description
new_page!(1, 7, STEPS)
cli.say Text::EVENT_DESCRIPTION_INTRO
cli.say DESCRIPTION_INTRO
event.description = cli.ask("Describe what the event tracks: #{input_required_text}", **input_opts) do |q|
q.required true
q.modify :trim
q.messages[:required?] = Text::EVENT_DESCRIPTION_HELP
q.messages[:required?] = DESCRIPTION_HELP
end
end
def prompt_for_action
new_page!(2, 7, STEPS)
cli.say Text::EVENT_ACTION_INTRO
cli.say ACTION_INTRO
event.action = cli.ask("Define the event name: #{input_required_text}", **input_opts) do |q|
q.required true
......@@ -84,7 +69,7 @@ def prompt_for_action
q.messages[:valid?] = format_warning(
"Invalid event name. Only lowercase/numbers/underscores allowed. " \
"Ensure %{value} is not an existing event.")
q.messages[:required?] = Text::EVENT_ACTION_HELP
q.messages[:required?] = ACTION_HELP
end
end
......@@ -99,7 +84,7 @@ def prompt_for_context
end
def prompt_for_identifiers
cli.say Text::EVENT_IDENTIFIERS_INTRO % event.action
cli.say IDENTIFIERS_INTRO % event.action
identifiers = prompt_for_array_selection(
'Which identifiers are available when the event occurs?',
......@@ -118,7 +103,7 @@ def format_identifier_choice(choice)
end
def prompt_for_additional_properties
cli.say Text::ADDITIONAL_PROPERTIES_INTRO
cli.say ADDITIONAL_PROPERTIES_INTRO
available_props = [:label, :property, :value, :add_extra_prop]
......@@ -191,7 +176,7 @@ def prompt_for_add_extra_properties
q.required true
q.validate ->(input) { input =~ NAME_REGEX && primary_props.none?(input) }
q.modify :trim
q.messages[:required?] = Text::ADDITIONAL_PROPERTIES_ADD_MORE_HELP
q.messages[:required?] = ADDITIONAL_PROPERTIES_ADD_MORE_HELP
q.messages[:valid?] = format_warning(
"Invalid property name. Only lowercase/numbers/underscores allowed. " \
"Ensure %{value} is not one of `property, label, value`.")
......@@ -280,7 +265,7 @@ def prompt_for_next_steps
when :view_usage
UsageViewer.new(cli, event.file_path, event).run
when :exit
cli.say Text::FEEDBACK_NOTICE
cli.say feedback_notice
end
end
end
......
# frozen_string_literal: true
require_relative '../helpers'
require_relative '../text/flow_advisor'
# Entrypoint for help flow, which directs the user to the
# correct flow or documentation based on their goal
......@@ -8,6 +9,7 @@ module InternalEventsCli
module Flows
class FlowAdvisor
include Helpers
include Text::FlowAdvisor
attr_reader :cli
......@@ -36,7 +38,7 @@ def usage_trackable_with_internal_events?
new_page!
cli.say format_info("Excellent! Let's check that this tool will fit your needs.\n")
cli.say Text::EVENT_TRACKING_EXAMPLES
cli.say EVENT_TRACKING_EXAMPLES
cli.yes?(
'Can usage for the feature be measured with a count of specific user actions or events? ' \
......@@ -49,7 +51,7 @@ def event_already_tracked?
new_page!
cli.say format_info("Super! Let's figure out if the event is already tracked & usable.\n")
cli.say Text::EVENT_EXISTENCE_CHECK_INSTRUCTIONS
cli.say EVENT_EXISTENCE_CHECK_INSTRUCTIONS
cli.yes?('Is the event already tracked?', **yes_no_opts)
end
......@@ -58,8 +60,8 @@ def use_case_error
new_page!
cli.error("Oh no! This probably isn't the tool you need!\n")
cli.say Text::ALTERNATE_RESOURCES_NOTICE
cli.say Text::FEEDBACK_NOTICE
cli.say ALTERNATE_RESOURCES_NOTICE
cli.say feedback_notice
end
def proceed_to_metric_definition
......@@ -84,7 +86,7 @@ def proceed_to_event_definition
def not_ready_error(description)
cli.say "\nNo problem! When you're ready, run the CLI & select '#{description}'\n"
cli.say Text::FEEDBACK_NOTICE
cli.say feedback_notice
end
end
end
......
# frozen_string_literal: true
require_relative '../helpers'
require_relative '../text'
require_relative '../text/metric_definer'
# Entrypoint for flow to create an metric definition file
module InternalEventsCli
module Flows
class MetricDefiner
include Helpers
include Text::MetricDefiner
SCHEMA = ::JSONSchemer.schema(Pathname('config/metrics/schema/base.json'))
STEPS = [
......@@ -23,21 +24,6 @@ class MetricDefiner
'Save files'
].freeze
NAME_REQUIREMENT_REASONS = {
filters: {
text: 'Metrics using filters are too complex for default naming.',
help: Text::METRIC_NAME_FILTER_HELP
},
length: {
text: 'The default filename will be too long.',
help: Text::METRIC_NAME_LENGTH_HELP
},
conflict: {
text: 'The default key path is already in use.',
help: Text::METRIC_NAME_CONFLICT_HELP
}
}.freeze
attr_reader :cli
def initialize(cli, starting_event = nil)
......@@ -123,8 +109,8 @@ def prompt_for_events(type)
**filter_opts(header_size: 7)
)
when :database_metric
cli.error Text::DATABASE_METRIC_NOTICE
cli.say Text::FEEDBACK_NOTICE
cli.error DATABASE_METRIC_NOTICE
cli.say feedback_notice
end
end
......@@ -132,8 +118,8 @@ def prompt_for_metrics
eligible_metrics = get_metric_options(selected_events)
if eligible_metrics.all? { |metric| metric[:disabled] }
cli.error Text::ALL_METRICS_EXIST_NOTICE
cli.say Text::FEEDBACK_NOTICE
cli.error ALL_METRICS_EXIST_NOTICE
cli.say feedback_notice
return
end
......@@ -206,7 +192,7 @@ def prompt_for_descriptions
if idx == 0 || separate_page_per_metric
new_page!(4, 9, STEPS)
cli.say Text::METRIC_DESCRIPTION_INTRO
cli.say DESCRIPTION_INTRO
cli.say selected_event_descriptions.join
end
......@@ -359,7 +345,7 @@ def prompt_for_next_steps(outcomes = [])
when :view_usage
UsageViewer.new(cli, @selected_event_paths.first, selected_events.first).run
when :exit
cli.say Text::FEEDBACK_NOTICE
cli.say feedback_notice
end
end
......@@ -487,7 +473,7 @@ def prompt_for_description(metric, default)
prompt_for_text(" Finish the description: #{description_start}", default, multiline: true) do |q|
q.required true
q.modify :trim
q.messages[:required?] = Text::METRIC_DESCRIPTION_HELP
q.messages[:required?] = DESCRIPTION_HELP
end
end
......@@ -515,7 +501,7 @@ def prompt_for_metric_name(metric, default)
prompt_for_text(' Replace with: ', default, multiline: true) do |q|
q.required true
q.messages[:required?] = name_reason[:help] % help_tokens
q.messages[:valid?] = Text::METRIC_NAME_ERROR % help_tokens
q.messages[:valid?] = NAME_ERROR % help_tokens
q.validate ->(input) do
input.length <= max_length &&
input.match?(NAME_REGEX) &&
......
......@@ -96,7 +96,7 @@ def prompt_for_usage_location(default = '1. ruby/rails')
when :other_event
self.class.new(cli).run
when :exit
cli.say(Text::FEEDBACK_NOTICE)
cli.say(feedback_notice)
end
end
......
......@@ -26,5 +26,18 @@ def new_page!(page = nil, total = nil, steps = [])
cli.say TTY::Cursor.move_to(0, 0)
cli.say "#{progress_bar(page, total, steps)}\n" if page && total
end
def feedback_notice
format_heading <<~TEXT.chomp
Thanks for using the Internal Events CLI!
Please reach out with any feedback!
About Internal Events: https://gitlab.com/gitlab-org/analytics-section/analytics-instrumentation/internal/-/issues/687
About CLI: https://gitlab.com/gitlab-org/gitlab/-/issues/434038
In Slack: #g_analyze_analytics_instrumentation
Let us know that you used the CLI! React with 👍 on the feedback issue or post in Slack!
TEXT
end
end
end
# frozen_string_literal: true
# Blocks of text rendered in CLI
module InternalEventsCli
module Text
extend Helpers
CLI_INSTRUCTIONS = <<~TEXT.freeze
#{format_info('INSTRUCTIONS:')}
To start tracking usage of a feature...
1) Define event (using CLI)
2) Trigger event (from code)
3) Define metric (using CLI)
4) View data in Tableau (after merge & deploy)
This CLI will help you create the correct defintion files, then provide code examples for instrumentation and testing.
Learn more: https://docs.gitlab.com/ee/development/internal_analytics/#fundamental-concepts
TEXT
FEEDBACK_NOTICE = format_heading <<~TEXT.chomp
Thanks for using the Internal Events CLI!
Please reach out with any feedback!
About Internal Events: https://gitlab.com/gitlab-org/analytics-section/analytics-instrumentation/internal/-/issues/687
About CLI: https://gitlab.com/gitlab-org/gitlab/-/issues/434038
In Slack: #g_analyze_analytics_instrumentation
Let us know that you used the CLI! React with 👍 on the feedback issue or post in Slack!
TEXT
ALTERNATE_RESOURCES_NOTICE = <<~TEXT.freeze
Other resources:
#{format_warning('Tracking GitLab feature usage from database info:')}
https://docs.gitlab.com/ee/development/internal_analytics/metrics/metrics_instrumentation.html#database-metrics
#{format_warning('Migrating existing metrics to use Internal Events:')}
https://docs.gitlab.com/ee/development/internal_analytics/internal_event_instrumentation/migration.html
#{format_warning('Remove an existing metric:')}
https://docs.gitlab.com/ee/development/internal_analytics/metrics/metrics_lifecycle.html
#{format_warning('Finding existing usage data for GitLab features:')}
https://metrics.gitlab.com/ (Customize Table > Snowflake query)
https://10az.online.tableau.com/#/site/gitlab/views/SnowplowEventExplorationLast30Days/SnowplowEventExplorationLast30D
https://10az.online.tableau.com/#/site/gitlab/views/PDServicePingExplorationDashboard/MetricsExploration
#{format_warning('Customer wants usage data for their own GitLab instance:')}
https://docs.gitlab.com/ee/user/analytics/
#{format_warning('Customer wants usage data for their own products:')}
https://docs.gitlab.com/ee/operations/product_analytics/
TEXT
EVENT_TRACKING_EXAMPLES = <<~TEXT
Product usage can be tracked in several ways.
By tracking events: ex) a user changes the assignee on an issue
ex) a user uploads a CI template
ex) a service desk request is received
ex) all stale runners are cleaned up
ex) a user copies code to the clipboard from markdown
ex) a user uploads an issue template OR a user uploads an MR template
From database data: ex) track whether each gitlab instance allows signups
ex) query how many projects are on each gitlab instance
TEXT
EVENT_EXISTENCE_CHECK_INSTRUCTIONS = <<~TEXT.freeze
To determine what to do next, let's figure out if the event is already tracked & usable.
If you're unsure whether an event exists, you can check the existing defintions.
#{format_info('FROM GDK')}: Check `config/events/` or `ee/config/events`
#{format_info('FROM BROWSER')}: Check https://metrics.gitlab.com/snowplow
Find one? Create a new metric for the event.
Otherwise? Create a new event.
If you find a relevant event that does not have the property `internal_events: true`, it can be migrated to
Internal Events. See https://docs.gitlab.com/ee/development/internal_analytics/internal_event_instrumentation/migration.html
TEXT
EVENT_DESCRIPTION_INTRO = <<~TEXT.freeze
#{format_info('EVENT DESCRIPTION')}
Include what the event is supposed to track, where, and when.
The description field helps others find & reuse this event. This will be used by Engineering, Product, Data team, Support -- and also GitLab customers directly. Be specific and explicit.
ex - Debian package published to the registry using a deploy token
ex - Issue confidentiality was changed
TEXT
EVENT_DESCRIPTION_HELP = <<~TEXT.freeze
#{format_warning('Required. 10+ words likely, but length may vary.')}
#{format_info('GOOD EXAMPLES:')}
- Pipeline is created with a CI Template file included in its configuration
- Quick action `/assign @user1` used to assign a single individual to an issuable
- Quick action `/target_branch` used on a Merge Request
- Quick actions `/unlabel` or `/remove_label` used to remove one or more specific labels
- User edits file using the single file editor
- User edits file using the Web IDE
- User removed issue link between issue and incident
- Debian package published to the registry using a deploy token
#{format_info('GUT CHECK:')}
For your description...
1. Would two different engineers likely instrument the event from the same code locations?
2. Would a new GitLab user find where the event is triggered in the product?
3. Would a GitLab customer understand what the description says?
TEXT
EVENT_ACTION_INTRO = <<~TEXT.freeze
#{format_info('EVENT NAME')}
The event name is a unique identifier used from both a) app code and b) metric definitions.
The name should concisely communicate the same information as the event description.
ex - change_time_estimate_on_issue
ex - push_package_to_repository
ex - publish_go_module_to_the_registry_from_pipeline
ex - admin_user_comments_on_issue_while_impersonating_blocked_user
#{format_info('EXPECTED FORMAT:')} #{format_selection('<action>_<target_of_action>_<where/when>')}
ex) click_save_button_in_issue_description_within_15s_of_page_load
- ACTION: click
- TARGET: save button
- WHERE: in issue description
- WHEN: within 15s of page load
TEXT
EVENT_ACTION_HELP = <<~TEXT.freeze
#{format_warning('Required. Must be globally unique. Must use only letters/numbers/underscores.')}
#{format_info('FAQs:')}
- Q: Present tense or past tense?
A: Prefer present tense! But it's up to you.
- Q: Other event names have prefixes like `i_` or the `g_group_name`. Why?
A: Those are leftovers from legacy naming schemes. Changing the names of old events/metrics can break dashboards, so stability is better than uniformity.
TEXT
EVENT_IDENTIFIERS_INTRO = <<~TEXT.freeze
#{format_info('KEY IDENTIFIERS')}
Indicates the attributes recorded when the event occurs. Generally, we want to include every identifier available to us when the event is triggered.
#{format_info('BACKEND')}: Attributes must be specified when the event is triggered
ex) User, project, and namespace are the identifiers available for backend instrumentation:
track_internal_event(
'%s',
user: user,
project: project,
namespace: project.namespace
)
#{format_info('FRONTEND')}: Attributes are automatically included from the URL
ex) When a user takes an action on the MR list page, the URL is https://gitlab.com/gitlab-org/gitlab/-/merge_requests
Because this URL is for a project, we know that all of user/project/namespace are available for the event
#{format_info('NOTE')}: If you're planning to instrument a unique-by-user metric, you should still include project & namespace when possible. This is especially helpful in the data warehouse, where namespace and project can make events relevant for CSM use-cases.
TEXT
ADDITIONAL_PROPERTIES_INTRO = <<~TEXT.freeze
#{format_info('ADDITIONAL PROPERTIES')}
Describe any related attributes or information which should be tracked when the event occurs. This enables extra capabilities:
- Service Ping: define metrics filtered to a specific subset of events (built-in properties only)
- Snowflake: view/sort/group individual events from GitLab.com
BUILT-IN PROPERTIES (recommended)
For the best performance and flexibility, provide event context using:
property (string), label (string), value (numeric)
These attribute names correspond to repurposed fields in Snowflake. They have no special meaning other than data type.
ex) To add a metric like "Monthly count of unique users who changed an MR status to closed" using a 'change_merge_request_status' event, define an additional property like:
Attribute: label (string)
Description: Status of merge request after update (one of opened, merged, closed)
CUSTOM PROPERTIES (as-needed)
When the built-in properties are insufficient, properties of any name can be provided.
This option becomes available after both property and label are defined, or after value is defined.
TEXT
ADDITIONAL_PROPERTIES_ADD_MORE_HELP = <<~TEXT.freeze
#{format_warning('Required. Must be unique within the event context. Must use only letters/numbers/underscores.')}
#{format_info('It should not be named any of the following:')}
- property#{' '}
- label
- value
TEXT
DATABASE_METRIC_NOTICE = <<~TEXT
For right now, this script can only define metrics for internal events.
For more info on instrumenting database-backed metrics, see https://docs.gitlab.com/ee/development/internal_analytics/metrics/metrics_instrumentation.html
TEXT
ALL_METRICS_EXIST_NOTICE = <<~TEXT
Looks like the potential metrics for this event either already exist or are unsupported.
Check out https://metrics.gitlab.com/ for improved event/metric search capabilities.
TEXT
METRIC_DESCRIPTION_INTRO = <<~TEXT.freeze
#{format_info('METRIC DESCRIPTION')}
Describes which occurrences of an event are tracked in the metric and how they're grouped.
The description field is critical for helping others find & reuse this event. This will be used by Engineering, Product, Data team, Support -- and also GitLab customers directly. Be specific and explicit.
#{format_info('GOOD EXAMPLES:')}
- Total count of analytics dashboard list views
- Weekly count of unique users who viewed the analytics dashboard list
- Monthly count of unique projects where the analytics dashboard list was viewed
- Total count of issue updates
#{format_info('SELECTED EVENT(S):')}
TEXT
METRIC_DESCRIPTION_HELP = <<~TEXT.chomp.freeze
#{format_warning('Required. 10+ words likely, but length may vary.')}
An event description can often be rearranged to work as a metric description.
ex) Event description: A merge request was created
Metric description: Total count of merge requests created
Metric description: Weekly count of unqiue users who created merge requests
Look at the event descriptions above to get ideas!
TEXT
METRIC_NAME_FILTER_HELP = <<~TEXT.freeze
#{format_warning('Required. Max %{count} characters. Only lowercase/numbers/underscores allowed.')}
Metrics with filters must manually define this portion of their key path.
Auto-generated key paths for metrics filters results in long & confusing naming. By defining them manually, clarity and discoverability should be better.
TEXT
METRIC_NAME_CONFLICT_HELP = <<~TEXT.freeze
#{format_warning('Required. Max %{count} characters. Only lowercase/numbers/underscores allowed.')}
Conflict! A metric with the same name already exists: %{name}
TEXT
METRIC_NAME_LENGTH_HELP = <<~TEXT.freeze
#{format_warning('Required. Max %{count} characters. Only lowercase/numbers/underscores allowed.')}
Filenames cannot exceed 100 characters. The key path (ID) is not restricted, but keeping them aligned is recommended.
If needed, you can modify the key path and filename further after saving.
TEXT
METRIC_NAME_ERROR = <<~TEXT.freeze
#{format_warning('Input is invalid. Max %{count} characters. Only lowercase/numbers/underscores allowed. Ensure this key path (ID) is not already in use.')}
TEXT
end
end
# frozen_string_literal: true
module InternalEventsCli
module Text
module EventDefiner
extend Helpers
DESCRIPTION_INTRO = <<~TEXT.freeze
#{format_info('EVENT DESCRIPTION')}
Include what the event is supposed to track, where, and when.
The description field helps others find & reuse this event. This will be used by Engineering, Product, Data team, Support -- and also GitLab customers directly. Be specific and explicit.
ex - Debian package published to the registry using a deploy token
ex - Issue confidentiality was changed
TEXT
DESCRIPTION_HELP = <<~TEXT.freeze
#{format_warning('Required. 10+ words likely, but length may vary.')}
#{format_info('GOOD EXAMPLES:')}
- Pipeline is created with a CI Template file included in its configuration
- Quick action `/assign @user1` used to assign a single individual to an issuable
- Quick action `/target_branch` used on a Merge Request
- Quick actions `/unlabel` or `/remove_label` used to remove one or more specific labels
- User edits file using the single file editor
- User edits file using the Web IDE
- User removed issue link between issue and incident
- Debian package published to the registry using a deploy token
#{format_info('GUT CHECK:')}
For your description...
1. Would two different engineers likely instrument the event from the same code locations?
2. Would a new GitLab user find where the event is triggered in the product?
3. Would a GitLab customer understand what the description says?
TEXT
ACTION_INTRO = <<~TEXT.freeze
#{format_info('EVENT NAME')}
The event name is a unique identifier used from both a) app code and b) metric definitions.
The name should concisely communicate the same information as the event description.
ex - change_time_estimate_on_issue
ex - push_package_to_repository
ex - publish_go_module_to_the_registry_from_pipeline
ex - admin_user_comments_on_issue_while_impersonating_blocked_user
#{format_info('EXPECTED FORMAT:')} #{format_selection('<action>_<target_of_action>_<where/when>')}
ex) click_save_button_in_issue_description_within_15s_of_page_load
- ACTION: click
- TARGET: save button
- WHERE: in issue description
- WHEN: within 15s of page load
TEXT
ACTION_HELP = <<~TEXT.freeze
#{format_warning('Required. Must be globally unique. Must use only letters/numbers/underscores.')}
#{format_info('FAQs:')}
- Q: Present tense or past tense?
A: Prefer present tense! But it's up to you.
- Q: Other event names have prefixes like `i_` or the `g_group_name`. Why?
A: Those are leftovers from legacy naming schemes. Changing the names of old events/metrics can break dashboards, so stability is better than uniformity.
TEXT
IDENTIFIERS_INTRO = <<~TEXT.freeze
#{format_info('KEY IDENTIFIERS')}
Indicates the attributes recorded when the event occurs. Generally, we want to include every identifier available to us when the event is triggered.
#{format_info('BACKEND')}: Attributes must be specified when the event is triggered
ex) User, project, and namespace are the identifiers available for backend instrumentation:
track_internal_event(
'%s',
user: user,
project: project,
namespace: project.namespace
)
#{format_info('FRONTEND')}: Attributes are automatically included from the URL
ex) When a user takes an action on the MR list page, the URL is https://gitlab.com/gitlab-org/gitlab/-/merge_requests
Because this URL is for a project, we know that all of user/project/namespace are available for the event
#{format_info('NOTE')}: If you're planning to instrument a unique-by-user metric, you should still include project & namespace when possible. This is especially helpful in the data warehouse, where namespace and project can make events relevant for CSM use-cases.
TEXT
IDENTIFIER_OPTIONS = {
%w[project namespace user] =>
'Use case: For project-level user actions (ex - issue_assignee_changed) [MOST COMMON]',
%w[namespace user] =>
'Use case: For namespace-level user actions (ex - epic_assigned_to_milestone)',
%w[user] =>
'Use case: For user-only actions (ex - admin_impersonated_user)',
%w[project namespace] =>
'Use case: For project-level events without user interaction (ex - service_desk_request_received)',
%w[namespace] =>
'Use case: For namespace-level events without user interaction (ex - stale_runners_cleaned_up)',
%w[feature_enabled_by_namespace_ids user] =>
'Use case: For user actions attributable to multiple namespaces (ex - Code-Suggestions / Duo Pro)',
%w[] =>
'Use case: For instance-level events without user interaction [LEAST COMMON]'
}.freeze
ADDITIONAL_PROPERTIES_INTRO = <<~TEXT.freeze
#{format_info('ADDITIONAL PROPERTIES')}
Describe any related attributes or information which should be tracked when the event occurs. This enables extra capabilities:
- Service Ping: define metrics filtered to a specific subset of events (built-in properties only)
- Snowflake: view/sort/group individual events from GitLab.com
BUILT-IN PROPERTIES (recommended)
For the best performance and flexibility, provide event context using:
property (string), label (string), value (numeric)
These attribute names correspond to repurposed fields in Snowflake. They have no special meaning other than data type.
ex) To add a metric like "Monthly count of unique users who changed an MR status to closed" using a 'change_merge_request_status' event, define an additional property like:
Attribute: label (string)
Description: Status of merge request after update (one of opened, merged, closed)
CUSTOM PROPERTIES (as-needed)
When the built-in properties are insufficient, properties of any name can be provided.
This option becomes available after both property and label are defined, or after value is defined.
TEXT
ADDITIONAL_PROPERTIES_ADD_MORE_HELP = <<~TEXT.freeze
#{format_warning('Required. Must be unique within the event context. Must use only letters/numbers/underscores.')}
#{format_info('It should not be named any of the following:')}
- property#{' '}
- label
- value
TEXT
end
end
end
# frozen_string_literal: true
# Blocks of text rendered in CLI
module InternalEventsCli
module Text
module FlowAdvisor
extend Helpers
ALTERNATE_RESOURCES_NOTICE = <<~TEXT.freeze
Other resources:
#{format_warning('Tracking GitLab feature usage from database info:')}
https://docs.gitlab.com/ee/development/internal_analytics/metrics/metrics_instrumentation.html#database-metrics
#{format_warning('Migrating existing metrics to use Internal Events:')}
https://docs.gitlab.com/ee/development/internal_analytics/internal_event_instrumentation/migration.html
#{format_warning('Remove an existing metric:')}
https://docs.gitlab.com/ee/development/internal_analytics/metrics/metrics_lifecycle.html
#{format_warning('Finding existing usage data for GitLab features:')}
https://metrics.gitlab.com/ (Customize Table > Snowflake query)
https://10az.online.tableau.com/#/site/gitlab/views/SnowplowEventExplorationLast30Days/SnowplowEventExplorationLast30D
https://10az.online.tableau.com/#/site/gitlab/views/PDServicePingExplorationDashboard/MetricsExploration
#{format_warning('Customer wants usage data for their own GitLab instance:')}
https://docs.gitlab.com/ee/user/analytics/
#{format_warning('Customer wants usage data for their own products:')}
https://docs.gitlab.com/ee/operations/product_analytics/
TEXT
EVENT_TRACKING_EXAMPLES = <<~TEXT
Product usage can be tracked in several ways.
By tracking events: ex) a user changes the assignee on an issue
ex) a user uploads a CI template
ex) a service desk request is received
ex) all stale runners are cleaned up
ex) a user copies code to the clipboard from markdown
ex) a user uploads an issue template OR a user uploads an MR template
From database data: ex) track whether each gitlab instance allows signups
ex) query how many projects are on each gitlab instance
TEXT
EVENT_EXISTENCE_CHECK_INSTRUCTIONS = <<~TEXT.freeze
To determine what to do next, let's figure out if the event is already tracked & usable.
If you're unsure whether an event exists, you can check the existing defintions.
#{format_info('FROM GDK')}: Check `config/events/` or `ee/config/events`
#{format_info('FROM BROWSER')}: Check https://metrics.gitlab.com/snowplow
Find one? Create a new metric for the event.
Otherwise? Create a new event.
If you find a relevant event that does not have the property `internal_events: true`, it can be migrated to
Internal Events. See https://docs.gitlab.com/ee/development/internal_analytics/internal_event_instrumentation/migration.html
TEXT
end
end
end
# frozen_string_literal: true
# Blocks of text rendered in CLI
module InternalEventsCli
module Text
module MetricDefiner
extend Helpers
DATABASE_METRIC_NOTICE = <<~TEXT
For right now, this script can only define metrics for internal events.
For more info on instrumenting database-backed metrics, see https://docs.gitlab.com/ee/development/internal_analytics/metrics/metrics_instrumentation.html
TEXT
ALL_METRICS_EXIST_NOTICE = <<~TEXT
Looks like the potential metrics for this event either already exist or are unsupported.
Check out https://metrics.gitlab.com/ for improved event/metric search capabilities.
TEXT
DESCRIPTION_INTRO = <<~TEXT.freeze
#{format_info('METRIC DESCRIPTION')}
Describes which occurrences of an event are tracked in the metric and how they're grouped.
The description field is critical for helping others find & reuse this event. This will be used by Engineering, Product, Data team, Support -- and also GitLab customers directly. Be specific and explicit.
#{format_info('GOOD EXAMPLES:')}
- Total count of analytics dashboard list views
- Weekly count of unique users who viewed the analytics dashboard list
- Monthly count of unique projects where the analytics dashboard list was viewed
- Total count of issue updates
#{format_info('SELECTED EVENT(S):')}
TEXT
DESCRIPTION_HELP = <<~TEXT.freeze
#{format_warning('Required. 10+ words likely, but length may vary.')}
An event description can often be rearranged to work as a metric description.
ex) Event description: A merge request was created
Metric description: Total count of merge requests created
Metric description: Weekly count of unqiue users who created merge requests
Look at the event descriptions above to get ideas!
TEXT
NAME_FILTER_HELP = <<~TEXT.freeze
#{format_warning('Required. Max %{count} characters. Only lowercase/numbers/underscores allowed.')}
Metrics with filters must manually define this portion of their key path.
Auto-generated key paths for metrics filters results in long & confusing naming. By defining them manually, clarity and discoverability should be better.
TEXT
NAME_CONFLICT_HELP = <<~TEXT.freeze
#{format_warning('Required. Max %{count} characters. Only lowercase/numbers/underscores allowed.')}
Conflict! A metric with the same name already exists: %{name}
TEXT
NAME_LENGTH_HELP = <<~TEXT.freeze
#{format_warning('Required. Max %{count} characters. Only lowercase/numbers/underscores allowed.')}
Filenames cannot exceed 100 characters. The key path (ID) is not restricted, but keeping them aligned is recommended.
If needed, you can modify the key path and filename further after saving.
TEXT
NAME_REQUIREMENT_REASONS = {
filters: {
text: 'Metrics using filters are too complex for default naming.',
help: NAME_FILTER_HELP
},
length: {
text: 'The default filename will be too long.',
help: NAME_LENGTH_HELP
},
conflict: {
text: 'The default key path is already in use.',
help: NAME_CONFLICT_HELP
}
}.freeze
NAME_ERROR = <<~TEXT.freeze
#{format_warning('Input is invalid. Max %{count} characters. Only lowercase/numbers/underscores allowed. Ensure this key path (ID) is not already in use.')}
TEXT
end
end
end
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment