Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions sentry-yabeda/lib/sentry/yabeda/adapter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,8 @@ def metric_name(metric)
[metric.group, metric.name].compact.join(".")
end

# TODO: Normalize Yabeda unit symbols (e.g. :milliseconds) to Sentry's
# canonical singular strings (e.g. "millisecond") once units are visible
# in the Sentry product. See https://develop.sentry.dev/sdk/foundations/state-management/scopes/attributes/#units
def unit_for(metric)
metric.unit&.to_s
metric.unit&.to_s&.chomp("s")
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: The use of chomp("s") to handle pluralization will incorrectly truncate any custom unit that naturally ends in "s", like :process.
Severity: MEDIUM

Suggested Fix

Instead of unconditionally calling chomp("s"), use a more robust method for singularization, such as a dedicated inflection library. Alternatively, if only handling standard units, use a targeted replacement based on a known list of plural forms rather than a blanket string manipulation.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.

Location: sentry-yabeda/lib/sentry/yabeda/adapter.rb#L72

Potential issue: The `unit_for` method unconditionally calls `.chomp("s")` on the string
representation of a metric's unit. This is problematic for custom units whose names
legitimately end in "s", such as `:process` or `:status`. In these cases, the unit will
be incorrectly truncated to `"proces"` or `"statu"` before being sent to Sentry. This
results in a mangled, unrecognized unit in Sentry without raising any error or warning,
leading to silent data corruption for those metrics. Standard Sentry units are
unaffected as they do not end in 's' in their singular form.

Did we get this right? 👍 / 👎 to inform future reviews.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's right but ok in our case, as we do have a fixed set of units for now imho

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Naive chomp("s") corrupts abbreviated unit names

Low Severity

Using chomp("s") to normalize units will mangle any unit whose singular/canonical form ends in "s". Abbreviated units like :ms, :ops, or :fps would become "m", "op", or "fp". The test "leaves already-singular units unchanged" uses :second (ending in "d"), so it doesn't actually exercise this risky scenario and gives false confidence in the approach.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 8694506. Configure here.

end
end
end
Expand Down
52 changes: 49 additions & 3 deletions sentry-yabeda/spec/sentry/yabeda/adapter_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ def build_metric(type, name:, group: nil, unit: nil)
expect(Sentry.metrics).to receive(:gauge).with(
"process.memory_usage",
1024,
unit: "bytes",
unit: "byte",
attributes: nil
)

Expand All @@ -101,7 +101,7 @@ def build_metric(type, name:, group: nil, unit: nil)
expect(Sentry.metrics).to receive(:distribution).with(
"rails.request_duration",
150.5,
unit: "milliseconds",
unit: "millisecond",
attributes: tags
)

Expand All @@ -117,14 +117,60 @@ def build_metric(type, name:, group: nil, unit: nil)
expect(Sentry.metrics).to receive(:distribution).with(
"http.response_size",
2048,
unit: "bytes",
unit: "byte",
attributes: tags
)

adapter.perform_summary_observe!(summary, tags, 2048)
end
end

describe "unit normalization" do
it "converts plural yabeda units to Sentry's singular form" do
perform_basic_setup

histogram = build_metric(:histogram, name: :duration, group: :rails, unit: :seconds)
expect(Sentry.metrics).to receive(:distribution).with(
"rails.duration", 1.5, unit: "second", attributes: nil
)

adapter.perform_histogram_measure!(histogram, {}, 1.5)
end

it "converts milliseconds to millisecond" do
perform_basic_setup

histogram = build_metric(:histogram, name: :latency, unit: :milliseconds)
expect(Sentry.metrics).to receive(:distribution).with(
"latency", 250.0, unit: "millisecond", attributes: nil
)

adapter.perform_histogram_measure!(histogram, {}, 250.0)
end

it "passes nil when unit is not set" do
perform_basic_setup

gauge = build_metric(:gauge, name: :threads)
expect(Sentry.metrics).to receive(:gauge).with(
"threads", 5, unit: nil, attributes: nil
)

adapter.perform_gauge_set!(gauge, {}, 5)
end

it "leaves already-singular units unchanged" do
perform_basic_setup

gauge = build_metric(:gauge, name: :uptime, unit: :second)
expect(Sentry.metrics).to receive(:gauge).with(
"uptime", 3600, unit: "second", attributes: nil
)

adapter.perform_gauge_set!(gauge, {}, 3600)
end
end

describe "registration methods (no-ops)" do
it "accepts register_counter! without error" do
expect { adapter.register_counter!(double) }.not_to raise_error
Expand Down
2 changes: 1 addition & 1 deletion sentry-yabeda/spec/sentry/yabeda/integration_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@
expect(metric[:name]).to eq("myapp.response_time")
expect(metric[:type]).to eq(:distribution)
expect(metric[:value]).to eq(150.5)
expect(metric[:unit]).to eq("milliseconds")
expect(metric[:unit]).to eq("millisecond")
expect(metric[:attributes][:controller]).to eq({ type: "string", value: "orders" })
expect(metric[:attributes][:action]).to eq({ type: "string", value: "index" })
end
Expand Down
Loading