Everything I know about wide events in 15 minutes

In your opinion, is this a good talk?

In your opinion, is this a good talk?

⏺ Honest take: it's a strong technical talk, probably 7/10 as-is.

✻ Crunched for 48s

— Claude Opus 4.7 (1M context)

Feature

# cart_items_controller.rb
def create
  current_cart.cart_items.create(item: item)
end

Ship it.

The problem

A week later someone asks:

How many Pro-plan users added an item to their cart yesterday?

So you add some logging.

Logs

# cart_items_controller.rb
def create
+  Rails.logger.info "Add started"
+
  current_cart.cart_items.create(item: item)
+  Rails.logger.info "Item added"
+
+  Rails.logger.info "Add finished"
end

Logs (output)

2026-04-28 10:23:14 INFO Add started
2026-04-28 10:23:14 INFO Item added
2026-04-28 10:23:15 INFO Add finished

Logs

# cart_items_controller.rb
def create
-  Rails.logger.info "Add started"
+  Rails.logger.info "Add started user:#{current_user.id}"

  current_cart.cart_items.create(item: item)
-  Rails.logger.info "Item added"
+  Rails.logger.info "Item added user:#{current_user.id} plan:#{current_user.subscription_plan} item:#{item.id}"

-  Rails.logger.info "Add finished"
+  Rails.logger.info "Add finished user:#{current_user.id}"
end

Logs (output)

2026-04-28 10:23:14 INFO Add started user:42
2026-04-28 10:23:14 INFO Item added user:42 plan:pro item:7891
2026-04-28 10:23:15 INFO Add finished user:42

Structured Logs + Metrics

# cart_items_controller.rb
def create
-  Rails.logger.info "Add started user:#{current_user.id}"
+  Rails.event.notify "Add started", user_id: current_user.id

  current_cart.cart_items.create(item: item)
-  Rails.logger.info "Item added user:#{current_user.id} plan:#{current_user.subscription_plan} item:#{item.id}"
+  MyMetricsService.count "Item added", item_id: item.id, plan: current_user.subscription_plan
+  Rails.event.notify "Item added", user_id: current_user.id

-  Rails.logger.info "Add finished user:#{current_user.id}"
+  Rails.event.notify "Add finished", user_id: current_user.id
end

Structured Logs + Metrics (output)

Events:

timestamp event user_id
2026-04-28 10:23:14 Add started 42
2026-04-28 10:23:14 Item added 42
2026-04-28 10:23:15 Add finished 42

Metrics: item_added{item_id=7891, plan=pro} +1

Structured Logs with Context

# cart_items_controller.rb
def create
-  Rails.event.notify "Add started", user_id: current_user.id
+  Rails.event.set_context(user_id: current_user.id)
+
+  Rails.event.notify "Add started"

  current_cart.cart_items.create(item: item)
  MyMetricsService.count "Item added", item_id: item.id, plan: current_user.subscription_plan
-  Rails.event.notify "Item added", user_id: current_user.id
+  Rails.event.notify "Item added"

-  Rails.event.notify "Add finished", user_id: current_user.id
+  Rails.event.notify "Add finished"
end

Structured Logs with Context (output)

Events:

timestamp event user_id
2026-04-28 10:23:14 Add started 42
2026-04-28 10:23:14 Item added 42
2026-04-28 10:23:15 Add finished 42

Metrics: item_added{item_id=7891, plan=pro} +1

Wide Events

  • one event per unit of work
  • lots of context

Wide Events

# cart_items_controller.rb
+around_action do
+  Rails.event.set_context(
+    user_id: current_user.id,
+    cart_id: current_cart.id,
+    controller: controller_name,
+    action: action_name
+  )
+  yield
+  Rails.event.notify "request"
+end
+
def create
-  Rails.event.set_context(user_id: current_user.id)
-
-  Rails.event.notify "Add started"
-
  current_cart.cart_items.create(item: item)
