Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
bd59d872
Commit
bd59d872
authored
May 07, 2021
by
Alina Mihaila
Committed by
Alper Akgun
May 07, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add base framework and tooling for Usage Data metrics instrumentation
parent
4c0d2649
Changes
17
Hide whitespace changes
Inline
Side-by-side
Showing
17 changed files
with
326 additions
and
0 deletions
+326
-0
config/metrics/license/20210201124933_uuid.yml
config/metrics/license/20210201124933_uuid.yml
+1
-0
config/metrics/schema.json
config/metrics/schema.json
+4
-0
lib/gitlab/usage/metric_definition.rb
lib/gitlab/usage/metric_definition.rb
+4
-0
lib/gitlab/usage/metrics/instrumentations/base_metric.rb
lib/gitlab/usage/metrics/instrumentations/base_metric.rb
+19
-0
lib/gitlab/usage/metrics/instrumentations/database_metric.rb
lib/gitlab/usage/metrics/instrumentations/database_metric.rb
+68
-0
lib/gitlab/usage/metrics/instrumentations/generic_metric.rb
lib/gitlab/usage/metrics/instrumentations/generic_metric.rb
+32
-0
lib/gitlab/usage/metrics/instrumentations/redis_hll_metric.rb
...gitlab/usage/metrics/instrumentations/redis_hll_metric.rb
+45
-0
lib/gitlab/usage/metrics/instrumentations/uuid_metric.rb
lib/gitlab/usage/metrics/instrumentations/uuid_metric.rb
+15
-0
lib/gitlab/usage/metrics/key_path_processor.rb
lib/gitlab/usage/metrics/key_path_processor.rb
+27
-0
lib/gitlab/usage_data_metrics.rb
lib/gitlab/usage_data_metrics.rb
+28
-0
lib/tasks/gitlab/usage_data.rake
lib/tasks/gitlab/usage_data.rake
+5
-0
rubocop/rubocop-code_reuse.yml
rubocop/rubocop-code_reuse.yml
+1
-0
spec/lib/gitlab/usage/metric_definition_spec.rb
spec/lib/gitlab/usage/metric_definition_spec.rb
+9
-0
spec/lib/gitlab/usage/metrics/instrumentations/uuid_metric_spec.rb
...gitlab/usage/metrics/instrumentations/uuid_metric_spec.rb
+7
-0
spec/lib/gitlab/usage/metrics/key_path_processor_spec.rb
spec/lib/gitlab/usage/metrics/key_path_processor_spec.rb
+23
-0
spec/lib/gitlab/usage_data_metrics_spec.rb
spec/lib/gitlab/usage_data_metrics_spec.rb
+25
-0
spec/support/gitlab/usage/metrics_instrumentation_shared_examples.rb
...t/gitlab/usage/metrics_instrumentation_shared_examples.rb
+13
-0
No files found.
config/metrics/license/20210201124933_uuid.yml
View file @
bd59d872
...
...
@@ -11,6 +11,7 @@ milestone: "9.1"
introduced_by_url
:
https://gitlab.com/gitlab-org/gitlab/-/merge_requests/1521
time_frame
:
none
data_source
:
database
instrumentation_class
:
'
Gitlab::Usage::Metrics::Instrumentations::UuidMetric'
distribution
:
-
ee
-
ce
...
...
config/metrics/schema.json
View file @
bd59d872
...
...
@@ -51,6 +51,10 @@
"type"
:
"string"
,
"enum"
:
[
"database"
,
"redis"
,
"redis_hll"
,
"prometheus"
,
"ruby"
]
},
"instrumentation_class"
:
{
"type"
:
"string"
,
"pattern"
:
"^(Gitlab::Usage::Metrics::Instrumentations::)(([A-Z][a-z]+)+::)*(([A-Z][a-z]+)+)$"
},
"distribution"
:
{
"type"
:
"array"
,
"items"
:
{
...
...
lib/gitlab/usage/metric_definition.rb
View file @
bd59d872
...
...
@@ -65,6 +65,10 @@ module Gitlab
@definitions
||=
load_all!
end
def
all
@all
||=
definitions
.
map
{
|
_key_path
,
definition
|
definition
}
end
def
schemer
@schemer
||=
::
JSONSchemer
.
schema
(
Pathname
.
new
(
METRIC_SCHEMA_PATH
))
end
...
...
lib/gitlab/usage/metrics/instrumentations/base_metric.rb
0 → 100644
View file @
bd59d872
# frozen_string_literal: true
module
Gitlab
module
Usage
module
Metrics
module
Instrumentations
class
BaseMetric
include
Gitlab
::
Utils
::
UsageData
attr_reader
:time_frame
def
initialize
(
time_frame
:)
@time_frame
=
time_frame
end
end
end
end
end
end
lib/gitlab/usage/metrics/instrumentations/database_metric.rb
0 → 100644
View file @
bd59d872
# frozen_string_literal: true
module
Gitlab
module
Usage
module
Metrics
module
Instrumentations
class
DatabaseMetric
<
BaseMetric
# Usage Example
#
# class CountUsersCreatingIssuesMetric < DatabaseMetric
# operation :distinct_count, column: :author_id
#
# relation do |database_time_constraints|
# ::Issue.where(database_time_constraints)
# end
# end
class
<<
self
def
start
(
&
block
)
@metric_start
=
block
end
def
finish
(
&
block
)
@metric_finish
=
block
end
def
relation
(
&
block
)
@metric_relation
=
block
end
def
operation
(
symbol
,
column:
nil
)
@metric_operation
=
symbol
@column
=
column
end
attr_reader
:metric_operation
,
:metric_relation
,
:metric_start
,
:metric_finish
,
:column
end
def
value
method
(
self
.
class
.
metric_operation
)
.
call
(
relation
,
self
.
class
.
column
,
start:
self
.
class
.
metric_start
&
.
call
,
finish:
self
.
class
.
metric_finish
&
.
call
)
end
def
relation
self
.
class
.
metric_relation
.
call
.
where
(
time_constraints
)
end
private
def
time_constraints
case
time_frame
when
'28d'
{
created_at:
30
.
days
.
ago
..
2
.
days
.
ago
}
when
'all'
{}
when
'none'
nil
else
raise
"Unknown time frame:
#{
time_frame
}
for DatabaseMetric"
end
end
end
end
end
end
end
lib/gitlab/usage/metrics/instrumentations/generic_metric.rb
0 → 100644
View file @
bd59d872
# frozen_string_literal: true
module
Gitlab
module
Usage
module
Metrics
module
Instrumentations
class
GenericMetric
<
BaseMetric
# Usage example
#
# class UuidMetric < GenericMetric
# value do
# Gitlab::CurrentSettings.uuid
# end
# end
class
<<
self
def
value
(
&
block
)
@metric_value
=
block
end
attr_reader
:metric_value
end
def
value
alt_usage_data
do
self
.
class
.
metric_value
.
call
end
end
end
end
end
end
end
lib/gitlab/usage/metrics/instrumentations/redis_hll_metric.rb
0 → 100644
View file @
bd59d872
# frozen_string_literal: true
module
Gitlab
module
Usage
module
Metrics
module
Instrumentations
class
RedisHLLMetric
<
BaseMetric
# Usage example
#
# class CountUsersVisitingAnalyticsValuestreamMetric < RedisHLLMetric
# event_names :g_analytics_valuestream
# end
class
<<
self
def
event_names
(
events
=
nil
)
@mentric_events
=
events
end
attr_reader
:metric_events
end
def
value
redis_usage_data
do
event_params
=
time_constraints
.
merge
(
event_names:
self
.
class
.
metric_events
)
Gitlab
::
UsageDataCounters
::
HLLRedisCounter
.
unique_events
(
**
event_params
)
end
end
private
def
time_constraints
case
time_frame
when
'28d'
{
start_date:
4
.
weeks
.
ago
.
to_date
,
end_date:
Date
.
current
}
when
'7d'
{
start_date:
7
.
days
.
ago
.
to_date
,
end_date:
Date
.
current
}
else
raise
"Unknown time frame:
#{
time_frame
}
for TimeConstraint"
end
end
end
end
end
end
end
lib/gitlab/usage/metrics/instrumentations/uuid_metric.rb
0 → 100644
View file @
bd59d872
# frozen_string_literal: true
module
Gitlab
module
Usage
module
Metrics
module
Instrumentations
class
UuidMetric
<
GenericMetric
value
do
Gitlab
::
CurrentSettings
.
uuid
end
end
end
end
end
end
lib/gitlab/usage/metrics/key_path_processor.rb
0 → 100644
View file @
bd59d872
# frozen_string_literal: true
module
Gitlab
module
Usage
module
Metrics
class
KeyPathProcessor
class
<<
self
def
process
(
key_path
,
value
)
unflatten
(
key_path
.
split
(
'.'
),
value
)
end
private
def
unflatten
(
keys
,
value
)
loop
do
value
=
{
keys
.
pop
.
to_sym
=>
value
}
break
if
keys
.
blank?
end
value
end
end
end
end
end
end
lib/gitlab/usage_data_metrics.rb
0 → 100644
View file @
bd59d872
# frozen_string_literal: true
module
Gitlab
class
UsageDataMetrics
class
<<
self
# Build the Usage Ping JSON payload from metrics YAML definitions which have instrumentation class set
def
uncached_data
::
Gitlab
::
Usage
::
MetricDefinition
.
all
.
map
do
|
definition
|
instrumentation_class
=
definition
.
attributes
[
:instrumentation_class
]
if
instrumentation_class
.
present?
metric_value
=
instrumentation_class
.
constantize
.
new
(
time_frame:
definition
.
attributes
[
:time_frame
]).
value
metric_payload
(
definition
.
key_path
,
metric_value
)
else
{}
end
end
.
reduce
({},
:deep_merge
)
end
private
def
metric_payload
(
key_path
,
value
)
::
Gitlab
::
Usage
::
Metrics
::
KeyPathProcessor
.
process
(
key_path
,
value
)
end
end
end
end
lib/tasks/gitlab/usage_data.rake
View file @
bd59d872
...
...
@@ -29,5 +29,10 @@ namespace :gitlab do
items
=
Gitlab
::
Usage
::
MetricDefinition
.
definitions
Gitlab
::
Usage
::
Docs
::
Renderer
.
new
(
items
).
write
end
desc
'GitLab | UsageDataMetrics | Generate usage ping from metrics definition YAML files in JSON'
task
generate_from_yaml: :environment
do
puts
Gitlab
::
Json
.
pretty_generate
(
Gitlab
::
UsageDataMetrics
.
uncached_data
)
end
end
end
rubocop/rubocop-code_reuse.yml
View file @
bd59d872
...
...
@@ -31,6 +31,7 @@ CodeReuse/ActiveRecord:
-
lib/gitlab/import_export/**/*.rb
-
lib/gitlab/project_authorizations.rb
-
lib/gitlab/sql/**/*.rb
-
lib/gitlab/usage/metrics/instrumentations/**/*.rb
-
lib/system_check/**/*.rb
-
qa/**/*.rb
-
rubocop/**/*.rb
...
...
spec/lib/gitlab/usage/metric_definition_spec.rb
View file @
bd59d872
...
...
@@ -25,6 +25,12 @@ RSpec.describe Gitlab::Usage::MetricDefinition do
let
(
:definition
)
{
described_class
.
new
(
path
,
attributes
)
}
let
(
:yaml_content
)
{
attributes
.
deep_stringify_keys
.
to_yaml
}
around
do
|
example
|
described_class
.
instance_variable_set
(
:@definitions
,
nil
)
example
.
run
described_class
.
instance_variable_set
(
:@definitions
,
nil
)
end
def
write_metric
(
metric
,
path
,
content
)
path
=
File
.
join
(
metric
,
path
)
dir
=
File
.
dirname
(
path
)
...
...
@@ -62,6 +68,9 @@ RSpec.describe Gitlab::Usage::MetricDefinition do
:distribution
|
'test'
:tier
|
%w(test ee)
:name
|
'count_<adjective_describing>_boards'
:instrumentation_class
|
'Gitlab::Usage::Metrics::Instrumentations::Metric_Class'
:instrumentation_class
|
'Gitlab::Usage::Metrics::MetricClass'
end
with_them
do
...
...
spec/lib/gitlab/usage/metrics/instrumentations/uuid_metric_spec.rb
0 → 100644
View file @
bd59d872
# frozen_string_literal: true
require
'spec_helper'
RSpec
.
describe
Gitlab
::
Usage
::
Metrics
::
Instrumentations
::
UuidMetric
do
it_behaves_like
'a correct instrumented metric value'
,
{
time_frame:
'none'
},
Gitlab
::
CurrentSettings
.
uuid
end
spec/lib/gitlab/usage/metrics/key_path_processor_spec.rb
0 → 100644
View file @
bd59d872
# frozen_string_literal: true
require
'spec_helper'
RSpec
.
describe
Gitlab
::
Usage
::
Metrics
::
KeyPathProcessor
do
describe
'#unflatten_default_path'
do
using
RSpec
::
Parameterized
::
TableSyntax
where
(
:key_path
,
:value
,
:expected_hash
)
do
'uuid'
|
nil
|
{
uuid:
nil
}
'uuid'
|
'1111'
|
{
uuid:
'1111'
}
'counts.issues'
|
nil
|
{
counts:
{
issues:
nil
}
}
'counts.issues'
|
100
|
{
counts:
{
issues:
100
}
}
'usage_activity_by_stage.verify.ci_builds'
|
100
|
{
usage_activity_by_stage:
{
verify:
{
ci_builds:
100
}
}
}
end
with_them
do
subject
{
described_class
.
process
(
key_path
,
value
)
}
it
{
is_expected
.
to
eq
(
expected_hash
)
}
end
end
end
spec/lib/gitlab/usage_data_metrics_spec.rb
0 → 100644
View file @
bd59d872
# frozen_string_literal: true
require
'spec_helper'
RSpec
.
describe
Gitlab
::
UsageDataMetrics
do
describe
'.uncached_data'
do
subject
{
described_class
.
uncached_data
}
around
do
|
example
|
described_class
.
instance_variable_set
(
:@definitions
,
nil
)
example
.
run
described_class
.
instance_variable_set
(
:@definitions
,
nil
)
end
before
do
allow
(
ActiveRecord
::
Base
.
connection
).
to
receive
(
:transaction_open?
).
and_return
(
false
)
end
context
'whith instrumentation_class'
do
it
'includes top level keys'
do
expect
(
subject
).
to
include
(
:uuid
)
end
end
end
end
spec/support/gitlab/usage/metrics_instrumentation_shared_examples.rb
0 → 100644
View file @
bd59d872
# frozen_string_literal: true
RSpec
.
shared_examples
'a correct instrumented metric value'
do
|
options
,
expected_value
|
let
(
:time_frame
)
{
options
[
:time_frame
]
}
before
do
allow
(
ActiveRecord
::
Base
.
connection
).
to
receive
(
:transaction_open?
).
and_return
(
false
)
end
it
'has correct value'
do
expect
(
described_class
.
new
(
time_frame:
time_frame
).
value
).
to
eq
(
expected_value
)
end
end
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment