Log Parser - Event Trigger Script - MikroTik Script RouterOS
This script will parse a log buffer, and take specified action if a log entry has been added.
There is an official "detect new log entry" script Manual:Scripting-examples#Detect_new_log_entry, however, this will only detect the last log entry. If multiple log entries have occurred since last check, you would only receive the last one.
This script will solve that problem by first reading the log buffer to internal memory, clearing the log buffer, then parsing the log entries (now loaded in memory). This allows for a very accurate log reading, and insures you will not miss multiple log entries before the next script execution. Also, it will produce a standard time format when reading the logs (mmm/dd/yyyy hh:mm:ss), meaning no extra conversion is necessary when reading log entries from the current day, or current year. For more details on this visit: [[1]] This script is also very fast, as it uses print as-value instead of find to read the log buffer.
First, you must create a memory log buffer to hold the information you want to parse.
/system logging action add memory-lines=100 memory-stop-on-full=no name=logParse target=memory
Next, create the topics to store in the newly created log buffer.
/system logging add action=logParse disabled=no prefix="" topics=system,info
/system logging add action=logParse disabled=no prefix="" topics=system,error,critical
/system logging add action=logParse disabled=no prefix="" topics=dhcp
Log Parser Script
:local logBuffer "logParse"
:local logParserScript "Log-Parser-Script"
:global logParseVar ""
:local loglastparsetime
:local loglastparsemessage
:local findindex
:local property
:local value
:local logEntryTopics
:local logEntryTime
:local logEntryMessage
:local curDate
:local curMonth
:local curDay
:local curYear
:local clearedbuf
:local lines
:set curDate [/system clock get date]
:set curMonth [:pick [:tostr $curDate] 0 3]
:set curDay [:pick [:tostr $curDate] 4 6]
:set curYear [:pick [:tostr $curDate] 7 11]
:set clearedbuf 0
:foreach rule in=[/log print as-value where buffer=($logBuffer)] do={
:if ($clearedbuf = 0) do={
/system logging action {
:set lines [get ($logBuffer) memory-lines]
set ($logBuffer) memory-lines 1
set ($logBuffer) memory-lines $lines
}
:set clearedbuf 1
}
:set logEntryTime ""
:set logEntryTopics ""
:set logEntryMessage ""
:foreach item in=[:toarray $rule] do={
:set findindex [:find [:tostr $item] "="]
:set property [:tostr [:pick [:tostr $item] 0 $findindex]]
:set value [:tostr [:pick [:tostr $item] ($findindex + 1) [:len [:tostr $item]]]]
:if ([:tostr $property] = "time") do={ :set logEntryTime $value }
:if ([:tostr $property] = "topics") do={ :set logEntryTopics $value }
:if ([:tostr $property] = "message") do={ :set logEntryMessage $value }
}
:set findindex [:find [:tostr $logEntryTime] " "]
:if ([:len $findindex] = 0) do={
:set logEntryTime ($curMonth . "/" . $curDay . "/" . $curYear . " " . \
[:tostr $logEntryTime])
}
:if ($findindex = 6) do={
:set logEntryTime ([:pick [:tostr $logEntryTime] 0 $findindex] . "/" . $curYear . \
[:pick [:tostr $logEntryTime] $findindex [:len [:tostr $logEntryTime]]])
}
:if ($findindex = 3) do={
:set logEntryTime ([:pick [:tostr $logEntryTime] 0 $findindex] . "/" . $curDay . "/" . $curYear . \
[:pick [:tostr $logEntryTime] $findindex [:len [:tostr $logEntryTime]]])
}
:if ($logEntryTime = $loglastparsetime && $logEntryMessage = $loglastparsemessage) do={
} else={
:set logParseVar ($logEntryTime . "," . $logEntryTopics . "," . $logEntryMessage)
/system script run ($logParserScript)
:set loglastparsetime $logEntryTime
:set loglastparsemessage $logEntryMessage
}
}
Parser Action Script
Next, create the parser action script that will run after each log entry is read.
:global logParseVar
:local logTime [:pick [:toarray $logParseVar] 0]
:local logTopics [:pick [:toarray $logParseVar] 1]
:local logMessage [:pick [:toarray $logParseVar] 2]
:set logParseVar ""
:local ruleop
:local loguser
:local logsettings
:local findindex
:local tmpstring
:if ([:find [:tostr $logMessage] "login failure"] != "") do={
:beep frequency=90 length=500ms
:beep frequency=130 length=500ms
:put ("A login failure has occured. Take some action")
}
:if ([:find [:tostr $logMessage] "logged in"] != "") do={
:beep frequency=900 length=300ms
:beep frequency=1300 length=200ms
:put ("A user has logged in.")
}
:if ([:tostr $logTopics] = "system;info") do={
:set ruleop ""
:if ([:len [:find [:tostr $logMessage] "changed "]] > 0) do={ :set ruleop "changed" }
:if ([:len [:find [:tostr $logMessage] "added "]] > 0) do={ :set ruleop "added" }
:if ([:len [:find [:tostr $logMessage] "removed "]] > 0) do={ :set ruleop "removed" }
:if ([:len $ruleop] > 0) do={
:set tmpstring $logMessage
:set findindex [:find [:tostr $tmpstring] [:tostr $ruleop]]
:set tmpstring ([:pick [:tostr $tmpstring] 0 $findindex] . \
[:pick [:tostr $tmpstring] ($findindex + [:len [:tostr $ruleop]]) [:len [:tostr $tmpstring]]])
:set findindex [:find [:tostr $tmpstring] " by "]
:set loguser ([:pick [:tostr $tmpstring] ($findindex + 4) [:len [:tostr $tmpstring]]])
:set logsettings [:pick [:tostr $tmpstring] 0 $findindex]
:put ($loguser . " " . $ruleop . " " . $logsettings . " configuration. We should take a backup now.")
}
}
:if ([:tostr $logTopics] = "dhcp;info") do={
:set ruleop ""
:if ([:len [:find [:tostr $logMessage] "assigned "]] > 0) do={ :set ruleop "assigned" }
:if ([:len [:find [:tostr $logMessage] "deassigned "]] > 0) do={ :set ruleop "deassigned" }
:if ([:len $ruleop] > 0) do={
:if ($ruleop = "assigned") do={
:put ("A new dhcp lease has been assigned. Check the DHCP IP Pool addresses")
}
:if ($ruleop = "deassigned") do={
:put ("A dhcp lease has been removed. Remove the host-name from static DNS")
}
}
}
Now, you have a way to trigger events based on log entries (and any event in RouterOS that is logged).