Skip to content
This repository was archived by the owner on Jul 19, 2025. It is now read-only.

Commit c092735

Browse files
committed
Emit parser metrics for HTTP parsed languages
Porting similar logic to https://github.com/codeclimate/codeclimate-structure/pull/289 into duplication.
1 parent 88b02cc commit c092735

File tree

8 files changed

+140
-8
lines changed

8 files changed

+140
-8
lines changed

lib/cc/engine/analyzers/analyzer_base.rb

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,9 @@ class Base
2828

2929
MAJOR_SEVERITY_THRESHOLD = 120 * POINTS_PER_MINUTE
3030

31-
def initialize(engine_config:)
31+
def initialize(engine_config:, parse_metrics:)
3232
@engine_config = engine_config
33+
@parse_metrics = parse_metrics
3334
end
3435

3536
def run(file)
@@ -97,7 +98,7 @@ def use_sexp_lines?
9798

9899
private
99100

100-
attr_reader :engine_config
101+
attr_reader :engine_config, :parse_metrics
101102

102103
def base_points
103104
self.class::BASE_POINTS
@@ -131,6 +132,7 @@ def skip?(_path)
131132

132133
def parse(file, request_path)
133134
processed_source = ProcessedSource.new(file, request_path)
135+
parse_metrics.incr(:succeeded)
134136
SexpBuilder.new(processed_source.ast, file).build
135137
rescue => ex
136138
handle_exception(processed_source, ex)
@@ -144,11 +146,14 @@ def handle_exception(processed_source, ex)
144146
CC.logger.warn("Skipping #{processed_source.path} due to #{ex.class}")
145147
CC.logger.warn("Response status: #{ex.response_status}")
146148
CC.logger.debug { "Response:\n#{ex.response_body}" }
149+
parse_metrics.incr(ex.code.to_sym)
147150
when ex.is_a?(CC::Parser::Client::EncodingError)
148151
CC.logger.warn("Skipping #{processed_source.path} due to #{ex.class}: #{ex.message}")
152+
parse_metrics.incr(:encoding_error)
149153
when ex.is_a?(CC::Parser::Client::NestingDepthError)
150154
CC.logger.warn("Skipping #{processed_source.path} due to #{ex.class}")
151155
CC.logger.warn(ex.message)
156+
parse_metrics.incr(:client_nesting_depth_error)
152157
else
153158
CC.logger.error("Error processing file: #{processed_source.path}")
154159
CC.logger.error(ex.message)

lib/cc/engine/duplication.rb

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# frozen_string_literal: true
22

33
require "bundler/setup"
4+
require "cc/engine/parse_metrics"
45
require "cc/engine/analyzers/ruby/main"
56
require "cc/engine/analyzers/java/main"
67
require "cc/engine/analyzers/javascript/main"
@@ -34,9 +35,17 @@ def run
3435

3536
Dir.chdir(directory) do
3637
languages_to_analyze.each do |language|
37-
engine = LANGUAGES[language].new(engine_config: engine_config)
38+
parse_metrics = ParseMetrics.new(
39+
language: language,
40+
io: io,
41+
)
42+
engine = LANGUAGES[language].new(
43+
engine_config: engine_config,
44+
parse_metrics: parse_metrics,
45+
)
3846
reporter = CC::Engine::Analyzers::Reporter.new(engine_config, engine, io)
3947
reporter.run
48+
parse_metrics.report
4049
end
4150
end
4251
end

lib/cc/engine/parse_metrics.rb

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
module CC
2+
module Engine
3+
class ParseMetrics
4+
def initialize(language:, io:)
5+
@language = language
6+
@io = io
7+
@counts = Hash.new(0)
8+
end
9+
10+
def incr(result_type)
11+
counts[result_type] += 1
12+
end
13+
14+
def report
15+
counts.each do |result_type, count|
16+
doc = metric_doc(result_type, count)
17+
# puts allows a race between content newline, use print
18+
io.print("#{JSON.generate(doc)}\0\n")
19+
end
20+
end
21+
22+
private
23+
24+
attr_reader :counts, :io, :language
25+
26+
def metric_doc(result_type, count)
27+
{
28+
name: "#{language}.parse.#{result_type}",
29+
type: "measurement",
30+
value: count,
31+
}
32+
end
33+
end
34+
end
35+
end

spec/cc/engine/analyzers/analyzer_base_spec.rb

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,15 @@ class DummyAnalyzer < Base
1515
include AnalyzerSpecHelpers
1616

1717
let(:engine_config) { EngineConfig.new({}) }
18-
let(:analyzer) { DummyAnalyzer.new(engine_config: engine_config) }
18+
let(:analyzer) do
19+
DummyAnalyzer.new(
20+
engine_config: engine_config,
21+
parse_metrics: CC::Engine::ParseMetrics.new(
22+
language: "dummy",
23+
io: StringIO.new,
24+
),
25+
)
26+
end
1927

2028
before(:each) do
2129
create_source_file("foo.a", "")

