Master Real-Time Log Streaming in Spring Boot with ResponseBodyEmitter
Learn how to implement efficient real-time log streaming in Spring Boot using ResponseBodyEmitter, covering its purpose, core methods, usage scenarios, step-by-step controller code, connection lifecycle management, comparison with SSE and Streaming, and important considerations for client support, timeouts, and thread safety.
Introduction
Spring Boot applications often need to push data to the client as it becomes available, such as progress bars, chat messages, stock updates, or server logs. The ResponseBodyEmitter class, introduced in Spring Framework 4.2, provides a simple way to achieve asynchronous HTTP responses.
What is ResponseBodyEmitter?
ResponseBodyEmitteris an interface that allows the server to send data to the client incrementally instead of waiting for the entire response to be ready. It uses HTTP chunked transfer encoding, so the Content‑Length header is not required.
Typical Use Cases
Long polling : Keep the connection open until new data arrives.
Server‑Sent Events (SSE) : Continuously push events to the client.
Streaming large payloads : Send files or real‑time data streams piece by piece.
Asynchronous processing : Return partial results while a long‑running task continues.
Real‑time Log Streaming Example
The following controller demonstrates how to expose a /api/log/stream endpoint that streams log entries every second.
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitter;
@RestController
@RequestMapping("/api/log")
public class LogController {
@GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public ResponseBodyEmitter streamLogs() {
ResponseBodyEmitter emitter = new ResponseBodyEmitter();
new Thread(() -> {
try {
while (true) {
String logEntry = getLatestLogEntry();
if (logEntry != null) {
emitter.send(logEntry);
}
// Check for new logs every second
Thread.sleep(1000);
}
} catch (Exception e) {
emitter.completeWithError(e);
}
}).start();
return emitter;
}
private String getLatestLogEntry() {
// Simulated log entry
return "2025-02-12 12:00:00 - INFO: User logged in successfully.";
}
}When the application runs and the client accesses /api/log/stream, the server pushes a new log line each second, producing a continuously updating view.
Core Methods of ResponseBodyEmitter
send(Object data): Sends a chunk of data to the client; can be called multiple times. complete(): Marks the response as finished and closes the connection. onTimeout(Runnable callback): Executes a callback when the connection times out. onCompletion(Runnable callback): Executes a callback after all data has been sent.
Working Principle
Traditional HTTP follows a request‑response model where the server waits until the whole payload is ready before sending it. ResponseBodyEmitter breaks this model by allowing the server to generate and send parts of the response as soon as they are available, similar to a relay race where each segment is handed off immediately.
It leverages HTTP chunked encoding: each chunk is prefixed with its length, enabling the client to process data incrementally without knowing the total size in advance.
Connection Lifecycle Management
After all data is transmitted, call complete() to signal the end of the stream. If an error occurs, use completeWithError() to close the connection and propagate the exception to the client. Proper lifecycle handling prevents resource leaks.
Comparison with Streaming and SSE
Streaming (OutputStream) : Gives maximum flexibility but requires manual stream closure and error handling.
Server‑Sent Events (SSE) : Uses the text/event-stream MIME type; ideal for event‑driven scenarios but requires client support for the SSE protocol.
ResponseBodyEmitter : Works with any HTTP client, integrates seamlessly with Spring MVC, and abstracts away low‑level stream management, making it a convenient choice for most real‑time use cases, including AI‑generated responses.
Precautions
Client compatibility : Most modern browsers support chunked transfer, but very old clients may have issues.
Timeout configuration : Prevent long‑running connections from exhausting resources. Example: emitter.onTimeout(() -> emitter.complete()); Thread safety : The send() method is thread‑safe, but you must manage the lifecycle of background threads to avoid leaks.
Connection closure : Always invoke complete() or completeWithError() when the task finishes; otherwise the connection may remain open indefinitely.
Conclusion
ResponseBodyEmitteris a lightweight streaming solution provided by Spring that significantly improves user experience in high‑concurrency, real‑time scenarios. By using its simple API, developers can quickly build features such as live progress updates, chat, stock tickers, or server‑log monitoring without dealing with low‑level HTTP stream handling.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Architect
Professional architect sharing high‑quality architecture insights. Topics include high‑availability, high‑performance, high‑stability architectures, big data, machine learning, Java, system and distributed architecture, AI, and practical large‑scale architecture case studies. Open to ideas‑driven architects who enjoy sharing and learning.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
