flow_chat 0.8.1 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.cliff.toml +74 -0
- data/.github/workflows/ci.yml +2 -3
- data/.github/workflows/release.yml +56 -0
- data/.standard.yml +4 -0
- data/CHANGELOG.md +22 -0
- data/CLAUDE.md +327 -0
- data/CONTRIBUTING.md +134 -0
- data/Gemfile +1 -0
- data/README.md +290 -105
- data/Rakefile +5 -1
- data/SECURITY.md +42 -349
- data/docs/architecture.md +510 -0
- data/docs/async-background-processing.md +298 -0
- data/docs/configuration.md +556 -226
- data/docs/factory-pattern.md +355 -0
- data/docs/gateway-context-variables.md +171 -0
- data/docs/gateway-development.md +723 -0
- data/docs/getting-started.md +429 -0
- data/docs/instrumentation.md +264 -153
- data/docs/platforms/telegram.md +1013 -0
- data/docs/platforms/ussd.md +693 -0
- data/docs/platforms/whatsapp.md +1395 -0
- data/docs/testing.md +243 -365
- data/examples/custom_session_id_example.rb +119 -0
- data/examples/http_controller.rb +154 -0
- data/examples/intercom_configuration_example.rb +118 -0
- data/examples/intercom_controller.rb +194 -0
- data/examples/multi_tenant_whatsapp_controller.rb +4 -4
- data/examples/simulator_controller.rb +21 -1
- data/examples/ussd_controller.rb +10 -10
- data/examples/whatsapp_controller.rb +2 -2
- data/flow_chat.gemspec +4 -0
- data/lib/flow_chat/{base_app.rb → app.rb} +28 -9
- data/lib/flow_chat/async_job.rb +176 -0
- data/lib/flow_chat/config.rb +23 -28
- data/lib/flow_chat/{base_executor.rb → executor.rb} +7 -12
- data/lib/flow_chat/factory.rb +94 -0
- data/lib/flow_chat/gateway_async_support.rb +106 -0
- data/lib/flow_chat/generic_async_job.rb +30 -0
- data/lib/flow_chat/http/gateway/simple.rb +125 -0
- data/lib/flow_chat/http/renderer.rb +41 -0
- data/lib/flow_chat/instrumentation/setup.rb +1 -1
- data/lib/flow_chat/instrumentation.rb +25 -0
- data/lib/flow_chat/intercom/client.rb +155 -0
- data/lib/flow_chat/intercom/configuration.rb +149 -0
- data/lib/flow_chat/intercom/gateway/intercom_api.rb +396 -0
- data/lib/flow_chat/intercom/renderer.rb +71 -0
- data/lib/flow_chat/phone_number_util.rb +49 -0
- data/lib/flow_chat/processor.rb +188 -0
- data/lib/flow_chat/renderers/markdown_support.rb +58 -0
- data/lib/flow_chat/session/cache_session_store.rb +1 -17
- data/lib/flow_chat/session/middleware.rb +43 -26
- data/lib/flow_chat/simulator/controller.rb +17 -5
- data/lib/flow_chat/simulator/views/simulator.html.erb +220 -8
- data/lib/flow_chat/telegram/client.rb +240 -0
- data/lib/flow_chat/telegram/configuration.rb +118 -0
- data/lib/flow_chat/telegram/gateway/bot_api.rb +300 -0
- data/lib/flow_chat/telegram/middleware/choice_mapper.rb +35 -0
- data/lib/flow_chat/telegram/renderer.rb +125 -0
- data/lib/flow_chat/telegram.rb +7 -0
- data/lib/flow_chat/ussd/gateway/nalo.rb +27 -11
- data/lib/flow_chat/ussd/middleware/pagination.rb +9 -5
- data/lib/flow_chat/ussd/renderer.rb +1 -1
- data/lib/flow_chat/version.rb +1 -1
- data/lib/flow_chat/whatsapp/client.rb +144 -13
- data/lib/flow_chat/whatsapp/configuration.rb +1 -1
- data/lib/flow_chat/whatsapp/gateway/cloud_api.rb +144 -106
- data/lib/flow_chat/whatsapp/id_generator.rb +124 -0
- data/lib/flow_chat/whatsapp/middleware/choice_mapper.rb +147 -0
- data/lib/flow_chat/whatsapp/renderer.rb +145 -11
- data/lib/flow_chat.rb +11 -1
- data/lib/tasks/release.rake +165 -0
- metadata +88 -21
- data/docs/flows.md +0 -320
- data/docs/images/simulator.png +0 -0
- data/docs/media.md +0 -153
- data/docs/sessions.md +0 -433
- data/docs/ussd-setup.md +0 -322
- data/docs/whatsapp-setup.md +0 -162
- data/examples/whatsapp_message_job.rb +0 -113
- data/lib/flow_chat/base_processor.rb +0 -145
- data/lib/flow_chat/session/rails_session_store.rb +0 -68
- data/lib/flow_chat/ussd/app.rb +0 -6
- data/lib/flow_chat/ussd/gateway/nsano.rb +0 -98
- data/lib/flow_chat/ussd/middleware/executor.rb +0 -24
- data/lib/flow_chat/ussd/processor.rb +0 -39
- data/lib/flow_chat/whatsapp/app.rb +0 -29
- data/lib/flow_chat/whatsapp/middleware/executor.rb +0 -24
- data/lib/flow_chat/whatsapp/processor.rb +0 -32
- data/lib/flow_chat/whatsapp/send_job_support.rb +0 -79
data/docs/instrumentation.md
CHANGED
|
@@ -1,216 +1,327 @@
|
|
|
1
|
-
# Instrumentation
|
|
1
|
+
# Instrumentation
|
|
2
2
|
|
|
3
|
-
FlowChat
|
|
3
|
+
FlowChat provides comprehensive instrumentation via ActiveSupport::Notifications, enabling monitoring, logging, metrics collection, and error tracking across all platforms.
|
|
4
4
|
|
|
5
|
-
## Quick
|
|
6
|
-
|
|
7
|
-
Enable instrumentation in your Rails application:
|
|
5
|
+
## Quick Start
|
|
8
6
|
|
|
9
7
|
```ruby
|
|
10
|
-
#
|
|
11
|
-
|
|
8
|
+
# Subscribe to all FlowChat events
|
|
9
|
+
ActiveSupport::Notifications.subscribe(/\.flow_chat$/) do |event|
|
|
10
|
+
Rails.logger.info "FlowChat: #{event.name} - #{event.payload}"
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Subscribe to specific events
|
|
14
|
+
ActiveSupport::Notifications.subscribe("api.error.flow_chat") do |event|
|
|
15
|
+
Sentry.capture_message("API Error", extra: event.payload)
|
|
16
|
+
end
|
|
12
17
|
```
|
|
13
18
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
- 🔍 **Event Tracking** - All framework events instrumented
|
|
18
|
-
- ⚡ **Performance Monitoring** - Execution timing and bottleneck detection
|
|
19
|
+
## Event Reference
|
|
20
|
+
|
|
21
|
+
All events are namespaced with `.flow_chat` suffix. Use `FlowChat::Instrumentation::Events` constants for consistency.
|
|
19
22
|
|
|
20
|
-
|
|
23
|
+
### Core Framework Events
|
|
21
24
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
| Event | Description | Key Payload |
|
|
26
|
+
|-------|-------------|-------------|
|
|
27
|
+
| `flow.execution.start` | Flow method begins executing | `flow_name`, `action`, `session_id` |
|
|
28
|
+
| `flow.execution.end` | Flow method completes | `flow_name`, `action`, `duration` |
|
|
29
|
+
| `flow.execution.error` | Unhandled error in flow | `flow_name`, `error`, `backtrace` |
|
|
30
|
+
| `context.created` | New request context created | `request_id`, `platform` |
|
|
26
31
|
|
|
27
|
-
|
|
28
|
-
- Flow execution counts and timing
|
|
29
|
-
- Session creation/destruction rates
|
|
30
|
-
- WhatsApp/USSD message volumes
|
|
31
|
-
- Cache hit/miss ratios
|
|
32
|
-
- Error tracking by type and flow
|
|
32
|
+
### Session Events
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
34
|
+
| Event | Description | Key Payload |
|
|
35
|
+
|-------|-------------|-------------|
|
|
36
|
+
| `session.created` | New session started | `session_id`, `boundaries` |
|
|
37
|
+
| `session.destroyed` | Session ended/cleared | `session_id` |
|
|
38
|
+
| `session.data.get` | Session data read | `session_id`, `key` |
|
|
39
|
+
| `session.data.set` | Session data written | `session_id`, `key` |
|
|
40
|
+
| `session.cache.hit` | Session found in cache | `session_id` |
|
|
41
|
+
| `session.cache.miss` | Session not in cache | `session_id` |
|
|
38
42
|
|
|
39
|
-
|
|
40
|
-
- Minimal performance overhead
|
|
41
|
-
- Thread-safe operations
|
|
42
|
-
- Graceful error handling
|
|
43
|
+
### Messaging Events
|
|
43
44
|
|
|
44
|
-
|
|
45
|
+
| Event | Description | Key Payload |
|
|
46
|
+
|-------|-------------|-------------|
|
|
47
|
+
| `message.received` | Inbound message from user | `platform`, `from`, `message_type` |
|
|
48
|
+
| `message.sent` | Outbound message to user | `platform`, `to`, `message_type`, `content_length` |
|
|
49
|
+
| `media.upload` | Media file uploaded (WhatsApp) | `platform`, `filename`, `mime_type`, `size` |
|
|
50
|
+
| `pagination.triggered` | USSD pagination activated | `session_id`, `page`, `total_pages` |
|
|
45
51
|
|
|
46
|
-
|
|
52
|
+
### Webhook Events
|
|
47
53
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
54
|
+
| Event | Description | Key Payload |
|
|
55
|
+
|-------|-------------|-------------|
|
|
56
|
+
| `webhook.verified` | Webhook signature valid (WhatsApp) | `platform`, `gateway` |
|
|
57
|
+
| `webhook.failed` | Webhook validation failed (WhatsApp) | `platform`, `reason` |
|
|
52
58
|
|
|
53
|
-
###
|
|
54
|
-
- `session.created.flow_chat`
|
|
55
|
-
- `session.destroyed.flow_chat`
|
|
56
|
-
- `session.data.get.flow_chat`
|
|
57
|
-
- `session.data.set.flow_chat`
|
|
58
|
-
- `session.cache.hit.flow_chat`
|
|
59
|
-
- `session.cache.miss.flow_chat`
|
|
59
|
+
### API Events
|
|
60
60
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
- `whatsapp.webhook.verified.flow_chat`
|
|
65
|
-
- `whatsapp.api.request.flow_chat`
|
|
66
|
-
- `whatsapp.media.upload.flow_chat`
|
|
61
|
+
| Event | Description | Key Payload |
|
|
62
|
+
|-------|-------------|-------------|
|
|
63
|
+
| `api.error` | API call failed | `platform`, `message`, error details |
|
|
67
64
|
|
|
68
|
-
###
|
|
69
|
-
- `ussd.message.received.flow_chat`
|
|
70
|
-
- `ussd.message.sent.flow_chat`
|
|
71
|
-
- `ussd.pagination.triggered.flow_chat`
|
|
65
|
+
### Events for Custom Use
|
|
72
66
|
|
|
73
|
-
|
|
67
|
+
The following event constants are provided for use in your own instrumentation but are not emitted by FlowChat internally:
|
|
74
68
|
|
|
75
|
-
|
|
69
|
+
| Event | Suggested Use | Key Payload |
|
|
70
|
+
|-------|---------------|-------------|
|
|
71
|
+
| `api.request` | Wrap outbound API calls | `platform`, `endpoint` |
|
|
72
|
+
| `middleware.before` | Custom middleware entry | `middleware_class` |
|
|
73
|
+
| `middleware.after` | Custom middleware exit | `middleware_class`, `duration` |
|
|
74
|
+
| `conversation.assigned` | Intercom assignment tracking | `conversation_id`, `admin_id` |
|
|
75
|
+
| `conversation.tagged` | Intercom tag tracking | `conversation_id`, `tag` |
|
|
76
|
+
| `conversation.state_changed` | Intercom state tracking | `conversation_id`, `state` |
|
|
76
77
|
|
|
78
|
+
## API Error Instrumentation
|
|
79
|
+
|
|
80
|
+
The `api.error` event provides detailed information when API calls fail. This is useful for monitoring, alerting, and debugging integration issues.
|
|
81
|
+
|
|
82
|
+
### Event Payload by Platform
|
|
83
|
+
|
|
84
|
+
**Telegram:**
|
|
77
85
|
```ruby
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
session_metrics = FlowChat.metrics.get_category("sessions")
|
|
88
|
-
puts session_metrics["sessions.created"] # Total sessions created
|
|
89
|
-
puts session_metrics["sessions.cache.hits"] # Cache hit count
|
|
86
|
+
{
|
|
87
|
+
platform: :telegram,
|
|
88
|
+
message: "Telegram API error: Unauthorized",
|
|
89
|
+
bot_id: "123456789",
|
|
90
|
+
api_method: "sendMessage",
|
|
91
|
+
error_code: 401,
|
|
92
|
+
error_description: "Unauthorized",
|
|
93
|
+
chat_id: 987654321
|
|
94
|
+
}
|
|
90
95
|
```
|
|
91
96
|
|
|
92
|
-
|
|
97
|
+
**WhatsApp:**
|
|
98
|
+
```ruby
|
|
99
|
+
{
|
|
100
|
+
platform: :whatsapp,
|
|
101
|
+
message: "WhatsApp API request failed",
|
|
102
|
+
phone_number_id: "123456789",
|
|
103
|
+
recipient: "+1234567890",
|
|
104
|
+
message_type: "text",
|
|
105
|
+
response_code: "401",
|
|
106
|
+
error_type: "OAuthException",
|
|
107
|
+
error_code: 190,
|
|
108
|
+
error_subcode: 463,
|
|
109
|
+
error_message: "Error validating access token"
|
|
110
|
+
}
|
|
111
|
+
```
|
|
93
112
|
|
|
113
|
+
**Intercom:**
|
|
94
114
|
```ruby
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
}) do
|
|
103
|
-
# Payment processing logic
|
|
104
|
-
result = process_mobile_money_payment
|
|
105
|
-
|
|
106
|
-
# Event automatically includes session_id, flow_name, gateway
|
|
107
|
-
result
|
|
108
|
-
end
|
|
109
|
-
end
|
|
110
|
-
end
|
|
115
|
+
{
|
|
116
|
+
platform: :intercom,
|
|
117
|
+
message: "Intercom authentication failed",
|
|
118
|
+
app_id: "abc123def",
|
|
119
|
+
conversation_id: "conv_123",
|
|
120
|
+
admin_id: "admin_456"
|
|
121
|
+
}
|
|
111
122
|
```
|
|
112
123
|
|
|
113
|
-
###
|
|
124
|
+
### Error Handling Behavior
|
|
114
125
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
126
|
+
| Error Type | Telegram | WhatsApp | Intercom |
|
|
127
|
+
|------------|----------|----------|----------|
|
|
128
|
+
| Invalid credentials | Returns `{"ok"=>false}` | Returns `nil` | Raises `ConfigurationError` |
|
|
129
|
+
| API error response | Returns error hash | Returns `nil` | Returns `nil` |
|
|
130
|
+
| Network timeout | Re-raises exception | Re-raises exception | Re-raises exception |
|
|
131
|
+
| Connection refused | Returns `{"ok"=>false}` | Returns `nil` | Returns `nil` |
|
|
118
132
|
|
|
119
|
-
|
|
120
|
-
ActiveSupport::Notifications.subscribe("flow.execution.end.flow_chat") do |event|
|
|
121
|
-
duration = event.duration
|
|
122
|
-
flow_name = event.payload[:flow_name]
|
|
123
|
-
|
|
124
|
-
# Send to external monitoring service
|
|
125
|
-
ExternalMonitoring.track_flow_execution(flow_name, duration)
|
|
126
|
-
end
|
|
133
|
+
**Note:** Network timeouts (`Net::OpenTimeout`, `Net::ReadTimeout`) are intentionally re-raised without instrumentation, allowing callers to implement retry logic at a higher level.
|
|
127
134
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
135
|
+
### Example: Error Monitoring
|
|
136
|
+
|
|
137
|
+
```ruby
|
|
138
|
+
# config/initializers/flow_chat_monitoring.rb
|
|
139
|
+
|
|
140
|
+
ActiveSupport::Notifications.subscribe("api.error.flow_chat") do |event|
|
|
141
|
+
payload = event.payload
|
|
142
|
+
|
|
143
|
+
# Log with structured data
|
|
144
|
+
Rails.logger.error({
|
|
145
|
+
event: "flow_chat.api_error",
|
|
146
|
+
platform: payload[:platform],
|
|
147
|
+
message: payload[:message],
|
|
148
|
+
error_code: payload[:error_code],
|
|
149
|
+
recipient: payload[:recipient] || payload[:chat_id] || payload[:conversation_id]
|
|
150
|
+
}.to_json)
|
|
151
|
+
|
|
152
|
+
# Send to error tracking service
|
|
153
|
+
Sentry.capture_message(
|
|
154
|
+
"FlowChat API Error: #{payload[:message]}",
|
|
155
|
+
level: :error,
|
|
156
|
+
extra: payload
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
# Increment metrics
|
|
160
|
+
StatsD.increment("flow_chat.api_error", tags: ["platform:#{payload[:platform]}"])
|
|
131
161
|
end
|
|
132
162
|
```
|
|
133
163
|
|
|
134
|
-
###
|
|
164
|
+
### Example: Rails Error Reporting
|
|
165
|
+
|
|
166
|
+
FlowChat automatically reports errors to `Rails.error` when available (Rails 7+):
|
|
135
167
|
|
|
136
168
|
```ruby
|
|
137
|
-
#
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
169
|
+
# Errors are automatically reported with context:
|
|
170
|
+
Rails.error.report(exception, handled: true, context: {
|
|
171
|
+
platform: :whatsapp,
|
|
172
|
+
recipient: "+1234567890",
|
|
173
|
+
message_type: "text"
|
|
174
|
+
})
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
You can subscribe to these in your error reporting configuration:
|
|
178
|
+
|
|
179
|
+
```ruby
|
|
180
|
+
# config/initializers/error_reporting.rb
|
|
181
|
+
Rails.error.subscribe(MyErrorReporter.new)
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## Custom Instrumentation
|
|
185
|
+
|
|
186
|
+
### In Flows
|
|
146
187
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
188
|
+
```ruby
|
|
189
|
+
class PaymentFlow < FlowChat::Flow
|
|
190
|
+
def process_payment
|
|
191
|
+
amount = app.screen(:amount) { |p| p.ask "Enter amount:" }
|
|
192
|
+
|
|
193
|
+
instrument(Events::API_REQUEST, { endpoint: "payment_gateway" }) do
|
|
194
|
+
result = PaymentGateway.charge(amount)
|
|
195
|
+
|
|
196
|
+
if result.success?
|
|
197
|
+
app.say "Payment successful!"
|
|
198
|
+
else
|
|
199
|
+
instrument(Events::API_ERROR, {
|
|
200
|
+
message: "Payment failed",
|
|
201
|
+
error_code: result.error_code
|
|
202
|
+
})
|
|
203
|
+
app.say "Payment failed: #{result.error}"
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
end
|
|
151
207
|
end
|
|
152
208
|
```
|
|
153
209
|
|
|
154
|
-
|
|
210
|
+
### In Custom Middleware
|
|
155
211
|
|
|
156
|
-
|
|
212
|
+
```ruby
|
|
213
|
+
class MetricsMiddleware
|
|
214
|
+
include FlowChat::Instrumentation
|
|
215
|
+
|
|
216
|
+
def initialize(app)
|
|
217
|
+
@app = app
|
|
218
|
+
end
|
|
157
219
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
- **Memory Usage**: <1MB for typical applications
|
|
161
|
-
- **Storage**: Events are ephemeral, metrics are kept in memory
|
|
220
|
+
def call(context)
|
|
221
|
+
@context = context
|
|
162
222
|
|
|
163
|
-
|
|
223
|
+
instrument(Events::MIDDLEWARE_BEFORE, { middleware_class: self.class.name })
|
|
164
224
|
|
|
165
|
-
|
|
225
|
+
start_time = Time.current
|
|
226
|
+
result = @app.call(context)
|
|
227
|
+
duration = Time.current - start_time
|
|
166
228
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
229
|
+
instrument(Events::MIDDLEWARE_AFTER, {
|
|
230
|
+
middleware_class: self.class.name,
|
|
231
|
+
duration: duration
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
result
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
# Required for context enrichment
|
|
238
|
+
attr_reader :context
|
|
239
|
+
end
|
|
170
240
|
```
|
|
171
241
|
|
|
172
|
-
###
|
|
242
|
+
### Module-Level Instrumentation
|
|
173
243
|
|
|
174
244
|
```ruby
|
|
175
|
-
#
|
|
176
|
-
FlowChat.
|
|
245
|
+
# Direct instrumentation without including the module
|
|
246
|
+
FlowChat::Instrumentation.instrument("custom.event", {
|
|
247
|
+
custom_key: "custom_value"
|
|
248
|
+
})
|
|
177
249
|
```
|
|
178
250
|
|
|
179
|
-
|
|
251
|
+
## Payload Enrichment
|
|
252
|
+
|
|
253
|
+
When instrumenting from objects with a `context` accessor, payloads are automatically enriched with:
|
|
254
|
+
|
|
255
|
+
- `request_id` - Unique request identifier
|
|
256
|
+
- `session_id` - Current session ID
|
|
257
|
+
- `flow_name` - Active flow class name
|
|
258
|
+
- `gateway` - Gateway handling the request
|
|
259
|
+
- `platform` - Platform (`:ussd`, `:whatsapp`, `:telegram`, `:intercom`)
|
|
260
|
+
|
|
261
|
+
All payloads also include:
|
|
262
|
+
- `timestamp` - Event timestamp (`Time.current`)
|
|
263
|
+
|
|
264
|
+
## Subscribing to Events
|
|
265
|
+
|
|
266
|
+
### Pattern Matching
|
|
180
267
|
|
|
181
268
|
```ruby
|
|
182
|
-
#
|
|
183
|
-
ActiveSupport::Notifications.
|
|
269
|
+
# All FlowChat events
|
|
270
|
+
ActiveSupport::Notifications.subscribe(/\.flow_chat$/) { |event| ... }
|
|
271
|
+
|
|
272
|
+
# All error events
|
|
273
|
+
ActiveSupport::Notifications.subscribe(/error\.flow_chat$/) { |event| ... }
|
|
274
|
+
|
|
275
|
+
# All session events
|
|
276
|
+
ActiveSupport::Notifications.subscribe(/^session\..*\.flow_chat$/) { |event| ... }
|
|
184
277
|
```
|
|
185
278
|
|
|
186
|
-
|
|
279
|
+
### Block vs Callable
|
|
187
280
|
|
|
188
281
|
```ruby
|
|
189
|
-
#
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
282
|
+
# Block form (simple)
|
|
283
|
+
ActiveSupport::Notifications.subscribe("message.sent.flow_chat") do |event|
|
|
284
|
+
puts event.payload
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
# Callable form (for complex subscribers)
|
|
288
|
+
class MessageLogger
|
|
289
|
+
def call(event)
|
|
290
|
+
# Access timing info
|
|
291
|
+
puts "Duration: #{event.duration}ms"
|
|
292
|
+
puts "Payload: #{event.payload}"
|
|
194
293
|
end
|
|
195
294
|
end
|
|
196
295
|
|
|
197
|
-
|
|
296
|
+
ActiveSupport::Notifications.subscribe("message.sent.flow_chat", MessageLogger.new)
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
## Testing
|
|
300
|
+
|
|
301
|
+
```ruby
|
|
198
302
|
class FlowInstrumentationTest < ActiveSupport::TestCase
|
|
199
|
-
|
|
303
|
+
def test_api_error_instrumentation
|
|
200
304
|
events = []
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
events << { name: name, payload: payload, duration: (finish - start) * 1000 }
|
|
305
|
+
|
|
306
|
+
ActiveSupport::Notifications.subscribe("api.error.flow_chat") do |event|
|
|
307
|
+
events << event
|
|
205
308
|
end
|
|
206
|
-
|
|
207
|
-
#
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
assert_equal
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
assert_equal "welcome_flow", events[0][:payload][:flow_name]
|
|
309
|
+
|
|
310
|
+
# Trigger the error condition
|
|
311
|
+
client.send_message_with_invalid_token
|
|
312
|
+
|
|
313
|
+
assert_equal 1, events.size
|
|
314
|
+
assert_equal :whatsapp, events.first.payload[:platform]
|
|
315
|
+
ensure
|
|
316
|
+
ActiveSupport::Notifications.unsubscribe("api.error.flow_chat")
|
|
215
317
|
end
|
|
216
|
-
end
|
|
318
|
+
end
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
## Production Recommendations
|
|
322
|
+
|
|
323
|
+
1. **Subscribe early** - Set up subscriptions in initializers before requests arrive
|
|
324
|
+
2. **Keep handlers fast** - Use async processing for slow operations (logging to external services)
|
|
325
|
+
3. **Filter events** - Only subscribe to events you need to avoid overhead
|
|
326
|
+
4. **Use structured logging** - Log payloads as JSON for easier querying
|
|
327
|
+
5. **Set up alerting** - Configure alerts on `api.error` events for proactive monitoring
|