Runbooks / Siem Cef Leef

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

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:

SlotIdentityMesh value
Version0 (CEF v0, the only published version)
DeviceVendorIdentityMesh
DeviceProductEngine
DeviceVersionAssembly version of IdentityMesh.Service
SignatureIDThe EventId property if present, else SourceContext, else IdentityMesh.Generic
NameThe Serilog message template text (parameterised, not rendered)
SeverityMapped from Serilog level (see below)
ExtensionSpace-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.

SlotIdentityMesh value
Version2.0
VendorIdentityMesh
ProductEngine
VersionAssembly version of IdentityMesh.Service
EventIDThe EventId property if present, else SourceContext
Delimx09 (ASCII tab — QRadar default)
ExtensionTab-separated key=value pairs

Severity scale (CEF + LEEF sev)

Both formats use the 0-10 scale. Serilog levels map as:

Serilog levelCEF / LEEF severity
Verbose0
Debug2
Information4
Warning6
Error8
Fatal10

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:

KeyProvenance
rt (CEF) / devTime (LEEF)UTC timestamp of the event
msgRendered message (template + properties resolved)
SourceContextLogger category — pivots to component / module
MachineNameStamped via Enrich.WithMachineName()
ThreadIdStamped via Enrich.WithThreadId()
InstanceNameSync-engine instance name
CycleIdSync-engine run id (present during connector cycles)
RequestIdASP.NET Core trace id (present on Admin API events)
ActorUpnAudit-row actor — present on *AuditStore log lines
ActionHTTP verb + route template — present on admin-audit lines
TargetKindAudit-row target type — present on admin-audit lines
TargetIdAudit-row target id — present on admin-audit lines
StatusCodeHTTP status — present on admin-audit lines
CorrelationIdAudit-row correlation id — pivots to IM_AdminAudit.CorrelationId
ChangeTypeObject-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

SinkWhen
Serilog.Sinks.FileSplunk UF tails the file. ArcSight File Reader Connector. Local forensic archive. Default and simplest.
Serilog.Sinks.SyslogQRadar LEEF DSM via TCP / TLS syslog. ArcSight Syslog SmartConnector.
Serilog.Sinks.HttpCustom collector that wants the formatted line over HTTPS rather than syslog.
Serilog.Sinks.ConsoleLocal 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

The IdentityMesh formatters implement both spec rules deterministically; embedded |, =, \, \r, \n, \t in property values are escaped before the line is written.

Failure modes

Operational notes

Configurable fields cheat-sheet

ConcernWhere to set it
Severity floorPer-sink restrictedToMinimumLevel
Format choiceformatter / textFormatter -> IdentityMeshCefFormatter or IdentityMeshLeefFormatter
Extension keysDetermined by Serilog enrichers + LogContext.PushProperty call sites
Per-component splitRun separate WriteTo blocks with the formatter applied; partition by file path or sink target