Robuste Protokollierung mit Lua
Für Qualitätssicherung, Fehleranalyse und nachvollziehbare Prozessdokumentation ist ein gutes Logging unverzichtbar. In diesem Beitrag zeigen wir, wie Sie mit Dobot Lua ein automatisches Logging-System bauen.
Lokale Protokollierung
ⓘ Hinweis:
Dieser Ansatz bietet ein schnelles, robustes Logging ohne zusätzliche Abhängigkeiten. Dies ist ideal für Tests und lokale Protokollierung direkt auf dem Roboter.
Für produktive Umgebungen empfehlen wir jedoch, das Logging an eine externe Stelle wie einen TCP-Server oder zentralen Log-Service auszulagern. Das vorgehen hierzu finden sie weiter unten, unter: Server-Protokollierung
Bei Fragen zur Integration oder Erweiterung unterstützen wir Sie gern per E-Mail unter backoffice@needful-apps.de oder telefonisch unter 0561 2207 6342.
Projektstruktur in DobotStudio Pro
| Datei | Rolle | Beschreibung |
|---|---|---|
Global.lua | Konfiguration | Globale Werte und (Logging-)Funktionen |
src0.lua | Hauptprogramm | Roboteraktionen und Bewegungen |
Global.lua
Neben den programmspezifischen Konfigurationen benötigen wir in der Global.lua folgenden Code:
ROBOT_ID = "MG400_001"
LOG_PATH = "/mnt/sdcard/logs/"
MAX_LOG_FILES = 10
LOG_FILE = LOG_PATH .. "robot_log.txt"
LOCK_FILE = LOG_FILE .. ".lock"
MAX_WAIT_MS = 1000
-- Prüft, ob Datei existiert
function file_exists(path)
local f = io.open(path, "r")
if f then f:close() return true end
return false
end
-- Initialisiert Logging: Verzeichnis, .lock entfernen, Rotation
function init_logging()
os.execute("mkdir -p " .. LOG_PATH)
-- Alte Lock-Dateien entfernen
for file in io.popen('ls "' .. LOG_PATH .. '"'):lines() do
if file:match("%.lock$") then
os.remove(LOG_PATH .. file)
end
end
-- Logrotation (maximale Anzahl Dateien)
local files = {}
for file in io.popen('ls -t "' .. LOG_PATH .. '"'):lines() do
if not file:match("%.lock$") then
table.insert(files, file)
end
end
for i = MAX_LOG_FILES + 1, #files do
os.remove(LOG_PATH .. files[i])
end
end
-- Loggt eine Nachricht mit Lock-Datei zum Schutz vor parallelem Zugriff
function log(message)
local waited = 0
while file_exists(LOCK_FILE) and waited < MAX_WAIT_MS do
Sleep(10)
waited = waited + 10
end
if file_exists(LOCK_FILE) then
print("WARNUNG: Logging blockiert durch Lock-Datei – Eintrag übersprungen.")
return
end
-- Lock setzen
local lock = io.open(LOCK_FILE, "w")
if lock then lock:write("locked\n") lock:close() end
-- Schreiben
local timestamp = os.date("%Y-%m-%d %H:%M:%S")
local f = io.open(LOG_FILE, "a")
if f then
f:write("[" .. timestamp .. "] " .. ROBOT_ID .. " - " .. message .. "\n")
f:close()
end
-- Lock wieder entfernen
os.remove(LOCK_FILE)
end
| Parameter | Typ | Beispielwert | Bedeutung |
|---|---|---|---|
ROBOT_ID | string | "MG400_001" | Eindeutige Kennung des Roboters: Nützlich, wenn mehrere Roboter im Einsatz sind. Wird in jedem Logeintrag mitgeschrieben. |
LOG_PATH | string | "/mnt/sdcard/logs/" | Speicherort für die Logdateien. Muss vorhanden sein oder per Skript erstellt werden. Beachten Sie hierbei, dass der Speicherort, ausgehend vom Roboter (Linux) ist. |
MAX_LOG_FILES | number | 10 | Maximale Anzahl an Logdateien, die im Ordner behalten werden. Ältere Dateien werden automatisch gelöscht (Logrotation). |
LOG_FILE | string | "robot_log.txt" | Baut den Pfad zur Log-Datei |
LOCK_FILE | string | ".lock" | Baut den Pfad zur Log-Lock-Datei |
MAX_WAIT_MS | number | 1000 | Gibt an, wie lange maximal versucht wird, zu warten, bis ein .lock entfernt wird. |
src0.lua
-- Muss einmal mit Start des Programms aufgerufen werden
init_logging()
-- Logging kann nun an beliebiger stelle verwendet werden
log("Starte Robotersequenz")
Server-Protokollierung
Für dieses Beispiel benutzen wir den rust-basierten TCP-Logserver von needful-apps.
Überblick
Statt (oder zusätzlich zu) einer lokalen Datei schickt der Roboter seine Meldungen an einen zentralen Log-Server.
Der Log-Server nimmt Logs per TCP an, speichert sie in einer Datenbank und stellt sie zentral bereit:
- Live-Ansicht im Browser (Web-Dashboard)
- Filter nach Roboter, Station, Fehlertyp
- Historie und QS-Unterlagen für alle Zellen
- Grafana-Anbindung (Loki-kompatible API)
- Mehrere Datenbanken möglich (SQLite, PostgreSQL, MySQL, Supabase)
Diese Variante eignet sich für:
- Fertigung mit mehreren Stationen / Robotern
- Traceability („Welches Teil wurde wann verarbeitet?“)
- Alarmierung / Monitoring
- Qualitätsnachweis gegenüber Kunden

