WebSocket API
License Monitor provides WebSocket endpoints for real-time streaming of logs, metrics, and combined data.
Connection
Section titled “Connection”Endpoint URLs
Section titled “Endpoint URLs”| Endpoint | Purpose |
|---|---|
/ws/logs | Real-time log streaming |
/ws/metrics | Real-time metrics streaming |
/ws/combined | Combined logs and metrics |
Connection URL
Section titled “Connection URL”ws://hostname:8080/ws/logs?api_key=YOUR_API_KEYwss://hostname/ws/logs?api_key=YOUR_API_KEY # With TLSAuthentication
Section titled “Authentication”Authentication is required for WebSocket connections:
// Query parameter (recommended for WebSocket)const ws = new WebSocket('ws://localhost:8080/ws/logs?api_key=YOUR_API_KEY');
// Or with protocol headerconst ws = new WebSocket('ws://localhost:8080/ws/logs', ['api_key', 'YOUR_API_KEY']);Log Streaming
Section titled “Log Streaming”Connection
Section titled “Connection”const ws = new WebSocket('ws://localhost:8080/ws/logs?api_key=YOUR_API_KEY');
ws.onopen = () => { console.log('Connected to log stream');
// Subscribe to specific log levels ws.send(JSON.stringify({ action: 'subscribe', filter: { level: ['error', 'warn'], source: 'license-server' } }));};
ws.onmessage = (event) => { const log = JSON.parse(event.data); console.log(`[${log.level}] ${log.message}`);};
ws.onclose = (event) => { console.log('Disconnected:', event.code, event.reason);};
ws.onerror = (error) => { console.error('WebSocket error:', error);};Log Message Format
Section titled “Log Message Format”{ "type": "log", "timestamp": "2024-01-15T10:30:45.123Z", "level": "warn", "message": "License utilization at 90%", "source": "license-server", "metadata": { "server": "flexlm-01", "feature": "Maya_Complete", "utilization": 90 }}Filter Commands
Section titled “Filter Commands”// Subscribe with filterws.send(JSON.stringify({ action: 'subscribe', filter: { level: ['error', 'warn'], // Log levels to include source: 'license-server', // Source filter pattern: 'utilization' // Message pattern match }}));
// Update filterws.send(JSON.stringify({ action: 'filter', filter: { level: ['error'] }}));
// Clear filter (receive all logs)ws.send(JSON.stringify({ action: 'filter', filter: null}));Metrics Streaming
Section titled “Metrics Streaming”Connection
Section titled “Connection”const ws = new WebSocket('ws://localhost:8080/ws/metrics?api_key=YOUR_API_KEY');
ws.onopen = () => { // Configure metrics interval ws.send(JSON.stringify({ action: 'configure', interval: 5000 // Receive metrics every 5 seconds }));};
ws.onmessage = (event) => { const metrics = JSON.parse(event.data); updateDashboard(metrics);};Metrics Message Format
Section titled “Metrics Message Format”{ "type": "metrics", "timestamp": "2024-01-15T10:30:45.123Z", "system": { "cpu": { "usage": 34.5, "cores": 4 }, "memory": { "total": 8589934592, "used": 4294967296, "available": 4294967296, "percentage": 50.0 }, "disk": { "total": 107374182400, "used": 53687091200, "available": 53687091200, "percentage": 50.0 } }, "licenses": { "total": 100, "used": 75, "available": 25, "utilization": 75.0 }, "connections": { "active": 12, "total": 156 }}Combined Streaming
Section titled “Combined Streaming”Connection
Section titled “Connection”const ws = new WebSocket('ws://localhost:8080/ws/combined?api_key=YOUR_API_KEY');
ws.onmessage = (event) => { const message = JSON.parse(event.data);
switch (message.type) { case 'log': handleLog(message); break; case 'metrics': handleMetrics(message); break; case 'alert': handleAlert(message); break; }};Protocol Messages
Section titled “Protocol Messages”Client to Server
Section titled “Client to Server”| Action | Description | Payload |
|---|---|---|
subscribe | Subscribe with filter | { filter: {...} } |
filter | Update filter | { filter: {...} } |
configure | Configure stream | { interval: number } |
ping | Keep-alive ping | {} |
Server to Client
Section titled “Server to Client”| Type | Description |
|---|---|
log | Log entry |
metrics | Metrics snapshot |
alert | Alert notification |
pong | Ping response |
error | Error message |
Error Messages
Section titled “Error Messages”{ "type": "error", "code": "INVALID_FILTER", "message": "Invalid filter configuration", "timestamp": "2024-01-15T10:30:45.123Z"}Keep-Alive
Section titled “Keep-Alive”Ping/Pong
Section titled “Ping/Pong”// Send ping every 30 secondssetInterval(() => { if (ws.readyState === WebSocket.OPEN) { ws.send(JSON.stringify({ action: 'ping' })); }}, 30000);
// Handle pongws.onmessage = (event) => { const message = JSON.parse(event.data); if (message.type === 'pong') { console.log('Connection alive'); }};Connection Timeout
Section titled “Connection Timeout”The server closes idle connections after 5 minutes without activity. Send periodic pings to keep the connection alive.
Reconnection
Section titled “Reconnection”Automatic Reconnection
Section titled “Automatic Reconnection”class ReconnectingWebSocket { constructor(url, options = {}) { this.url = url; this.maxRetries = options.maxRetries || 10; this.baseDelay = options.baseDelay || 1000; this.maxDelay = options.maxDelay || 30000; this.retries = 0; this.connect(); }
connect() { this.ws = new WebSocket(this.url);
this.ws.onopen = () => { this.retries = 0; console.log('Connected'); };
this.ws.onclose = (event) => { if (event.code !== 1000) { this.reconnect(); } };
this.ws.onerror = () => { // Error will trigger onclose }; }
reconnect() { if (this.retries >= this.maxRetries) { console.error('Max retries exceeded'); return; }
const delay = Math.min( this.baseDelay * Math.pow(2, this.retries), this.maxDelay );
console.log(`Reconnecting in ${delay}ms...`);
setTimeout(() => { this.retries++; this.connect(); }, delay); }}Code Examples
Section titled “Code Examples”Python
Section titled “Python”import asyncioimport websocketsimport json
async def stream_logs(): uri = "ws://localhost:8080/ws/logs?api_key=YOUR_API_KEY"
async with websockets.connect(uri) as ws: # Subscribe to error logs await ws.send(json.dumps({ "action": "subscribe", "filter": {"level": ["error"]} }))
async for message in ws: log = json.loads(message) print(f"[{log['level']}] {log['message']}")
asyncio.run(stream_logs())package main
import ( "encoding/json" "log"
"github.com/gorilla/websocket")
func main() { url := "ws://localhost:8080/ws/logs?api_key=YOUR_API_KEY"
conn, _, err := websocket.DefaultDialer.Dial(url, nil) if err != nil { log.Fatal("dial:", err) } defer conn.Close()
// Subscribe conn.WriteJSON(map[string]interface{}{ "action": "subscribe", "filter": map[string]interface{}{ "level": []string{"error", "warn"}, }, })
for { _, message, err := conn.ReadMessage() if err != nil { log.Println("read:", err) return }
var logEntry map[string]interface{} json.Unmarshal(message, &logEntry) log.Printf("[%s] %s", logEntry["level"], logEntry["message"]) }}Next Steps
Section titled “Next Steps”- Server-Sent Events - Alternative streaming protocol
- REST API - Standard HTTP endpoints
- Error Codes - Error handling