diff --git a/.changeset/populate-telemetry-columns.md b/.changeset/populate-telemetry-columns.md new file mode 100644 index 0000000000..05891bab77 --- /dev/null +++ b/.changeset/populate-telemetry-columns.md @@ -0,0 +1,5 @@ +--- +'@core/sync-service': patch +--- + +Populate previously-empty telemetry span attributes for shape requests: `num_bytes` on the root shape-get span and `electric.subqueries.subset_result.{bytes,rows,duration_µs}` on subset materialisation spans. This makes the corresponding Honeycomb columns queryable. diff --git a/.gitignore b/.gitignore index 9d4bcc8d04..a89b4d42bc 100644 --- a/.gitignore +++ b/.gitignore @@ -30,4 +30,5 @@ response.tmp .claude !website/.claude/commands !website/.claude/skills -_artifacts \ No newline at end of file +_artifacts +.agent-tasks \ No newline at end of file diff --git a/packages/sync-service/lib/electric/plug/serve_shape_plug.ex b/packages/sync-service/lib/electric/plug/serve_shape_plug.ex index ba4311bd2f..c57afea19a 100644 --- a/packages/sync-service/lib/electric/plug/serve_shape_plug.ex +++ b/packages/sync-service/lib/electric/plug/serve_shape_plug.ex @@ -291,11 +291,13 @@ defmodule Electric.Plug.ServeShapePlug do # is the place to assign them because we keep this plug last in the "plug pipeline" defined # in this module. defp end_telemetry_span(%Conn{assigns: assigns} = conn, _ \\ nil) do + bytes_sent = assigns[:streaming_bytes_sent] || 0 + OpenTelemetry.execute( [:electric, :plug, :serve_shape], %{ count: 1, - bytes: assigns[:streaming_bytes_sent] || 0, + bytes: bytes_sent, monotonic_time: System.monotonic_time(), duration: System.monotonic_time() - conn.private[:electric_telemetry_span][:start_time] }, @@ -308,6 +310,12 @@ defmodule Electric.Plug.ServeShapePlug do } ) + # Expose the total response body size as a dedicated span attribute + # alongside the semantic `http.response_size` set by + # `Electric.Plug.Utils.common_open_telemetry_attrs/1`. This populates the + # `num_bytes` Honeycomb column for shape-get requests. + OpenTelemetry.add_span_attributes(%{num_bytes: bytes_sent}) + add_span_attrs_from_conn(conn) OpentelemetryTelemetry.end_telemetry_span(OpenTelemetry, %{}) conn diff --git a/packages/sync-service/lib/electric/shapes/partial_modes.ex b/packages/sync-service/lib/electric/shapes/partial_modes.ex index 53482d7d41..a188cbf57c 100644 --- a/packages/sync-service/lib/electric/shapes/partial_modes.ex +++ b/packages/sync-service/lib/electric/shapes/partial_modes.ex @@ -63,10 +63,12 @@ defmodule Electric.Shapes.PartialModes do {[row], {start_time, bytes + IO.iodata_length(row), rows + 1}} end, fn {start_time, bytes, rows} -> + duration = System.monotonic_time(:microsecond) - start_time + OpenTelemetry.execute( [:electric, :subqueries, :subset_result], %{ - duration: System.monotonic_time(:microsecond) - start_time, + duration: duration, bytes: bytes, count: 1, rows: rows @@ -77,6 +79,15 @@ defmodule Electric.Shapes.PartialModes do "shape.root_table": shape.root_table } ) + + # Expose subset materialisation metrics as span attributes on the + # active shape_snapshot.query_fn child span so they become queryable + # Honeycomb columns. Mirrors the telemetry event measurements above. + OpenTelemetry.add_span_attributes(%{ + "electric.subqueries.subset_result.bytes" => bytes, + "electric.subqueries.subset_result.rows" => rows, + "electric.subqueries.subset_result.duration_µs" => duration + }) end ) end