Konfiguration für Remote-Logging (Global.lua)
Wir ergänzen in Global.lua zusätzliche Konstanten für den Log-Server:
-- Remote Logging Konfiguration
REMOTE_LOG_ENABLED = true -- zentralen Log-Server benutzen?
REMOTE_LOG_HOST = "192.168.10.50" -- IP/Hostname des Log-Servers
REMOTE_LOG_PORT = 9090 -- TCP-Port des Log-Servers
REMOTE_CLIENT_ID = "MG400_001" -- eindeutige Kennung dieses Roboters
REMOTE_API_KEY = "secure-random-key" -- API-Key aus dem Admin-Panel des Log-Servers
REMOTE_SOCKET_TIMEOUT_MS = 200 -- max. Verbindungswartezeit in Millisekunden
Wichtig:
- Diese Werte sind pro Roboter unterschiedlich.
REMOTE_LOG_ENABLEDkann auffalsegesetzt werden, wenn der Roboter offline getestet wird.
TCP-Verbindung zum Log-Server herstellen
Konzeptionelle Hilfsfunktion (vereinfacht im Lua-Stil mit Socket-API):
function connect_log_server()
if not REMOTE_LOG_ENABLED then
return nil, "remote disabled"
end
local socket = require("socket") -- abhängig von Firmware / Umgebung
local tcp = assert(socket.tcp())
-- Kurzer Timeout, Roboter darf nicht hängen
tcp:settimeout(REMOTE_SOCKET_TIMEOUT_MS / 1000.0)
local ok, err = tcp:connect(REMOTE_LOG_HOST, REMOTE_LOG_PORT)
if not ok then
print("WARNUNG: Konnte Log-Server nicht erreichen: " .. tostring(err))
return nil, err
end
-- 1. Auth schicken
local auth_payload = string.format(
'{"client_id":"%s","api_key":"%s"}\n',
REMOTE_CLIENT_ID,
REMOTE_API_KEY
)
tcp:send(auth_payload)
-- 2. Antwort lesen (optional, nicht blockierend kritisch)
local resp, recv_err = tcp:receive("*l")
-- Wenn resp nil ist, ignorieren wir das still. Roboterlauf geht vor Komfort.
return tcp, nil
end
Hinweis:
Falls die eingesetzte Roboter-Firmware keine direkten TCP-Sockets zulässt, übernimmt diese Rolle ein kleines Edge-Gerät, das seriell/Modbus vom Roboter Nachrichten bekommt und dann an den Log-Server weiterleitet. Die Log-Server-Seite bleibt identisch.
Remote-Log senden
Diese Funktion schickt einen einzelnen Eintrag an den Server.
Sie ist bewusst fehlertolerant: Wenn der Server nicht erreichbar ist, läuft der Roboter einfach weiter.
-- level: "INFO", "WARN", "ERROR", ...
-- message: Freitext
-- meta: optionale Tabelle { key = value, ... } mit Zusatzinformationen
function log_remote(level, message, meta)
if not REMOTE_LOG_ENABLED then
return
end
local ok, err = pcall(function()
local tcp, cerr = connect_log_server()
if not tcp then
-- Server nicht erreichbar -> wir geben auf, aber stoppen nicht die Produktion
return
end
-- Metadaten als einfaches JSON bauen (ohne Sonderzeichen-Escaping)
local meta_json = "{}"
if meta ~= nil then
local parts = {}
for k,v in pairs(meta) do
table.insert(parts,
string.format('"%s":"%s"', tostring(k), tostring(v))
)
end
meta_json = "{" .. table.concat(parts, ",") .. "}"
end
local timestamp = os.date("%Y-%m-%dT%H:%M:%S")
local payload = string.format(
'{"level":"%s","message":"%s","metadata":%s,"robot_id":"%s","ts":"%s"}\n',
level,
tostring(message),
meta_json,
ROBOT_ID,
timestamp
)
tcp:send(payload)
-- optional: kurzes Ack lesen, aber nicht hart blockieren
tcp:settimeout(0.05)
tcp:receive("*l")
tcp:close()
end)
if not ok then
print("WARNUNG: Remote-Logging fehlgeschlagen: " .. tostring(err))
end
end
Wichtig:
- Die Funktion blockiert den Roboter nicht dauerhaft.
Im schlimmsten Fall wird der Eintrag einfach nicht gesendet. - Jeder Eintrag enthält
robot_id,level,message,metadata,ts.
Genau diese Felder tauchen später im Dashboard und in Grafana auf.
Verwendung in src0.lua (Remote-Logging)
-- Beispielhafter Produktionsablauf
-- Lokale Initialisierung nur nötig, wenn auch lokal geloggt wird
init_logging()
-- Schrittstart dokumentieren
log_remote("INFO", "Starte Robotersequenz", {job="init", step="boot"})
-- Bauteil greifen
log_remote("INFO", "Greife Bauteil", {part_id="GPU-4711", station="pick"})
-- Warnung melden (z. B. Vakuumdruck zu niedrig)
log_remote("WARN", "Vakuumdruck niedrig", {vacuum_kpa="-38.2"})
-- Fehlerfall
log_remote("ERROR", "Bauteil nicht korrekt ausgerichtet", {part_id="GPU-4711"})
Diese Logs erscheinen dann zentral:
- im Web-Dashboard des Log-Servers (Port 8080),
- und in Grafana über die Loki-Datasource (Filter z. B.
{client_id="MG400_001"}oder{level="ERROR"}).
Was der Log-Server bietet
Kurz zusammengefasst (aus Sicht der Fertigung / QS):
- Zentrale Sicht für alle Roboter
Alle Einträge landen an einer Stelle. - Live-Ansicht
Neue Logs erscheinen in Echtzeit im Browser (WebSocket). - Filterbarkeit
Nach Roboter, Level (WARN/ERROR), Station, Werkstück-ID. - Grafana-Integration (Loki-kompatibel)
- Zeiträume vergleichen
- Fehlerhäufigkeit als Graph
- Alarme definieren
- Datenbank-Backends
- SQLite für Test / PoC
- PostgreSQL/MySQL für Produktion
- Supabase für managed Cloud-Varianten
- Sicherheit durch API-Key
Jeder Roboter bekommt eine eigeneclient_id+api_key. Kann zentral gesperrt werden.
Mehr davon
Mehr zu den Möglichkeiten mit Lua in DobotStudio Pro erfahren Sie in unserem Kurs: Dobot Lua – Schulung | Roboter programmieren mit Lua

