RunReveal
Log ManagementLog Processing

Log Transforms

Transforms convert raw log data into RunReveal's normalized schema. Each transform contains one or more rules. Each rule is a linear chain of processors that ends with Output to Field.

transform pipeline

How processing works

Understanding what each step receives as input is the most important part of writing transforms.

  1. Every rule starts from the raw log — the full event string as ingested (usually JSON).
  2. Processors run in order — the output of step N becomes the input of step N+1.
  3. The final processor is always Output to Field — it writes the pipeline result to one normalized field (actor.email, eventTime, tags, etc.).
  4. Rules are independent — rule 2 does not see the output of rule 1; it also starts from the raw log.

Extract JSON Field requires valid JSON input. After you extract one field, the pipeline value is a plain string (for example John), not JSON. A second Extract JSON Field step on that value fails with invalid JSON input. To read multiple JSON fields in one rule, use Regex Find/Replace with capture groups, or create separate rules that each start from the raw log.

Example: single-rule flow

Raw log (sample event):

{"userGivenName":"John","userFamilyName":"Doe","eventName":"user.login"}

Rule: map eventNameeventName

StepProcessorConfigInputOutput
1Extract JSON FieldeventName{"userGivenName":"John",...}user.login
2Output to FieldeventNameuser.login(writes to normalized eventName)

Available processors

The tables below use a shared sample where helpful. Step input is what the processor receives; Step output is what it passes to the next step.


Extract JSON Field (gjson)

Extracts a value from JSON using GJSON path syntax.

