CEF / LEEF formatting (ArcSight, QRadar)
Operator runbook for emitting IdentityMesh log events as
Common Event Format (CEF, ArcSight) or Log Event Extended
Format (LEEF, IBM QRadar) text envelopes. Most modern SIEMs
prefer JSON (use siem-webhook.md) or
RFC 5424 syslog (use siem-syslog.md) — but
ArcSight ESM and QRadar’s older DSMs still expect their own
text formats and won’t parse anything else without a custom
mapper.
The formatters ship as in-process Serilog ITextFormatter
implementations: they don’t open sockets, they only shape the
per-event line. Compose them with any sink — file, syslog,
HTTP — and let the sink handle transport.
When to choose CEF / LEEF
- Your SIEM is ArcSight ESM or an ArcSight SmartConnector fronting another collector. CEF is its native dialect.
- Your SIEM is IBM QRadar with a DSM that requires LEEF (most modern QRadar DSMs prefer LEEF over CEF).
- Compliance / detection rules at your SIEM are pinned to CEF / LEEF field names and refactoring the rules to JSON isn’t on the table.
- You need a stable, parseable, text-line format on disk that a Splunk Universal Forwarder or syslog tail can pick up without a JSON parser.
If neither ArcSight nor QRadar is in the picture, prefer the JSON webhook or RFC 5424 syslog feed — both are easier to debug and more forward-compatible.
What the formatters emit
CEF (Common Event Format) v0
The standard CEF prefix:
CEF:Version|DeviceVendor|DeviceProduct|DeviceVersion|SignatureID|Name|Severity|Extension
IdentityMesh fills it in like:
CEF:0|IdentityMesh|Engine|1.0.0|IdentityMesh.Infrastructure.Sql.SqlAdminAuditStore|Admin audit row recorded: {Action} -> {StatusCode}|4|rt=2026-04-25T11:32:18.4210000Z msg=Admin audit row recorded: POST /api/connectors -> 201 ActorUpn=alice@corp Action=POST /api/connectors TargetKind=Connector TargetId=8e6b8c44-... StatusCode=201 CorrelationId=0HMV-... MachineName=im-engine-01 InstanceName=IdentityMeshEngine
Header field meanings:
| Slot | IdentityMesh value |
|---|---|
| Version | 0 (CEF v0, the only published version) |
| DeviceVendor | IdentityMesh |
| DeviceProduct | Engine |
| DeviceVersion | Assembly version of IdentityMesh.Service |
| SignatureID | The EventId property if present, else SourceContext, else IdentityMesh.Generic |
| Name | The Serilog message template text (parameterised, not rendered) |
| Severity | Mapped from Serilog level (see below) |
| Extension | Space-separated key=value pairs from LogEvent.Properties |
LEEF (Log Event Extended Format) v2.0
The LEEF prefix:
LEEF:Version|Vendor|Product|Version|EventID|Delim|Extension
IdentityMesh fills it in like:
LEEF:2.0|IdentityMesh|Engine|1.0.0|IdentityMesh.Infrastructure.Sql.SqlAdminAuditStore|x09|devTime=2026-04-25T11:32:18.4210000Z[TAB]devTimeFormat=yyyy-MM-ddTHH:mm:ss.fffffffZ[TAB]sev=4[TAB]msg=Admin audit row recorded: POST /api/connectors -> 201[TAB]ActorUpn=alice@corp[TAB]Action=POST /api/connectors[TAB]...
The fifth header slot (x09) declares the extension delimiter
as ASCII tab (0x09) — the QRadar-preferred default. Extension
fields are tab-separated key=value pairs.
| Slot | IdentityMesh value |
|---|---|
| Version | 2.0 |
| Vendor | IdentityMesh |
| Product | Engine |
| Version | Assembly version of IdentityMesh.Service |
| EventID | The EventId property if present, else SourceContext |
| Delim | x09 (ASCII tab — QRadar default) |
| Extension | Tab-separated key=value pairs |
Severity scale (CEF + LEEF sev)
Both formats use the 0-10 scale. Serilog levels map as:
| Serilog level | CEF / LEEF severity |
|---|---|
Verbose | 0 |
Debug | 2 |
Information | 4 |
Warning | 6 |
Error | 8 |
Fatal | 10 |
LEEF stamps this on the sev extension field; CEF stamps it on
the dedicated severity header slot.
Extension keys IdentityMesh emits
The formatters pass through every Serilog property as a
sanitised key=value pair. The keys you’ll see in steady state
include:
| Key | Provenance |
|---|---|
rt (CEF) / devTime (LEEF) | UTC timestamp of the event |
msg | Rendered message (template + properties resolved) |
SourceContext | Logger category — pivots to component / module |
MachineName | Stamped via Enrich.WithMachineName() |
ThreadId | Stamped via Enrich.WithThreadId() |
InstanceName | Sync-engine instance name |
CycleId | Sync-engine run id (present during connector cycles) |
RequestId | ASP.NET Core trace id (present on Admin API events) |
ActorUpn | Audit-row actor — present on *AuditStore log lines |
Action | HTTP verb + route template — present on admin-audit lines |
TargetKind | Audit-row target type — present on admin-audit lines |
TargetId | Audit-row target id — present on admin-audit lines |
StatusCode | HTTP status — present on admin-audit lines |
CorrelationId | Audit-row correlation id — pivots to IM_AdminAudit.CorrelationId |
ChangeType | Object-audit row change type — present on IM_ObjectAudit lines |
Keys are sanitised to ASCII alphanumeric / underscore — any
property name with characters outside that set has the offending
characters stripped, not replaced. Values escape \, =,
\r, \n (CEF) and additionally \t (LEEF) per the format
specs.
Enabling CEF or LEEF
The Sync Engine ships two samples:
<install dir>\appsettings.Cef.example.json
<install dir>\appsettings.Leef.example.json
Copy the Serilog.Using array entry and the WriteTo block
into the live appsettings.json (or, preferred for a
host-specific override, appsettings.Production.json). Restart
the IdentityMesh service.
CEF to a daily-rolled file
{
"Serilog": {
"Using": [
"Serilog.Sinks.Console",
"Serilog.Sinks.File"
],
"WriteTo": [
{
"Name": "File",
"Args": {
"path": "logs/identitymesh-cef-.log",
"rollingInterval": "Day",
"retainedFileCountLimit": 30,
"shared": true,
"formatter": "IdentityMesh.Service.Telemetry.Cef.IdentityMeshCefFormatter, IdentityMesh.Service"
}
}
]
}
}
A Splunk Universal Forwarder or ArcSight File Reader
SmartConnector picks up the resulting identitymesh-cef-*.log
file directly.
LEEF over TCP syslog to QRadar
{
"Serilog": {
"Using": [
"Serilog.Sinks.Console",
"Serilog.Sinks.File",
"Serilog.Sinks.Syslog"
],
"WriteTo": [
{
"Name": "TcpSyslog",
"Args": {
"host": "qradar.corp.example",
"port": 514,
"appName": "IdentityMesh",
"framingType": "OCTET_COUNTING",
"format": "RFC5424",
"facility": "Local0",
"messageFormatter": {
"type": "IdentityMesh.Service.Telemetry.Cef.IdentityMeshLeefFormatter, IdentityMesh.Service"
}
}
}
]
}
}
The syslog sink wraps the LEEF text frame in an RFC 5424 envelope before transport. QRadar’s LEEF DSM ignores the syslog wrap and parses the LEEF body. Test the field mapping on a small sample before flipping the rule that gates incident generation.
CEF to the webhook endpoint
{
"Serilog": {
"Using": [
"Serilog.Sinks.Console",
"Serilog.Sinks.File",
"Serilog.Sinks.Http"
],
"WriteTo": [
{
"Name": "Http",
"Args": {
"requestUri": "https://siem.corp.example/identitymesh/cef",
"textFormatter": "IdentityMesh.Service.Telemetry.Cef.IdentityMeshCefFormatter, IdentityMesh.Service",
"logEventsInBatchLimit": 100,
"period": "00:00:02"
}
}
]
}
}
Useful when the SIEM-side ingest is a custom collector that prefers HTTPS over a syslog socket.
Composition with a sink — pick one
| Sink | When |
|---|---|
Serilog.Sinks.File | Splunk UF tails the file. ArcSight File Reader Connector. Local forensic archive. Default and simplest. |
Serilog.Sinks.Syslog | QRadar LEEF DSM via TCP / TLS syslog. ArcSight Syslog SmartConnector. |
Serilog.Sinks.Http | Custom collector that wants the formatted line over HTTPS rather than syslog. |
Serilog.Sinks.Console | Local debugging / containerised stdout pickup. Not for production volume. |
Combining formatters is also fine — emit JSON to a webhook
and CEF to a separate file in the same Serilog block. They’re
independent WriteTo entries.
Sample lines
CEF (information-level admin audit row):
CEF:0|IdentityMesh|Engine|1.0.0|IdentityMesh.Infrastructure.Sql.SqlAdminAuditStore|Admin audit row recorded: {Action} -> {StatusCode}|4|rt=2026-04-25T11:32:18.4210000Z msg=Admin audit row recorded: POST /api/connectors -> 201 ActorUpn=alice@corp Action=POST /api/connectors TargetKind=Connector TargetId=8e6b8c44-... StatusCode=201 CorrelationId=0HMV-... MachineName=im-engine-01 InstanceName=IdentityMeshEngine SourceContext=IdentityMesh.Infrastructure.Sql.SqlAdminAuditStore
CEF (warning-level run failure):
CEF:0|IdentityMesh|Engine|1.0.0|IdentityMesh.Service.IdentityMeshEngine|Connector run {ConnectorName} failed after {Elapsed} ms|6|rt=2026-04-25T11:42:01.0030000Z msg=Connector run AD-East failed after 4128 ms ConnectorName=AD-East Elapsed=4128 CycleId=cf0d9a91 reason=LdapException
LEEF (information-level admin audit row, tab-delimited):
LEEF:2.0|IdentityMesh|Engine|1.0.0|IdentityMesh.Infrastructure.Sql.SqlAdminAuditStore|x09|devTime=2026-04-25T11:32:18.4210000Z[TAB]devTimeFormat=yyyy-MM-ddTHH:mm:ss.fffffffZ[TAB]sev=4[TAB]msg=Admin audit row recorded: POST /api/connectors -> 201[TAB]ActorUpn=alice@corp[TAB]Action=POST /api/connectors[TAB]TargetKind=Connector[TAB]TargetId=8e6b8c44-...[TAB]StatusCode=201
([TAB] shown for clarity — actual character is 0x09.)
LEEF (error-level run failure):
LEEF:2.0|IdentityMesh|Engine|1.0.0|IdentityMesh.Service.IdentityMeshEngine|x09|devTime=2026-04-25T11:42:01.0030000Z[TAB]sev=8[TAB]msg=Connector run AD-East failed after 4128 ms[TAB]ConnectorName=AD-East[TAB]Elapsed=4128[TAB]CycleId=cf0d9a91[TAB]exceptionType=LdapException[TAB]exceptionMessage=Server unavailable
Spec verification
- CEF: ArcSight Common Event Format white paper, currently
CEF:0— header pipes are literal|, header values escape\and|with\, extension keys are alphanumeric + underscore, extension values escape\and=with\, CR/LF in values are escaped to\r/\n. - LEEF: IBM Security QRadar LEEF v2.0 specification — header
pipes are literal
|, the fifth header slot declares the extension delimiter (x09for tab,^etc), extension keys follow the same restrictions as CEF, values additionally escape the chosen delimiter.
The IdentityMesh formatters implement both spec rules
deterministically; embedded |, =, \, \r, \n, \t
in property values are escaped before the line is written.
Failure modes
- Header field too long. CEF / LEEF do not formally cap the
extension block, but some SmartConnector parsers truncate at
~8 KB. The IdentityMesh formatters do not pre-truncate;
reduce volume by tightening
restrictedToMinimumLevelon the sink rather than relying on parser-side truncation. - SourceContext missing on a custom log call. The formatter
falls back to
IdentityMesh.Genericfor the SignatureID / EventID slot. Detection rules pinned to specificSourceContextvalues should default-allowIdentityMesh.Genericrather than reject it. - Property name collision with header field.
rt,devTime,msg,sevare reserved by the formatters. A Serilog property of the same name on aLogContextpush emits twice — the formatter’s own value first, then the property’s. Pick distinct property names if this matters. - Property values containing newlines. Escaped to
\n/\rper spec; the line stays single-line on disk / in transport. ArcSight + QRadar parsers handle the escape correctly.
Operational notes
- Time zones. The
rt/devTimefield is UTC, ISO-8601 with seven-digit fractional seconds and the trailingZ. Both ArcSight and QRadar parse this format natively. - Volume sizing. CEF / LEEF lines are typically 200-500 bytes — comparable to the rolling-file template. A 100k-mesh-object connector with hourly syncs writes 50-200k object-audit lines per day, so plan ~20-100 MB / day per connector.
- Combine with file sink. If the CEF / LEEF feed is the primary SIEM channel, keep the default JSON-friendly file sink alongside it. The default file is the durable record; CEF / LEEF is the SIEM-shaped projection.
Configurable fields cheat-sheet
| Concern | Where to set it |
|---|---|
| Severity floor | Per-sink restrictedToMinimumLevel |
| Format choice | formatter / textFormatter -> IdentityMeshCefFormatter or IdentityMeshLeefFormatter |
| Extension keys | Determined by Serilog enrichers + LogContext.PushProperty call sites |
| Per-component split | Run separate WriteTo blocks with the formatter applied; partition by file path or sink target |
Related
siem-integration.md— broader SIEM field map.siem-syslog.md— RFC 5424 syslog sink that the LEEF formatter can ride on top of.siem-webhook.md— HTTPS webhook for CEF / JSON / LEEF push to a custom collector.siem-sentinel.md— Microsoft Sentinel ingestion — typically prefers JSON over CEF / LEEF.- ArcSight Common Event Format white paper - authoritative CEF reference.
- IBM QRadar LEEF v2.0 specification - authoritative LEEF reference.