Last active
June 15, 2016 13:04
-
-
Save NeQuissimus/d682c8183c7b686b1c07 to your computer and use it in GitHub Desktop.
Playing with parboiled2 to parse router logs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import org.parboiled2._ | |
import scala.util.{ Success, Failure } | |
import java.net.{ InetAddress, Inet4Address } | |
import java.time.{ LocalDateTime => LDT } | |
import java.time.format.{ DateTimeFormatter => DTF } | |
object Main extends App { | |
import Logs._ | |
List(l1, l2, l3, l4, l5) foreach { l => | |
println(l) | |
val p = LogParser(l) | |
p.InputLine.run() match { | |
case Success(v) => println(s"Expression output: $v") | |
case Failure(e: ParseError) => println(s"Expression invalid: ${p.formatError(e)} || Stack: ${p.valueStack}") | |
case Failure(e) => println(s"Error: $e") | |
} | |
println() | |
} | |
} | |
object Logs { | |
val l1 = "Jan 1 00:13:44 10.30.26.1 Got new client [18:B4:30:23:AA:67] associated from BAND24G-1.1 (2.4 Ghz)" | |
val l2 = "Jan 11 00:13:53 10.30.26.2 DHCP: Server receive DISCOVER from 18:b4:30:23:aa:67." | |
val l3 = "Jan 1 22:13:53 10.30.26.3 DHCP: Server sending OFFER of 10.30.26.115 for static DHCP client." | |
val l4 = "Jan 3 18:13:53 10.30.26.4 DHCP: Server receive REQUEST from 18:b4:30:23:aa:67." | |
val l5 = "Feb 22 12:13:53 10.30.26.5 DHCP: Server sending ACK to 10.30.26.115. (Lease time = -1)" | |
} | |
case class LogParser(val input: ParserInput) extends Parser { | |
def InputLine = rule { DateMonth3NoYear ~ Whitespace ~ HHMMSS ~ Whitespace ~ IPv4 ~ Whitespace ~ (( | |
"DHCP: Server " ~ ( | |
"receive " ~ ( | |
"DISCOVER from " ~ MAC ~ "." ~ EOI ~> os.linux.dhcp.Discover | |
| "REQUEST from " ~ MAC ~ "." ~ EOI ~> os.linux.dhcp.Request | |
) | |
| "sending " ~ ( | |
"OFFER of " ~ IPv4 ~ " for static DHCP client." ~ EOI ~> os.linux.dhcp.Offer | |
| "ACK to " ~ IPv4 ~ ". (Lease time = " ~ Number ~ ")" ~ EOI ~> os.linux.dhcp.Ack | |
) | |
)) | |
| (Anything ~ EOI ~> os.linux.UnknownMessage) | |
)} | |
def DateMonth3NoYear = rule { capture(Month3 ~ Whitespace ~ DayOfMonth) } | |
def Month3 = rule { "Jan" | "Feb" | "Mar" | "Apr" | "May" | "Jun" | "Jul" | "Aug" | "Sep" | "Oct" | "Nov" | "Dec" } | |
def DayOfMonth = rule { Digits ~> (i => test(i >= 1 && i <= 31)) } | |
def ZeroTo23 = rule { Digits ~> (i => test(i >= 0 && i <= 23)) } | |
def ZeroTo59 = rule { Digits ~> (i => test(i >= 0 && i <= 59)) } | |
def HHMMSS = rule { capture(ZeroTo23 ~ ':' ~ ZeroTo59 ~ ':' ~ ZeroTo59) } | |
def IPv4 = rule { capture(4.times(IPv4Octet).separatedBy('.')) } | |
def IPv4Octet = rule { Digits ~> (i => test(i >= 0 && i <= 255)) } | |
def MAC = rule { capture(6.times(2.times(CharPredicate.HexDigit)).separatedBy(':')) } | |
def Number = rule { capture(optional('-') ~ DigitsStr) } | |
def Digit = rule { capture(CharPredicate.Digit) ~> (_.toInt) } | |
def Digits = rule { capture(DigitsStr) ~> (_.toInt) } | |
def DigitsStr = rule { oneOrMore(CharPredicate.Digit) } | |
def Whitespace = rule { zeroOrMore(' ') } | |
def Anything = rule { capture(zeroOrMore(CharPredicate.Printable)) } | |
} | |
trait ParsedDate { self: {val dateStr: String; val timeStr: String} => | |
lazy val date = LDT.parse("2014 " + dateStr + ' ' + timeStr, DTF.ofPattern("yyyy MMM [ ]d HH:mm:ss")) | |
} | |
trait ParsedIp { self: {val ipStr: String} => | |
lazy val ip: InetAddress = InetAddress.getByName(ipStr) | |
} | |
package os.linux.dhcp { | |
case class Ack(dateStr: String, timeStr: String, ipStr: String, targetIpStr: String, leaseStr: String) extends ParsedDate with ParsedIp { | |
lazy val targetIp = InetAddress.getByName(targetIpStr) | |
lazy val lease = leaseStr.toLong | |
override def toString = s"dhcp.Ack[$date, $ip, $targetIp, $lease]" | |
} | |
case class Discover(dateStr: String, timeStr: String, ipStr: String, macStr: String) extends ParsedDate with ParsedIp { | |
override def toString = s"dhcp.Discover[$date, $ip, $macStr]" | |
} | |
case class Offer(dateStr: String, timeStr: String, ipStr: String, offeredIpStr: String) extends ParsedDate with ParsedIp { | |
lazy val offeredIp = InetAddress.getByName(offeredIpStr) | |
override def toString = s"dhcp.Offer[$date, $ip, $offeredIp]" | |
} | |
case class Request(dateStr: String, timeStr: String, ipStr: String, macStr: String) extends ParsedDate with ParsedIp { | |
override def toString = s"dhcp.Request[$date, $ip, $macStr]" | |
} | |
} | |
package os.linux { | |
case class UnknownMessage(dateStr: String, timeStr: String, ipStr: String, rest: String) extends ParsedDate with ParsedIp { | |
override def toString = s"UnknownMessage[$date, $ip, $rest]" | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
[info] Running Main
Jan 1 00:13:44 10.30.26.1 Got new client [18:B4:30:23:AA:67] associated from BAND24G-1.1 (2.4 Ghz)
Expression output: UnknownMessage[2014-01-01T00:13:44, /10.30.26.1, Got new client [18:B4:30:23:AA:67] associated from BAND24G-1.1 (2.4 Ghz)]
Jan 11 00:13:53 10.30.26.2 DHCP: Server receive DISCOVER from 18:b4:30:23:aa:67.
Expression output: dhcp.Discover[2014-01-11T00:13:53, /10.30.26.2, 18:b4:30:23:aa:67]
Jan 1 22:13:53 10.30.26.3 DHCP: Server sending OFFER of 10.30.26.115 for static DHCP client.
Expression output: dhcp.Offer[2014-01-01T22:13:53, /10.30.26.3, /10.30.26.115]
Jan 3 18:13:53 10.30.26.4 DHCP: Server receive REQUEST from 18:b4:30:23:aa:67.
Expression output: dhcp.Request[2014-01-03T18:13:53, /10.30.26.4, 18:b4:30:23:aa:67]
Feb 22 12:13:53 10.30.26.5 DHCP: Server sending ACK to 10.30.26.115. (Lease time = -1)
Expression output: dhcp.Ack[2014-02-22T12:13:53, /10.30.26.5, /10.30.26.115, -1]