Events¶
Four event types capture the lifecycle of an API call. They map 1:1 to event_type values in api_log.
Event hierarchy¶
All three Java event classes extend org.springframework.context.ApplicationEvent:
ApplicationEvent
├── ApiCallInitiatedEvent → INITIATED row
├── ApiCallSuccessEvent → SUCCESS row
└── ApiCallErrorEvent → ERROR or RETRY_ERROR row
(depending on is_retry)
Note: there are only three event classes, but ApiCallErrorEvent produces either an ERROR or RETRY_ERROR row based on the isRetry flag — ApiLogService decides which event_type to write.
ApiCallInitiatedEvent¶
Fired immediately before the request is sent. The row gives you a "we tried to call X" record even if the call hangs forever or the JVM crashes before getting a response.
Constructor:
Fields available on the event:
| Field | Type | Notes |
|---|---|---|
request |
ApiRequest |
The endpoint + payload being sent |
eventTimestamp |
LocalDateTime |
When the event was constructed |
Resulting api_log row:
| Column | Value |
|---|---|
event_type |
INITIATED |
request_id |
UUID (generated by the publisher) |
endpoint |
from request.endpoint |
payload |
from request.payload (JSONB-serialized) |
response, status_code, error_message |
NULL |
retry_count |
0 (initial) |
is_retry |
false |
ApiCallSuccessEvent¶
Fired after a successful HTTP response (2xx). For non-RestApiClientUtil callers, you decide what "success" means.
Constructor:
Fields:
| Field | Type | Notes |
|---|---|---|
request |
ApiRequest |
The original request |
response |
ApiResponse |
data (body) + statusCode |
eventTimestamp |
LocalDateTime |
Resulting row:
| Column | Value |
|---|---|
event_type |
SUCCESS |
response |
from response.data (JSONB) |
status_code |
from response.statusCode |
error_message |
NULL |
ApiCallErrorEvent¶
Fired when the call fails. Used for both terminal errors and retry attempts — the isRetry flag flips the event_type between ERROR and RETRY_ERROR.
Constructor:
new ApiCallErrorEvent(
Object source,
ApiRequest request,
Throwable error,
int retryCount,
boolean isRetry
)
Fields:
| Field | Type | Notes |
|---|---|---|
request |
ApiRequest |
The request that failed |
error |
Throwable |
The exception |
retryCount |
int |
0 for first attempt, 1+ for retries |
isRetry |
boolean |
true for retry attempts, false for the first |
eventTimestamp |
LocalDateTime |
Resulting row:
| Column | Value |
|---|---|
event_type |
RETRY_ERROR if isRetry == true, else ERROR |
error_message |
{"type": "<exception class>", "message": "<message>"} |
status_code |
If the exception is an HttpClientErrorException / HttpServerErrorException, the upstream status. Otherwise NULL. |
retry_count |
from retryCount |
is_retry |
from isRetry |
Event type cheat sheet¶
event_type |
Source | Has response |
Has error_message |
Has status_code |
|---|---|---|---|---|
INITIATED |
ApiCallInitiatedEvent |
❌ | ❌ | ❌ |
SUCCESS |
ApiCallSuccessEvent |
✅ | ❌ | ✅ |
ERROR |
ApiCallErrorEvent (isRetry=false) |
❌ | ✅ | Sometimes |
RETRY_ERROR |
ApiCallErrorEvent (isRetry=true) |
❌ | ✅ | Sometimes |
Listening to events yourself¶
Beyond api-log's own listener, you can attach more — alerting, metrics, side-effects:
@Component
public class HighErrorRateAlerter {
@EventListener
@Async
public void onError(ApiCallErrorEvent event) {
if (event.getError() instanceof HttpServerErrorException &&
"/critical/endpoint".equals(event.getRequest().getEndpoint())) {
slackClient.notify("5xx on critical endpoint: " + event.getError().getMessage());
}
}
}
Multiple listeners on the same event run independently — api-log writes the row, your alerter sends Slack. Neither blocks the caller.
See also¶
- Schema reference — column shapes
- Publishing events guide — how to emit events from your own code
- Retry handling guide —
RETRY_ERRORin practice