Skip to content

Conversation

@hhertout
Copy link
Contributor

@hhertout hhertout commented Nov 23, 2025

PR Description

The purpose of this pool request is to add the implementation of the connector.count of open telemetry in alloy.

The count connector generates metrics by counting telemetry signals (traces, logs, metrics) passing through the pipeline. This provides visibility into data ingestion volumes and allows tracking of data patterns across the collection layer.

OpenTelemetry connector count implementation: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/connector/countconnector

Features

The count connector provides visibility into your telemetry pipeline:

  • Monitor ingestion volumes: Track the number of spans, logs, and metrics that Alloy processes
  • Add pipeline observability: Instrument the collector itself to detect bottlenecks or data loss
  • Filter and segment data: Count specific signals using OTTL conditions, for example HTTP requests or error logs
  • Group by attributes: Generate dimensional metrics grouped by service, environment, or other attributes

You can configure the count connector with custom metric definitions that use conditions to filter telemetry and attributes to group counts.

Default values are taken from the OpenTelemetry Collector repository to align with its default behavior. When you don't specify custom metrics, the connector generates standard count metrics for all telemetry types.

Example of alloy configuration:

otelcol.connector.count "default" {
  // Count only HTTP GET spans
  spans {
    name        = "http_get_requests"
    description = "Number of HTTP GET requests"
    conditions  = [
      "attributes[\"http.method\"] == \"GET\"",
    ]
  }

  // Count spans grouped by service and environment
  spans {
    name        = "spans_by_service"
    description = "Spans per service and environment"
    attributes {
      key           = "service.name"
      default_value = "unknown"
    }
    attributes {
      key           = "env"
      default_value = "production"
    }
  }

  // Count error and fatal logs only
  logs {
    name        = "error_logs"
    description = "Error and fatal log records"
    conditions  = [
      "severity_number >= 17",
    ]
  }

  // Count logs by environment
  logs {
    name        = "logs_by_env"
    description = "Log records per environment"
    attributes {
      key = "env"
    }
  }

  output {
    metrics = [otelcol.exporter.otlp.default.input]
  }
}

Default values are taken from the OpenTelemetry Collector repository to align with its default behavior.

Which issue(s) this PR fixes

Testing

  • Unit tests
  • Testdata validation for OTel→Alloy converter
  • Manual end-to-end testing with Prometheus export

Notes to the Reviewer

During development, I encountered a build failure in the vendored dependencies related to OpenTelemetry Collector's experimental profiles support. The error manifested in the debugexporter component.

It takes me a while to understand where the problem would be, but from my understanding:
The root cause was a version mismatch between OpenTelemetry Collector components. The project uses OpenTelemetry Collector v0.139.0, but the profiles API (go.opentelemetry.io/collector/pdata/pprofile) had been upgraded to v0.140.0 in the vendor directory. The debugexporter v0.139.0 expected the older profiles API, creating an incompatibility.

I didn't want to upgrade the entire OpenTelemetry Collector dependency tree to v0.140.x for several reasons:

  • It would introduce unrelated breaking changes
  • The scope exceeded the PR's goal of adding the count connector
  • It risked introducing additional compatibility issues with other Grafana Alloy components

As a workaround, I wrote integration tests to verify the behavior is correct.
I tested the complete behavior locally by fixing the vendor files, and running Alloy with the connector.count, to ensure everything runs correctly.

Impact: This workaround doesn't affect the count connector functionality. The profiles functionality in debugexporter isn't used by the count connector implementation.

PR Checklist

  • CHANGELOG.md updated
  • Documentation added
  • Tests updated
  • Config converters updated

@hhertout hhertout requested review from a team and clayton-cornell as code owners November 23, 2025 14:26
@kalleep
Copy link
Contributor

kalleep commented Nov 24, 2025

Hey, thanks for the pr. This is a duplicate of #4610 and #4550.

We have just not gotten around to review those

@hhertout
Copy link
Contributor Author

hhertout commented Nov 24, 2025

Hello,

Sorry, I hadn't seen the second one you mentioned.
For the first one, after review it, my implementation is different, with tests, and the converter has also been implemented.

Let me know if you're still interested. Otherwise, I'll close it. There's no need for another duplicate if it's not useful.

@kalleep
Copy link
Contributor

kalleep commented Nov 24, 2025

Yes I see that. I merged the pr to add support for these kind of connectors so you could rebase on that and remove your changes to connector.go.

There have not been any reactions to docs feeback in the other pr and I prefer the config structure added in this one so I am fine to go with this one instead.

@yann-soubeyrand
Copy link
Contributor

Hello, I’m the author of the other PR. I’m sorry, I didn’t have the time (and still don’t have for the moment) to move it forward. I chose this (somewhat weird) config structure, because it allows disabling the default counts (for example if you’re only interested in counting logs). I’m not sure it improves performances (I guess the unused counts do not run if there are no consumer for them, right?), so maybe it’s not worth it.

@kalleep
Copy link
Contributor

kalleep commented Nov 24, 2025

Hello, I’m the author of the other PR. I’m sorry, I didn’t have the time (and still don’t have for the moment) to move it forward. I chose this (somewhat weird) config structure, because it allows disabling the default counts (for example if you’re only interested in counting logs). I’m not sure it improves performances (I guess the unused counts do not run if there are no consumer for them, right?), so maybe it’s not worth it.

Gotcha, yes it won't make any difference and that I why I prefer the config structure in this pr. I merged your changes to support these kind of connectors. So lets move ahead with this pr instead

@hhertout
Copy link
Contributor Author

Hello, I’m the author of the other PR. I’m sorry, I didn’t have the time (and still don’t have for the moment) to move it forward. I chose this (somewhat weird) config structure, because it allows disabling the default counts (for example if you’re only interested in counting logs). I’m not sure it improves performances (I guess the unused counts do not run if there are no consumer for them, right?), so maybe it’s not worth it.

That makes sense. I can add that functionality if you think it would be valuable. It shouldn't be too complex...

@hhertout hhertout force-pushed the feature/otel-connector-count branch from 7154693 to 2710fe9 Compare November 24, 2025 17:56
@hhertout
Copy link
Contributor Author

hhertout commented Nov 24, 2025

Yes I see that. I merged the pr to add support for these kind of connectors so you could rebase on that and remove your changes to connector.go.

There have not been any reactions to docs feeback in the other pr and I prefer the config structure added in this one so I am fine to go with this one instead.

I rebased, but I have to keep the if structure instead of the switch statements... due to this:

// ConnectorType() int implements connector.Arguments.
func (Arguments) ConnectorType() int {
	return connector.ConnectorLogsToMetrics | connector.ConnectorTracesToMetrics | connector.ConnectorMetricsToMetrics
}

As I understand, ConnectorType must return the type of connector used. In this case, it returns the three types of connectors, because it allows to generate metrics based on metrics, traces and logs inputs. This is slightly different from the span metrics for example, where there is only one type.

In the mentioned PR, the implementation have the following code:

func (args Arguments) ConnectorType() int {
	return connector.ConnectorLogsToMetrics
}

which is, from my point of view, not enough ? This will generate only metrics based on the log input right ?

Moreover, a switch statement doesn't work with combined flags since it checks for exact equality.
Here it works for both single-flag connectors (servicegraph, spanmetrics) and multi-flag connectors (count).

All existing connector tests pass with this approach.

WDYT ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants