What a LEEF / IBM QRadar line looks like
The LEEF sample below is fed verbatim into the engine to produce every parser on this page.
LEEF:2.0|IBM|QRadar|7.5.0|NewEvent|^|devTime=1783085000000^src=203.0.113.66^dst=192.0.2.30^sev=9^cat=IPS^msg=Log4j RCE attempt blocked at perimeter
LEEF:2.0|IBM|QRadar|7.5.0|NewEvent|^|devTime=1783085062000^src=203.0.113.90^dst=192.0.2.31^sev=7^cat=Malware^msg=Suspicious outbound beacon detected Detected fields
The engine classified this sample as kv and consolidated 12 fields across 2 lines. Fields marked literal were identical on every sample line, so they are baked into the pattern as anchors rather than captured.
- leef_version : number · literal
- leef_vendor : literal · literal
- leef_product : literal · literal
- leef_device_version : literal · literal
- leef_event_id : literal · literal
- leef_delimiter : literal · literal
- devtime : timestamp
- src : ipv4
- dst : ipv4
- sev : number
- cat : literal
- msg : literal
Regex (named capture groups)
# sample: LEEF:2.0|IBM|QRadar|7.5.0|NewEvent|^|devTime=1783085000000^src=203.0.113.66^dst=192.0.2.30^sev=9^cat=IPS^msg=Log4j RCE attempt blocked at perimeter
# groups: devtime=1783085000000, src=203.0.113.66, dst=192.0.2.30, sev=9, cat=IPS, msg=Log4j RCE attempt blocked at perimeter
^LEEF:2\.0\|IBM\|QRadar\|7\.5\.0\|NewEvent\|\^\|devTime=(?<devtime>\d+)\^src=(?<src>\d{1,3}(?:\.\d{1,3}){3})\^dst=(?<dst>\d{1,3}(?:\.\d{1,3}){3})\^sev=(?<sev>-?\d+(?:\.\d+)?)\^cat=(?<cat>[A-Za-z]+)\^msg=(?<msg>(?:[A-Za-z]+\d+[A-Za-z]+ [A-Za-z]+ [A-Za-z]+ [A-Za-z]+ [A-Za-z]+ [A-Za-z]+|[A-Za-z]+ [A-Za-z]+ [A-Za-z]+ [A-Za-z]+))$ Grok pattern (Logstash / Elastic)
# custom patterns
LEEF_EPOCH \d{10}(?:\d{3})?
LEEF:2\.0\|IBM\|QRadar\|7\.5\.0\|NewEvent\|\^\|devTime=%{LEEF_EPOCH:devtime}\^src=%{IPV4:src}\^dst=%{IPV4:dst}\^sev=%{NUMBER:sev}\^cat=%{DATA:cat}\^msg=%{GREEDYDATA:msg} - note kv-structured input — consider the Logstash kv filter instead of (or after) grok
- note constant field "leef_version" embedded as literal anchor "2.0" (varying=false)
- note custom patterns emitted — save the '# custom patterns' block to a file in your patterns_dir
Wazuh decoder (OS_Regex XML)
<!--
Generated by LogForge - Wazuh decoder (OS_Regex dialect, not PCRE)
sample: LEEF:2.0|IBM|QRadar|7.5.0|NewEvent|^|devTime=1783085000000^src=203.0.113.66^dst=192.0.2.30^sev=9^cat=IPS^msg=Log4j RCE attempt blocked at perimeter
test with: /var/ossec/bin/wazuh-logtest
-->
<decoder name="leef-kv">
<prematch>^LEEF:\d+.\d+\|\w+\|\w+\|\w+.\w+.\w+\|\w+\|</prematch>
</decoder>
<decoder name="leef-kv">
<parent>leef-kv</parent>
<regex offset="after_parent">\|devTime=(\d+)</regex>
<order>devtime</order>
</decoder>
<decoder name="leef-kv">
<parent>leef-kv</parent>
<regex offset="after_parent">src=(\d+.\d+.\d+.\d+)</regex>
<order>srcip</order>
</decoder>
<decoder name="leef-kv">
<parent>leef-kv</parent>
<regex offset="after_parent">dst=(\d+.\d+.\d+.\d+)</regex>
<order>dstip</order>
</decoder>
<decoder name="leef-kv">
<parent>leef-kv</parent>
<regex offset="after_parent">sev=(\d+)</regex>
<order>sev</order>
</decoder>
<decoder name="leef-kv">
<parent>leef-kv</parent>
<regex offset="after_parent">cat=(\w+)</regex>
<order>cat</order>
</decoder>
<decoder name="leef-kv">
<parent>leef-kv</parent>
<regex offset="after_parent">msg=(\.+)</regex>
<order>msg</order>
</decoder>
- note constant field "leef_version" skipped (identical in every line)
- note constant field "leef_vendor" skipped (identical in every line)
- note constant field "leef_product" skipped (identical in every line)
- note constant field "leef_device_version" skipped (identical in every line)
- note constant field "leef_event_id" skipped (identical in every line)
- note constant field "leef_delimiter" skipped (identical in every line)
- note field "src": '^' delimiter cannot start an OS_Regex (it would anchor); matching bare "src=" instead
- note field "src" mapped to Wazuh conventional field "srcip"
- note field "dst": '^' delimiter cannot start an OS_Regex (it would anchor); matching bare "dst=" instead
- note field "dst" mapped to Wazuh conventional field "dstip"
- note field "sev": '^' delimiter cannot start an OS_Regex (it would anchor); matching bare "sev=" instead
- note field "cat": '^' delimiter cannot start an OS_Regex (it would anchor); matching bare "cat=" instead
- note field "msg": '^' delimiter cannot start an OS_Regex (it would anchor); matching bare "msg=" instead
- note field "msg": free-text capture (\.+) anchored at end of line — OS_Regex quantifiers are greedy, keep this field last
- note kv fields are extracted by same-named sibling decoders (offset="after_parent"), so per-line field order/absence is tolerated — the shared name is what makes Wazuh evaluate every sibling
- note decoder order and prematch specificity may need site-specific tuning (other decoders in your ruleset can shadow these) — validate with /var/ossec/bin/wazuh-logtest
rsyslog template / liblognorm rulebase
version=2
# leef — liblognorm v2 rulebase (generated by LogForge)
# Usage with rsyslog (mmnormalize runs liblognorm):
# module(load="mmnormalize")
# action(type="mmnormalize" rulebase="/etc/rsyslog.d/leef.rb" useRawMsg="on")
# Literal "%" is escaped as "%%"; raw tabs are written as \x09.
rule=leef:LEEF:2.0|IBM|QRadar|7.5.0|NewEvent|^|devTime=%devtime:number%^src=%src:ipv4%^dst=%dst:ipv4%^sev=%sev:number%^cat=%cat:char-to{"extradata":"^"}%^msg=%msg:rest%
- note kv structure: rsyslog offers mmfields (fast, fixed single-char separator, untyped) and mmnormalize (this rulebase, typed fields + literal anchors); mmnormalize was chosen for typed extraction
- note chosen parser types: devtime=number, src=ipv4, dst=ipv4, sev=number, cat=char-to(^), msg=rest
FAQ
- What is the delimiter in a LEEF log?
- It depends on the version and the header. LEEF 1.0 always uses a tab between attributes. LEEF 2.0 lets the emitter choose and declares the choice in an optional fifth header field — in LEEF:2.0|IBM|QRadar|7.5.0|NewEvent|^| the ^ means attributes are caret-delimited. Always read that field before splitting; assuming tabs is the classic LEEF parsing bug.
- How does LEEF differ from CEF?
- Both are header + key=value envelopes for SIEM ingestion, but LEEF is IBM QRadar-oriented while CEF comes from ArcSight. LEEF has a version marker and a configurable attribute delimiter declared in the header; CEF has a six-field header and a space-separated extension split on key boundaries. Their header layouts and key dictionaries differ, so they need separate parsers.
- Why is the LEEF devTime a big number instead of a date?
- That is epoch time in milliseconds — 1783085000000 is a Unix timestamp times 1000. LEEF permits devTime either as an epoch value or as a formatted string with an accompanying devTimeFormat attribute. To make it human-readable, divide by 1000 and convert from Unix seconds, or parse the declared format if one is supplied.
- Which LEEF attributes should I map for QRadar correlation?
- The normalized keys QRadar understands: src and dst (IP addresses), srcPort and dstPort, sev (1–10 severity), cat (category), usrName (user), and devTime (event time). Mapping your source onto these standard keys is what lets QRadar apply built-in rules; msg carries the free-text description for context.
Try it on your own LEEF / IBM QRadar lines
Paste a few real lines, review the detected fields, and copy whichever format your stack needs. Free, no account, nothing uploaded.
Open this sample in LogForge →