spec/cc/engine/analyzers/ruby/main_spec.rb

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
require 'spec_helper'
2-
require 'cc/engine/analyzers/ruby/main'
32
require 'cc/engine/analyzers/engine_config'
3+
require 'cc/engine/analyzers/ruby/main'
4+
require 'cc/engine/parse_metrics'
45

56
module CC::Engine::Analyzers
67
RSpec.describe Ruby::Main, in_tmpdir: true do
@@ -228,7 +229,7 @@ def identical
228229
end
229230

230231
describe "#calculate_points" do
231-
let(:analyzer) { Ruby::Main.new(engine_config: engine_conf) }
232+
let(:analyzer) { new_analyzer }
232233
let(:base_points) { Ruby::Main::BASE_POINTS }
233234
let(:points_per) { Ruby::Main::POINTS_PER_OVERAGE }
234235
let(:threshold) { Ruby::Main::DEFAULT_MASS_THRESHOLD }
@@ -274,7 +275,7 @@ def identical
274275
end
275276

276277
describe "#calculate_severity(points)" do
277-
let(:analyzer) { Ruby::Main.new(engine_config: engine_conf) }
278+
let(:analyzer) { new_analyzer }
278279
let(:base_points) { Ruby::Main::BASE_POINTS }
279280
let(:points_per) { Ruby::Main::POINTS_PER_OVERAGE }
280281
let(:threshold) { Ruby::Main::DEFAULT_MASS_THRESHOLD }
@@ -307,6 +308,16 @@ def identical
307308
end
308309
end
309310

311+
def new_analyzer
312+
Ruby::Main.new(
313+
engine_config: engine_conf,
314+
parse_metrics: CC::Engine::ParseMetrics.new(
315+
language: "ruby",
316+
io: StringIO.new,
317+
),
318+
)
319+
end
320+
310321
def engine_conf
311322
EngineConfig.new({})
312323
end

spec/cc/engine/duplication_spec.rb

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
require "cc/engine/duplication"
33

44
RSpec.describe(CC::Engine::Duplication) do
5+
include AnalyzerSpecHelpers
6+
57
describe "#run" do
68
it "skips analysis when all duplication checks are disabled" do
79
dir = "foo"
@@ -24,5 +26,25 @@
2426
directory: dir, engine_config: config, io: double,
2527
).run
2628
end
29+
30+
it "emits parse metrics for HTTP parsed languages", in_tmpdir: true do
31+
create_source_file("foo.js", <<-EOJS)
32+
console.log("hello JS!");
33+
EOJS
34+
35+
stdout = StringIO.new
36+
37+
CC::Engine::Duplication.new(
38+
directory: @code, engine_config: {}, io: stdout,
39+
).run
40+
41+
expect(stdout.string).not_to be_empty
42+
measurement = JSON.parse(stdout.string.strip)
43+
expect(measurement).to eq(
44+
"name" => "javascript.parse.succeeded",
45+
"type" => "measurement",
46+
"value" => 1,
47+
)
48+
end
2749
end
2850
end

spec/cc/engine/parse_metrics_spec.rb

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
require "spec_helper"
2+
3+
require "cc/engine/parse_metrics"
4+
5+
RSpec.describe CC::Engine::ParseMetrics do
6+
it "sends issues to stdout" do
7+
stdout = StringIO.new
8+
metrics = CC::Engine::ParseMetrics.new(
9+
language: "intercal",
10+
io: stdout,
11+
)
12+
13+
metrics.incr(:source_minified)
14+
metrics.incr(:parse_error)
15+
metrics.incr(:source_minified)
16+
17+
metrics.report
18+
19+
out_pieces = stdout.string.split("\0\n").map(&:strip)
20+
expect(out_pieces.count).to eq(2)
21+
22+
expect(JSON.parse(out_pieces[0])).to eq({
23+
"name" => "intercal.parse.source_minified",
24+
"type" => "measurement",
25+
"value" => 2,
26+
})
27+
28+
expect(JSON.parse(out_pieces[1])).to eq({
29+
"name" => "intercal.parse.parse_error",
30+
"type" => "measurement",
31+
"value" => 1,
32+
})
33+
end
34+
end

spec/support/helpers/analyzer_spec_helpers.rb

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
require 'cc/engine/parse_metrics'
2+
13
module AnalyzerSpecHelpers
24
def create_source_file(path, content)
35
raise "Must use in_tmpdir tag" unless @code
@@ -11,7 +13,13 @@ def fixture_path(fixture_name)
1113
def run_engine(config = nil)
1214
io = StringIO.new
1315

14-
engine = described_class.new(engine_config: config)
16+
engine = described_class.new(
17+
engine_config: config,
18+
parse_metrics: CC::Engine::ParseMetrics.new(
19+
language: described_class::LANGUAGE,
20+
io: StringIO.new,
21+
),
22+
)
1523
reporter = ::CC::Engine::Analyzers::Reporter.new(config, engine, io)
1624

1725
reporter.run

0 commit comments

Comments
 (0)