onderka.com

Live-Anzeige von Logdateien mit Websocket, Apache/Nginx und Javascript

Logfile -> Websocket -> HTML & JS -> Browser

Ein Log (hier das Protokoll von DNSmasq) auf einem Server soll live angezeigt werden, ohne jedes Mal mit SSH zu verbinden und ein tail -f ausführen zu müssen.

  • Service: Startet Websocketd
  • Bash-Skript: Zeigt letzte N Zeilen des Logs
  • Websocketd: Startet Skript und stellt seine Ausgabe mit Socket zur Verfügung
  • HTML: Beinhaltet <div> mit Zeilen der Logdatei
  • Javascript: Aktualisiert das <div> live bei neuen Websocket-Meldungen

Auch hier gilt: Dies ist ein Rohgerüst ohne Fehlerbehandlung. "Works for me", macht was draus.

Websocket

websocketd (Sourcen, Download) kann die Ausgabe eines beliebigen Skripts oder Binaries, das auf stdout schreiben kann, via Websocket bereit stellen.

Bash Skript

Das Skript /pfad/zum/skript/service_ws_dhcplog.sh zur Anzeige der letzten N Zeilen startet einen Websocket auf 127.0.0.1, Port 8000:

#!/bin/bash

# Open a websocket on 127.0.0.1:8000
websocketd --address=127.0.0.1 --port=8000 --loglevel=info tail -F -n 100 /var/log/dnsmasq.log

Service

Einen neuen System-Dienst anlegen — hier unter Ubuntu/Debian. Als Namen z.B. ws-dnsmasq verwenden. Der Inhalt der Dienst-Konfiguration, /etc/systemd/system/ws-dnsmasq.service:

[Unit]
Description=Websocket Port 8000: DNSmasq-logfile
After=network.target
After=systemd-user-sessions.service
After=network-online.target

[Service]
User=root
WorkingDirectory=/pfad/zum/skript/
Type=simple
ExecStart=/pfad/zum/skript/service_ws_dhcplog.sh
TimeoutSec=30
Restart=on-failure
RestartSec=30
StartLimitInterval=350
StartLimitBurst=10

[Install]
WantedBy=multi-user.target

Der Service startet das Bash-Skript als Dienst. Aktivieren/bekanntmachen mit systemctl daemon-reload, Starten mit service ws-dnsmasq restart.

Apache

Aktivieren der Proxy-Module:

# Proxy - https://httpd.apache.org/docs/2.4/mod/mod_proxy.html
a2enmod proxy
# HTTP-Proxy - https://httpd.apache.org/docs/2.4/mod/mod_proxy_http.html
a2enmod proxy_http
# Proxy zu Backend-Websocket - https://httpd.apache.org/docs/2.4/mod/mod_proxy_wstunnel.html
a2enmod proxy_wstunnel

Folgendes in die Konfiguration des (HTTPS-)Virtual Hosts einfügen:

# Websocket
# --------------------------------------------------------------------
RewriteEngine On
RewriteCond %{HTTP:Upgrade} websocket               [NC]
RewriteRule /(.*)           ws://127.0.0.1:8000/$1  [P]
ProxyPass /ws               ws://127.0.0.1:8000/

Nginx

In den (HTTPS-)Server Block einfügen:

# Websocket
# ----------------------------------------------------------------------------
location /ws/ {
    proxy_pass http://127.0.0.1:8000;
    proxy_http_version 1.1;
    proxy_set_header Host 127.0.0.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "Upgrade";
    add_header X-location websocket always;
}

HTML

Das Dokument benötigt jQuery sowie das weiter unten stehende Javascript:

...
<!-- Datum/Uhrzeit aktuell -->
<span id='clock'></span>
<!-- Status Websocket -->
<span id='websocket_status'></span>
<!-- Letzte Message vor N Sekunde(n) -->
<span id='websocket_ago'></span>
<!-- Datum/Uhrzeit letzte Message -->
<span id='websocket_timestamp'></span>
<!-- Timestamp letzte Message, unsichtbar -->
<span id='websocket_last_timestamp' style='display: none;'></span>
<!-- Anzeige Logdatei -->
<pre id='livelog' class='livelog' style='height: 600px;'></pre>
<!-- jQuery -->
<script src="jquery-3.6.0.min.js"></script>
<!-- JavaScript -->
<script src='ws.js'></script>
...

Javascript

Das Skript ws.js. Anpassen von vhost.domain.com.

Nix 127.0.0.1! Dieses Skript läuft im Browser, Hostnamen des Servers eintragen!

/* Ziel-WS auf vhost.domain.com:8000 - ws:// bei HTTP, wss:// bei HTTPS */
var socket_address = 'wss://vhost.domain.com/ws/';
var message_counter = 0;

/* Hallo! */
console.log('Connecting to websocket '+socket_address);

/* WS initialisieren */
var webSocket = new WebSocket(socket_address);

/* WS Error */
webSocket.onerror = function(event) {
    onError(event)
};

/* WS Open */
webSocket.onopen = function(event) {
    onOpen(event)
};

/* WS Message */
webSocket.onmessage = function(event) {
    onMessage(event)
};

/* Event Message */
function onMessage(event) {
    /* Timestamp jetzt */
    var timeStamp = $.now();
    /* Datum jetzt */
    var date = new Date(timeStamp);
    /* Zähler +1 */
    message_counter = message_counter+1;
    /* Feld Status */
    $('#websocket_status').html('Message received. Total: '+message_counter);
    /* Feld Datum */
    $('#websocket_timestamp').html(date);
    /* Feld Timestamp */
    $('#websocket_last_timestamp').html(timeStamp);
    /* Feld Messages: Anhängen */
    $('#livelog').append('<br />' + event.data);
    /* Feld Messages: Scrollen */
    $('#livelog').animate({
        scrollTop: $('#livelog').get(0).scrollHeight}, 10);
    $('#livelog').scrollLeft();
    /* Konsole */
    console.log('New Message (#' + message_counter + ')');
}

/* Event Open */
function onOpen(event) {
    $('#websocket_status').html('Opened');
    console.log('WebSocket: Opened');
}

/* Event Error */
function onError(event) {
    $('#websocket_status').html('Error :'+JSON.stringify(event));
    console.log('Websocket: Error '+JSON.stringify(event));
}

/* Event Ready */
$(document).ready(function() {
  clockUpdate();
  /* Jede Sekunde */
  setInterval(clockUpdate, 1000);
})

/* Uhrzeit und vergangene Zeit seit letztem Empfang */
function clockUpdate() {
    /* Jetzt */
    var date = new Date();
    /* Timestamp letzter Empfyng */
    var last_message = $('#websocket_last_timestamp').html();
    /* Uhr */
    $('#clock').html(Date());
    /* Differenz Uhrzeit letzte Message zu jetzt in Sekunden */
    var ago_difference = (date - last_message) / 1000;
    /* Vor X Sekunden */
    $('#websocket_ago').html(parseInt(ago_difference));
};

HTTP & HTTPS

Bei Zugriff über HTTP und HTTPS muss mit einer Hilfskonstruktion eventuell auch das Protokoll für den Websocket im Javascript (ws/wss) angepasst werden. Tipp:

var protocol = 'ws://'; 
if (window.location.protocol === 'https:') {
    protocol = 'wss://';
}
var socket_address = protocol+ "vhost.domain.com/ws/";  

Ergebnis

Ansicht mit einem gefilterten dnsmasq.log (dhcp-script = ...):

Andere Seiten unter 'Computer und Netzwerk'