-  MyMetricsService.count "Item added", item_id: item.id, plan: current_user.subscription_plan
-  Rails.event.notify "Item added"
-
-  Rails.event.notify "Add finished"
+  Rails.event.set_context(item_id: item.id)
end

Wide Events (output)

event: request
timestamp: 2026-04-28T10:23:15Z
request_id: 7c3a-1f9d
controller: cart_items
action: create
user_id: 42
cart_id: 99
item_id: 7891

Wide Events queries (BadgerQL)

Top users by add-to-cart

filter controller::str == "cart_items"
| filter action::str == "create"
| stats count() as adds by user_id::int
| sort adds desc
| limit 10

Every request from user 42 in the last hour

filter user_id::int == 42
| sort @ts desc

Wide Events graphs (BadgerQL)

Add-to-cart volume over time

filter controller::str == "cart_items"
| filter action::str == "create"
| stats count() by bin()

Even Wider Events

# user.rb
def context
  {
    id: id,
    signed_up_at: signed_up_at,
    subscribed_at: subscribed_at,
    subscription_plan: subscription_plan
  }
end

# cart.rb
def context
  {
    id: id,
    created_at: created_at,
    item_count: items_count,
    subtotal_cents: subtotal_cents
  }
end

# item.rb
def context
  {
    id: id,
    name: name,
    category: category,
    price_cents: price_cents
  }
end

Even Wider Events

# cart_items_controller.rb
around_action do
  Rails.event.set_context(
-    user_id: current_user.id,
-    cart_id: current_cart.id,
+    user: current_user.context,
+    cart: current_cart.context,
    controller: controller_name,
    action: action_name
  )
  yield
  Rails.event.notify "request"
end

def create
  current_cart.cart_items.create(item: item)
-  Rails.event.set_context(item_id: item.id)
+  Rails.event.set_context(item: item.context)
end

Even Wider Events (output)

event: request
timestamp: 2026-04-28T10:23:15Z
request_id: 7c3a-1f9d
controller: cart_items
action: create
user:
  id: 42
  signed_up_at: 2025-09-12
  subscribed_at: 2026-01-03
  subscription_plan: pro
cart:
  id: 99
  created_at: 2026-04-28T09:51:02Z
  item_count: 3
  subtotal_cents: 12500
item:
  id: 7891
  name: "Wide-brimmed hat"
  category: apparel
  price_cents: 4500

Wider Events queries (BadgerQL)

How many Pro-plan users added an item yesterday?

filter controller::str == "cart_items"
| filter action::str == "create"
| filter user.subscription_plan::str == "pro"
| stats unique(user.id::int) as users

Adds by category × subscription plan

filter action::str == "create"
| stats count() by item.category::str, user.subscription_plan::str

Wider Events graphs (BadgerQL)

Adds-to-cart over time, by subscription plan

filter controller::str == "cart_items"
| filter action::str == "create"
| stats count() by bin(), user.subscription_plan::str

What this unlocks

  • Helps answer questions you never thought to ask when you wrote the code.
    • Which subscription plans saw the slowest add-to-cart last week?
    • What items did expired-trial users try to add?
    • Show me every request from user_id=42 in the last hour.
  • Every field on the event becomes a dimension you can slice on (cardinality).

A note on dimensions and cardinality

  • Wide events love high-cardinality fields: user_id, item_id, request_id, git_sha.
  • The more fields you add, the better your cross references become.
  • Storage is cheap, use it!

When not to use wide events

  • System-level monitoring — CPU, memory, queue depth, error rates. Sometimes you don't need context, just the number.
  • High-throughput, low-latency events — e.g. every mouse movement on a web page, where the cardinality is too high and becomes unmanageable.
  • Ad-hoc debugging — throwing in a quick log is cheaper and faster.

Key Takeaways

  • One event per unit of work.
  • Pile context onto it. set_context is your friend.
  • Look for high cardinality data.
  • Practice, practice, practice. Don't wait till there's a fire.

Thanks!

Questions?

roel.bondoc@phoenix.ca