Skip to content
Open
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
6 changes: 5 additions & 1 deletion src/cmt_encode_prometheus.c
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,11 @@ static cfl_sds_t bucket_value_to_string(double val)
len = snprintf(str, 64, "%g", val);
cfl_sds_len_set(str, len);

if (!strchr(str, '.')) {
/*
* Append .0 only when there is no decimal point and the number
* is not in scientific notation.
*/
if (!strchr(str, '.') && !strchr(str, 'e')) {
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

The check for scientific notation only looks for lowercase 'e'. While %g typically uses 'e', this becomes fragile if the formatting ever changes (e.g., %G) or if a platform emits an uppercase exponent. Consider using a character-class check (e.g., treating either 'e' or 'E' as scientific notation) so .0 is never appended to exponent-form values.

Suggested change
if (!strchr(str, '.') && !strchr(str, 'e')) {
if (!strchr(str, '.') && !strchr(str, 'e') && !strchr(str, 'E')) {

Copilot uses AI. Check for mistakes.
cfl_sds_cat_safe(&str, ".0", 2);
Comment on lines +376 to 381
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

This change fixes a Prometheus parsing failure for large bucket boundaries, but there doesn’t appear to be a regression test covering scientific-notation le (e.g., a histogram bucket at >= 1e6 that should encode as 1e+06 and remain parseable). Adding a test that encodes and then decodes Prometheus text (or validates the encoded string) would prevent this from reoccurring.

Copilot uses AI. Check for mistakes.
}
Comment on lines 373 to 382
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

bucket_value_to_string() will still append .0 to non-finite printf outputs like inf/nan (which contain neither . nor e), producing values like inf.0/nan.0 that are not parseable as floats in Prometheus label values. Since histogram buckets can be user-provided without an isfinite validation, consider explicitly handling non-finite val (e.g., reject, or map infinities to +Inf/-Inf, and avoid appending .0 for nan/inf).

Copilot uses AI. Check for mistakes.

Expand Down
40 changes: 40 additions & 0 deletions tests/histogram.c
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,45 @@ static void prometheus_encode_test(struct cmt *cmt)
cmt_encode_prometheus_destroy(buf);
}

void test_histogram_scientific_notation_bucket_label()
{
uint64_t ts;
cfl_sds_t buf;
struct cmt *cmt;
struct cmt_histogram *h;
struct cmt_histogram_buckets *buckets;

cmt_initialize();

ts = 0;
cmt = cmt_create();
TEST_CHECK(cmt != NULL);

buckets = cmt_histogram_buckets_create(1, 100000000.0);
TEST_CHECK(buckets != NULL);

h = cmt_histogram_create(cmt,
"cm", "encoding", "scientific_bucket",
"Histogram scientific bucket label",
buckets,
0, NULL);
TEST_CHECK(h != NULL);

cmt_histogram_observe(h, ts, 42.0, 0, NULL);

buf = cmt_encode_prometheus_create(cmt, CMT_TRUE);
TEST_CHECK(buf != NULL);
if (buf != NULL) {
TEST_CHECK(strstr(buf,
"cm_encoding_scientific_bucket_bucket{le=\"1e+08\"} 1 0") != NULL);
TEST_CHECK(strstr(buf,
"cm_encoding_scientific_bucket_bucket{le=\"1e+08.0\"}") == NULL);
cmt_encode_prometheus_destroy(buf);
}

cmt_destroy(cmt);
}


void test_histogram()
{
Expand Down Expand Up @@ -206,6 +245,7 @@ void test_set_defaults()
}

TEST_LIST = {
{"scientific_notation_bucket_label", test_histogram_scientific_notation_bucket_label},
{"histogram" , test_histogram},
{"set_defaults", test_set_defaults},
{ 0 }
Expand Down
Loading