api-log¶
Event-driven API call logging for Spring Boot. Async event pipeline + PostgreSQL JSONB. Log every outbound call without slowing the request path.
At a glance¶
Drop the starter on your classpath and your service automatically logs every outbound HTTP call — request, response, status code, errors, retries — to PostgreSQL. The HTTP call doesn't wait for the log write; events are dispatched asynchronously.
@Service
public class UserService {
private final RestApiClientUtil api;
public UserService(RestApiClientUtil api) {
this.api = api;
}
public User createUser(User newUser) {
// The HTTP call is synchronous and returns immediately.
// Three log rows land in api_log behind the scenes: INITIATED, then SUCCESS or ERROR.
return api.postSyncTyped("/api/users", newUser, User.class);
}
}
A single call writes to the api_log table:
SELECT event_type, endpoint, status_code, timestamp FROM api_log ORDER BY id DESC LIMIT 3;
event_type | endpoint | status_code | timestamp
------------+--------------+-------------+----------------------
SUCCESS | /api/users | 201 | 2026-05-18 10:02:14
INITIATED | /api/users | | 2026-05-18 10:02:14
Bodies (payload, response, error_message) are stored as JSONB — queryable with ->, ->>, and GIN indexes. No more "we don't know what we sent to that vendor".
What you get¶
-
Non-blocking by design
Log writes happen on a separate thread via
ApplicationEventPublisher. Your HTTP call returns the moment the response arrives — the log row is persisted after. -
PostgreSQL JSONB storage
Request bodies, response bodies, and error details are stored as JSONB. Index with GIN, query with
->,->>,jsonb_path_query. Schema-less where it matters, structured where you need it. -
Retry-aware
Integrates with Spring Retry. Each failed retry produces its own
RETRY_ERRORrow withretry_count— full timeline of every attempt. -
Virtual Threads ready
Designed for Java 21+ async. Compatible with
spring.threads.virtual.enabled=true— low memory footprint, high concurrency. -
Drop-in starter
Auto-configuration registers
ApiEventListener,ApiLogService, andRestApiClientUtilbehind@ConditionalOnMissingBean. Override any of them by declaring your own. -
Production-tested
Used in Devslab's own SaaS infrastructure before open-sourcing. 31 tests including PostgreSQL integration tests via Testcontainers.
When to use this¶
Reach for api-log when you need durable, queryable history of outbound API calls — for debugging vendor integrations, compliance audits, billing reconciliation, or producing customer-facing usage reports.
Reach for something else when you only need ephemeral observability (use OpenTelemetry + a trace backend) or fire-and-forget transport logs (just log to stdout and ship to a log aggregator).
Architecture¶
Caller code
↓
RestApiClientUtil (or your own HTTP client)
↓ publishEvent
ApplicationEventPublisher
↓ @EventListener (async)
ApiEventListener
↓
ApiLogService
↓
ApiLogRepository (JPA)
↓
PostgreSQL (api_log · JSONB columns)
Every call produces at least two rows: INITIATED (fired immediately) and a terminal row (SUCCESS, ERROR, or RETRY_ERROR). All four event types correlate via request_id, so you can reconstruct the full timeline of any call with a single query.
Next steps¶
- Install the starter — Maven and Gradle coordinates
- Quickstart — get your first log row in 5 minutes
- Using
RestApiClientUtil— bundled HTTP client - Publishing events manually — bring your own HTTP client
- Reference / Schema —
api_logtable structure