Config: GJSON Path (for example user.email, data.items.0.id, users.#.name)

Step inputConfigStep output
{"name":"John","age":30}nameJohn
{"user":{"name":"John","age":30}}user.nameJohn
{"users":["John","Jane","Bob"]}users.1Jane
{"users":[{"name":"John"},{"name":"Jane"}]}users.#.name["John","Jane"]
{"name":"John"}address(empty — path not found)
not jsonnameError: invalid JSON input

Extract Delimited Field (field)

Splits delimited text and returns the field at a zero-based index. Unlike Split String, this processor can respect quoted fields that contain the delimiter.

Config: Delimiter, Field Index, Quote Handling (none, single ', or double ")

Step inputConfigStep output
field1,field2,field3delimiter ,, index 0field1
field1, field2 , field3delimiter ,, index 1field2
"field1","field,2","field3"delimiter ,, index 1, quotes ""field,2"
field1,field2,field3delimiter ,, index 5(empty — index out of range)

Extract with Regex (regex)

Matches the input against a regex pattern and returns one capture group (or the full match if no group is selected).

Config: Regex Pattern, Capture Group (number or name), optional Match index for multiple matches

Step inputConfigStep output
hello worldpattern worldworld
hello worldpattern (hello) (world), group 2world
hello worldpattern (?P<greeting>hello) (?P<subject>world), group subjectworld
hello world hello marspattern (hello) (\w+), match 1, group 2mars
hello worldpattern xyz(empty — no match)

Add Prefix (prefix)

Prepends a static string.

Config: Prefix

Step inputConfigStep output
worldprefix hello hello world
helloprefix (empty)hello
(empty)prefix hello hello

Add Suffix (suffix)

Appends a static string.

Config: Suffix

Step inputConfigStep output
hellosuffix worldhello world
hellosuffix (empty)hello
(empty)suffix world world

Add Prefix and Add Suffix only accept static text. They cannot append the value of another JSON field. To join two dynamic fields, use Regex Find/Replace (below).


Convert to Lowercase (lowercase)

Config: none

Step inputStep output
HELLO WORLDhello world
HeLLo WoRLDhello world

Convert to Uppercase (uppercase)

Config: none

Step inputStep output
hello worldHELLO WORLD

Regex Find/Replace (regexreplace)

Finds matches in the input and replaces them. Supports Go regexp capture references in the replacement string: $1, $2, etc.

Config: Find Pattern, Replace With

Step inputConfigStep output
hello123worldpattern \d+, replacement (empty)helloworld
hello123worldpattern (\d+), replacement [$1]hello[123]world
test123test456testpattern \d+, replacement NUMtestNUMtestNUMtest
hello worldpattern \d+, replacement NUMhello world (no match — unchanged)

Join two JSON fields into one value (for example actor.username):

Step inputConfigStep output
{"userGivenName":"John","userFamilyName":"Doe"}pattern ^\s*\{.*"userGivenName"\s*:\s*"([^"]*)".*"userFamilyName"\s*:\s*"([^"]*)".*\}\s*$, replacement $1 $2John Doe

The delimiter between $1 and $2 is whatever you put in Replace With — a space, underscore, @, etc.


Strip Characters (strip)

Removes every occurrence of the specified characters from the input.

Config: Characters to Strip

Step inputConfigStep output
hello123worldchars 123helloworld
hello!@#worldchars !@#helloworld
hello...worldchars .helloworld

Trim Whitespace (trim)

Removes leading and trailing whitespace (spaces, tabs, newlines).

Config: none

Step inputStep output
hello world hello world
hello worldhello world

Extract Substring (substring)

Returns a portion of the string by start/end index. Indices can be negative (count from the end). If end is omitted, it defaults to the end of the string.

Config: Start Index (optional), End Index (optional)

Step inputConfigStep output
hello worldstart 0, end 5hello
hello worldstart 6world
hello worldstart -5world
hello worldstart 0, end -6hello
hello worldstart -5, end -1worl
hello worldstart 5, end 2(empty — start ≥ end)

Split String (split)

Splits on a delimiter and returns the segment at the given index. Supports negative indices.

Config: Delimiter, Index to Take

Step inputConfigStep output
hello,worlddelimiter ,, index 1world
hello worlddelimiter , index 0hello
hello,world,testdelimiter ,, index -1test
hello,worlddelimiter ,, index 5(empty — index out of range)

Format Date/Time (datetimeformat)

Parses a timestamp string and normalizes it to RFC3339 (for example 2021-01-01T00:00:00Z). Use before Output to Field → eventTime.

Config: Input Format

Step inputConfigStep output
1609459200@unix_s (Unix seconds)2021-01-01T00:00:00Z
1609459200000@unix_m (Unix milliseconds)2021-01-01T00:00:00Z
2021-01-01T00:00:00ZRFC33392021-01-01T00:00:00Z
01 Jan 21 00:00 UTCRFC8222021-01-01T00:00:00Z
Fri Jan 01 00:00:00 2021ANSIC2021-01-01T00:00:00Z
2023-10-15custom YYYY-MM-DD2023-10-15T00:00:00Z
2023-10-15 14:30:45custom YYYY-MM-DD HH:mm:ss2023-10-15T14:30:45Z
(empty)any formatcurrent UTC time
not-a-dateYYYY-MM-DDError: invalid time format

Custom format tokens: YYYY, MM, DD, HH, hh, mm, ss, SSS, Z.


Output to Field (output)

Final step in every rule. Passes the pipeline value through unchanged and writes it to a normalized field when the transform is applied.

Config: Field Path (and Tag Key when field is tags)

Pipeline valueConfigNormalized result
user.logineventNameeventName = "user.login"
johndoeactor.usernameactor.username = "johndoe"
[email protected]actor.emailactor.email = "[email protected]"
192.168.1.1src.ipsrc.ip = "192.168.1.1"
8080src.portsrc.port = 8080 (parsed as number)
truereadOnlyreadOnly = true (parsed as boolean)
2024-03-20T10:00:00ZeventTimeeventTime set to that timestamp
productiontags + key environmenttags["environment"] = "production"
{"type":"document","id":"123"}resourcesJSON appended to resources array

Available output fields: id, eventName, eventTime, readOnly, actor.id, actor.email, actor.username, src.ip, src.port, dst.ip, dst.port, service.name, resources, tags.

Each normalized field (except tags and resources) can only be targeted by one rule per transform. Use tags with different keys for additional custom values.


Full pipeline examples

Map a JSON field to actor.email

Raw log:

{"userEmail":"[email protected]","action":"login"}
StepProcessorConfigOutput
1Extract JSON FielduserEmail[email protected]
2Convert to Lowercase[email protected]
3Output to Fieldactor.email(normalized)

Parse a CSV-style log line to eventName

Raw log:

2024-01-15,user.login,[email protected]
StepProcessorConfigOutput
1Extract Delimited Fielddelimiter ,, index 1user.login
2Output to FieldeventName(normalized)

Concatenate userGivenName + userFamilyNameactor.username

Raw log:

{"userGivenName":"John","userFamilyName":"Doe"}
StepProcessorConfigOutput
1Regex Find/Replacepattern ^\s*\{.*"userGivenName"\s*:\s*"([^"]*)".*"userFamilyName"\s*:\s*"([^"]*)".*\}\s*$, replacement $1 $2John Doe
2Output to Fieldactor.username(normalized)

Unix timestamp → eventTime

Raw log:

{"ts":"1609459200","event":"login"}
StepProcessorConfigOutput
1Extract JSON Fieldts1609459200
2Format Date/Time@unix_s2021-01-01T00:00:00Z
3Output to FieldeventTime(normalized)

Building an effective pipeline

transform pipeline

  1. Extract — Use Extract JSON Field, Extract with Regex, or Extract Delimited Field on the raw log.
  2. Clean and format — Trim, case conversion, strip unwanted characters, substring/split.
  3. Transform dates — Format Date/Time before writing to eventTime.
  4. Map to schema — Output to Field.

Add and test one processor at a time using Test Rule in the transform editor.

Testing your transform

transform preview

  1. Paste a representative event into Sample Event.
  2. Click Test Rule to run the current rule. Each step shows its output; errors appear in red (for example invalid JSON input).
  3. Click View Normalized Event to see the full normalized event after all rules run.

Troubleshooting

SymptomLikely causeFix
invalid JSON input on step 2+Prior step returned a plain string; Extract JSON Field needs JSONUse Regex Find/Replace to combine fields, or start a new rule from the raw log
Empty step outputPath/group/index not foundVerify GJSON path, regex group, or delimiter index against sample data
invalid time formatInput does not match selected date formatMatch Input Format to the actual string; use Test Rule to iterate
Field not in normalized outputMissing Output step, wrong field name, or field already used by another ruleEnd every rule with Output to Field; check for duplicate field assignments
Concatenation not workingChained Extract JSON Field + Add SuffixUse Regex Find/Replace with $1 $2 instead