Skip to content

Instantly share code, notes, and snippets.

@tatac1
Created June 26, 2013 09:32
Show Gist options
  • Save tatac1/5866108 to your computer and use it in GitHub Desktop.
Save tatac1/5866108 to your computer and use it in GitHub Desktop.
{-# LANGUAGE OverloadedStrings #-}
module ParsecRFC3164
(
parseRFC3164
, RFC3164
, PRI
, Severity
, Facility
, Header
, Msg
, pri
, header
, msg
, content
) where
import SyslogPriority
import Control.Applicative
import Data.Attoparsec as P
import qualified Data.Attoparsec.Char8 as P8
import Data.Attoparsec.Char8 (char8, endOfLine)
import Data.ByteString
{-
http://www.amris.co.jp/netdocs/rfc3164_j.html
http://www.ietf.org/rfc/rfc3164.txt
-}
-- SYSLOG-MSG = PRI SP HEADER SP MSG
data RFC3164 = RFC3164
{
pri :: !PRI
, header :: !Header
, msg :: !Msg
} deriving (Show)
-- | parser for rfc 3164
-- >>> :set -XOverloadedStrings
-- >>> parseTest parseRFC3164 "<34>Oct 11 22:14:15 mymachine su: 'su root' failed for lonvick on /dev/pts/8\n "
-- Done " " RFC3164 {pri = PRI {severity = CRITICAL, facility = AUTH}, header = Header {timestamp = "Oct 11 22:14:15", hostName = "mymachine"}, msg = Msg {tag = Just "su", procId = Nothing, content = " 'su root' failed for lonvick on /dev/pts/8"}}
--
-- >>> parseTest parseRFC3164 "<0>Oct 22 10:52:12 scapegoat apache[2321]: 10.1.2.3 sched[0]: That's All Folks!\n "
-- Done " " RFC3164 {pri = PRI {severity = EMERGENCY, facility = KERN}, header = Header {timestamp = "Oct 22 10:52:12", hostName = "scapegoat"}, msg = Msg {tag = Just "apache", procId = Just 2321, content = " 10.1.2.3 sched[0]: That's All Folks!"}}
--
parseRFC3164 :: Parser RFC3164
parseRFC3164 = do
pri' <- parsePRI
header' <- parseHeader
msg' <- parseMsg
return $ RFC3164 pri' header' msg'
-- PRI = '<' INT '>'
-- INT = 1*3 DIGIT ; range 0 .. 191
--
data PRI = PRI {
severity :: !Severity
, facility :: !Facility
} deriving (Show)
-- | facrity and priority check
--
-- Example:
--
-- >>> parseTest parsePRI "<0> "
-- Done " " PRI {severity = EMERGENCY, facility = KERN}
--
-- >>> parseTest parsePRI "<191> "
-- Done " " PRI {severity = DEBUG, facility = LOCAL7}
--
parsePRI :: Parser PRI
parsePRI = do
pri <- char8 '<' *> P8.decimal <* char8 '>'
return $ PRI (parseSeverity pri) (parseFac pri)
parseFac :: Int -> Facility
parseFac pri = facOfCode $ pri `div` 8
parseSeverity :: Int -> Severity
parseSeverity pri = priOfCode $ pri `mod` 8
-- HEADER = TIMESTAMP SP HOSTNAME
-- TIMESTAMP = MMM SP DD SP HH COL MM COL SS
-- MMM = 3PRINTUSASCII ; Jan Feb Mar Arp May Jun Jul Aug Sep Oct Nov Dev
-- DD = 2DIGIT ; <SP>1-28, <SP>1-29, <SP>1-30, <SP>1-31
-- HH = 2DIGIT ; 00-23
-- MM = 2DIGIT ; 00-59
-- HOSTNAME = IPv4 | IPv6 | PRINTUSASCII/DIGIT
-- IPv4 = 3NONZERO-DIGIT DOT 3NONZERO-DIGIT DOT 3NONZERO-DIGIT
-- IPv6 =
data Header = Header
{
timestamp :: !ByteString
, hostName :: !ByteString
} deriving (Show, Read)
-- | header parser check
--
-- example:
--
-- >>> parseTest parseHeader "Feb 12 19:27:01 hostname "
-- Done "" Header {timestamp = "Feb 12 19:27:01", hostName = "hostname"}
--
-- >>> parseTest parseHeader "Feb 2 19:27:01 hostname "
-- Done "" Header {timestamp = "Feb 2 19:27:01", hostName = "hostname"}
--
parseHeader :: Parser Header
parseHeader = do
timestamp <- P8.take 15 <* char8 ' '
hostName <- takeTill P8.isHorizontalSpace <* char8 ' '
return $ Header timestamp hostName
data Msg = Msg {
tag :: !(Maybe ByteString)
, procId :: !(Maybe Int)
, content :: !ByteString
} deriving (Show)
--MSG = TAG COL CONTENT
--TAG = 1*32 PROCNAME [ PID ]:
--PROCNAME = PRINTUSASCII / DIGIT
--TAGID = DIGIT
--CONTENT = PRINTUSASCII
--SP = %d32
--PRINTUSASCII = %d33-126
--NONZERO-DIGIT = %d49-57
--DIGIT = %d48 / NONZERO-DIGIT
--DOT = %46
--COL = %58
--[ = %91
--] = %93
-- | msg parser check
--
-- example:
--
-- >>> parseTest parseMsg "app[12]: message starting \n" -- "12]: message starting \n"
-- Done "" Msg {tag = Just "app", procId = Just 12, content = " message starting "}
--
-- >>> parseTest parseMsg "app: message starting \n" --"message starting \n"
-- Done "" Msg {tag = Just "app", procId = Nothing, content = " message starting "}
--
-- >>> parseTest parseMsg "[INSPECT] LAN2[out][200100] UDP 192.168.55.254:53 > 202.231.192.120:53 (2012/06/28 22:33:49) \n"
-- Done "" Msg {tag = Nothing, procId = Nothing, content = "[INSPECT] LAN2[out][200100] UDP 192.168.55.254:53 > 202.231.192.120:53 (2012/06/28 22:33:49) "}
--
parseMsg :: Parser Msg
parseMsg = do
tag <- Just <$> try (takeTill (\c -> inClass "[:" c)) <|> pure Nothing <$> char8 '['
pid <- Just <$> try (char8 '[' *> P8.decimal <* char8 ']' <* char8 ':') <|> pure Nothing <$> char8 ':'
content <- takeTill P8.isEndOfLine <* endOfLine
return $ Msg tag pid content
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment