Server-Sent Events
Server-Sent Events (SSE) provides a simple, HTTP-based approach to real-time streaming from License Monitor.
Overview
Section titled “Overview”SSE advantages over WebSocket:
| Feature | SSE | WebSocket |
|---|---|---|
| Protocol | HTTP | WebSocket |
| Direction | Server → Client | Bidirectional |
| Reconnection | Automatic | Manual |
| Browser support | Native EventSource | Native WebSocket |
| Proxy compatibility | Excellent | May require config |
| Complexity | Simple | More complex |
Endpoints
Section titled “Endpoints”| Endpoint | Purpose |
|---|---|
/stream/logs | Real-time log events |
/stream/metrics | Real-time metrics |
/stream/combined | Combined event stream |
Log Streaming
Section titled “Log Streaming”Basic Connection
Section titled “Basic Connection”const eventSource = new EventSource( 'http://localhost:8080/stream/logs?api_key=YOUR_API_KEY');
eventSource.onopen = () => { console.log('Connected to log stream');};
eventSource.onmessage = (event) => { const log = JSON.parse(event.data); console.log(`[${log.level}] ${log.message}`);};
eventSource.onerror = (error) => { console.error('SSE error:', error); if (eventSource.readyState === EventSource.CLOSED) { console.log('Connection closed'); }};Named Events
Section titled “Named Events”const eventSource = new EventSource( 'http://localhost:8080/stream/logs?api_key=YOUR_API_KEY');
// Listen to specific event typeseventSource.addEventListener('log', (event) => { const log = JSON.parse(event.data); handleLog(log);});
eventSource.addEventListener('error_log', (event) => { const log = JSON.parse(event.data); handleErrorLog(log);});
eventSource.addEventListener('warning_log', (event) => { const log = JSON.parse(event.data); handleWarningLog(log);});With Filters
Section titled “With Filters”// Filter by log levelconst url = new URL('http://localhost:8080/stream/logs');url.searchParams.set('api_key', 'YOUR_API_KEY');url.searchParams.set('level', 'error,warn');url.searchParams.set('source', 'license-server');
const eventSource = new EventSource(url.toString());Metrics Streaming
Section titled “Metrics Streaming”Connection
Section titled “Connection”const eventSource = new EventSource( 'http://localhost:8080/stream/metrics?api_key=YOUR_API_KEY&interval=5000');
eventSource.addEventListener('metrics', (event) => { const metrics = JSON.parse(event.data); updateDashboard(metrics);});Query Parameters
Section titled “Query Parameters”| Parameter | Description | Default |
|---|---|---|
api_key | API key for authentication | Required |
interval | Metrics interval (ms) | 10000 |
include | Metrics to include | all |
Event Format
Section titled “Event Format”SSE Message Structure
Section titled “SSE Message Structure”event: logid: 1705312245123data: {"type":"log","level":"warn","message":"High utilization"}
event: metricsid: 1705312250000data: {"type":"metrics","cpu":34.5,"memory":62.1}Log Event
Section titled “Log Event”{ "type": "log", "id": "log-1705312245123", "timestamp": "2024-01-15T10:30:45.123Z", "level": "warn", "message": "License utilization at 90%", "source": "license-server", "metadata": { "server": "flexlm-01", "feature": "Maya_Complete" }}Metrics Event
Section titled “Metrics Event”{ "type": "metrics", "timestamp": "2024-01-15T10:30:50.000Z", "system": { "cpu": 34.5, "memory": 62.1, "disk": 45.0 }, "licenses": { "total": 100, "used": 75, "utilization": 75.0 }}Reconnection
Section titled “Reconnection”Automatic Reconnection
Section titled “Automatic Reconnection”EventSource automatically reconnects on connection loss:
const eventSource = new EventSource( 'http://localhost:8080/stream/logs?api_key=YOUR_API_KEY');
eventSource.onerror = (error) => { if (eventSource.readyState === EventSource.CONNECTING) { console.log('Reconnecting...'); } else if (eventSource.readyState === EventSource.CLOSED) { console.log('Connection closed, will not reconnect'); }};Custom Reconnection
Section titled “Custom Reconnection”class RobustEventSource { constructor(url, options = {}) { this.url = url; this.maxRetries = options.maxRetries || 10; this.retryDelay = options.retryDelay || 3000; this.retries = 0; this.handlers = new Map(); this.connect(); }
connect() { this.eventSource = new EventSource(this.url);
this.eventSource.onopen = () => { this.retries = 0; this.emit('open'); };
this.eventSource.onerror = () => { if (this.eventSource.readyState === EventSource.CLOSED) { this.reconnect(); } };
this.eventSource.onmessage = (event) => { this.emit('message', event); }; }
reconnect() { if (this.retries >= this.maxRetries) { this.emit('maxRetries'); return; }
this.retries++; const delay = this.retryDelay * Math.pow(2, this.retries - 1);
setTimeout(() => { this.connect(); }, Math.min(delay, 30000)); }
on(event, handler) { if (!this.handlers.has(event)) { this.handlers.set(event, []);
if (event !== 'open' && event !== 'message' && event !== 'maxRetries') { this.eventSource.addEventListener(event, (e) => { this.emit(event, e); }); } } this.handlers.get(event).push(handler); }
emit(event, data) { const handlers = this.handlers.get(event) || []; handlers.forEach(handler => handler(data)); }
close() { this.eventSource.close(); }}Last-Event-ID
Section titled “Last-Event-ID”SSE supports resuming from the last received event:
// Server sends event IDs// event: log// id: 1705312245123// data: {...}
// On reconnection, browser sends Last-Event-ID header// The server can resume from that pointServer Response
Section titled “Server Response”The server uses Last-Event-ID to send missed events:
GET /stream/logs HTTP/1.1Last-Event-ID: 1705312245123
HTTP/1.1 200 OKContent-Type: text/event-stream
:resume from 1705312245123
event: logid: 1705312245124data: {"type":"log",...}Code Examples
Section titled “Code Examples”React Hook
Section titled “React Hook”import { useEffect, useState, useCallback } from 'react';
interface SSEOptions { onMessage?: (data: any) => void; onError?: (error: Event) => void; eventTypes?: string[];}
function useSSE(url: string, options: SSEOptions = {}) { const [isConnected, setIsConnected] = useState(false); const [lastMessage, setLastMessage] = useState<any>(null);
useEffect(() => { const eventSource = new EventSource(url);
eventSource.onopen = () => { setIsConnected(true); };
eventSource.onerror = (error) => { setIsConnected(false); options.onError?.(error); };
const handleMessage = (event: MessageEvent) => { const data = JSON.parse(event.data); setLastMessage(data); options.onMessage?.(data); };
if (options.eventTypes?.length) { options.eventTypes.forEach(type => { eventSource.addEventListener(type, handleMessage); }); } else { eventSource.onmessage = handleMessage; }
return () => { eventSource.close(); }; }, [url]);
return { isConnected, lastMessage };}
// Usagefunction LogViewer() { const { isConnected, lastMessage } = useSSE( 'http://localhost:8080/stream/logs?api_key=KEY', { eventTypes: ['log', 'error_log'], onMessage: (log) => console.log('New log:', log), } );
return ( <div> <span>{isConnected ? 'Connected' : 'Disconnected'}</span> <pre>{JSON.stringify(lastMessage, null, 2)}</pre> </div> );}Python
Section titled “Python”import sseclientimport requests
url = "http://localhost:8080/stream/logs"headers = {"Accept": "text/event-stream"}params = {"api_key": "YOUR_API_KEY", "level": "error,warn"}
response = requests.get(url, headers=headers, params=params, stream=True)client = sseclient.SSEClient(response)
for event in client.events(): if event.event == "log": print(f"Log: {event.data}") elif event.event == "metrics": print(f"Metrics: {event.data}")# Stream logscurl -N -H "Accept: text/event-stream" \ "http://localhost:8080/stream/logs?api_key=YOUR_API_KEY"
# Stream with filterscurl -N -H "Accept: text/event-stream" \ "http://localhost:8080/stream/logs?api_key=YOUR_API_KEY&level=error"Comparison with WebSocket
Section titled “Comparison with WebSocket”| Use Case | Recommended |
|---|---|
| Simple log viewing | SSE |
| Dashboard metrics | SSE |
| Interactive queries | WebSocket |
| High-frequency updates | WebSocket |
| Behind strict proxies | SSE |
| Mobile apps | Either |
Next Steps
Section titled “Next Steps”- WebSocket API - Bidirectional streaming
- REST API - Standard HTTP endpoints
- Error Codes - Error handling