17.1 Transformation Mechanics: props.conf & transforms.conf
Key Takeaways
- props.conf selects the data and invokes TRANSFORMS-<class> = <stanza>,...; transforms.conf holds REGEX, FORMAT, DEST_KEY, and related settings.
- Index-time TRANSFORMS run in the typing pipeline on heavy forwarders or indexers; a universal forwarder ignores them unless the UF-only force_local_processing = true is set for that source type.
- Transforms listed in one TRANSFORMS-<class> value run in list order, and each one sees the event as changed by the previous one.
- At index time FORMAT defaults to <stanza-name>::$1, DEST_KEY is required unless WRITE_META = true, and LOOKAHEAD (default 4096 characters) limits how far into the event REGEX searches.
- Metadata keys need prefixes in FORMAT: MetaData:Host values start with host::, MetaData:Source with source::, and MetaData:Sourcetype with sourcetype::.
Transformation Mechanics: props.conf & transforms.conf
Quick Summary: Index-time transformations are split across two files.
props.confchooses which data is affected and invokes transforms withTRANSFORMS-<class>, andtransforms.confdefines what each transform does (REGEX,FORMAT,DEST_KEY). They run in the typing pipeline, so they take effect on heavy forwarders and indexers. A universal forwarder ignores them unlessforce_local_processingis enabled on it.
Architectural Division of Labor: props.conf vs. transforms.conf
Splunk Enterprise manages data onboarding and index-time transformations through a two-file configuration model. This architectural separation decouples where and when an action occurs from what and how the data is manipulated.
+-----------------------------------------------------------------------+
| props.conf |
| Identifies Input Scope: [<spec>] (sourcetype, source, or host) |
| Invokes Transform Class: TRANSFORMS-<class> = <stanza1>, <stanza2> |
+-----------------------------------------------------------------------+
|
v (Calls Stanza Definition)
+-----------------------------------------------------------------------+
| transforms.conf |
| Defines Mutation Logic: [<stanza1>] |
| Directives: REGEX, FORMAT, DEST_KEY, WRITE_META, LOOKAHEAD |
+-----------------------------------------------------------------------+
1. The Role of props.conf (Scoping & Orchestration)
In props.conf, administrators identify the context of the incoming data stream. The stanza header defines the matching boundary:
[<sourcetype>]: Applies the rule to all data assigned the specified sourcetype (most common and recommended practice).[source::<source>]: Applies the rule to events matching a source pattern (using glob wildcards, e.g.,[source::.../access*.log]).[host::<host>]: Applies the rule to data arriving from a specific host.
Within that stanza, the TRANSFORMS-<class> directive references one or more transformation definitions located in transforms.conf:
# $SPLUNK_HOME/etc/apps/<app_name>/local/props.conf
[custom:app:json]
TRANSFORMS-route_and_filter = drop_healthcheck, route_critical_to_sec
Here, <class> represents a unique label (route_and_filter) used to differentiate multiple transform directives. It points directly to stanza headers declared in transforms.conf.
2. The Role of transforms.conf (Mutation Specification)
In transforms.conf, each stanza header matches a name referenced by props.conf. This file contains the granular regular expression matching, field formatting, and destination queue or metadata targeting instructions.
| Parameter | Purpose & Permissible Values | Default Behavior |
|---|---|---|
REGEX | Regular expression pattern (Perl Compatible Regular Expression - PCRE). Capturing groups () extract sub-patterns for use in FORMAT. | None (Required for regex-based transforms) |
FORMAT | The value written to DEST_KEY. Uses $1, $2, … for capture groups; $0 is the DEST_KEY value before the transform ran | At index time: <stanza-name>::$1 |
DEST_KEY | Where the FORMAT result is stored: queue, _raw, _MetaData:Index, MetaData:Host, MetaData:Source, MetaData:Sourcetype, _TCP_ROUTING, _SYSLOG_ROUTING (case-sensitive) | No default; required for index-time transforms unless WRITE_META = true |
WRITE_META | When true, the result is written to the event's metadata as an indexed field | false |
LOOKAHEAD | How many characters into the event REGEX searches (all index-time transforms) | 4096 |
REPEAT_MATCH | Index-time only: run REGEX repeatedly, each time starting where the last match stopped (ignored when DEST_KEY = _raw) | false |
DEFAULT_VALUE | Index-time only: value written to DEST_KEY if REGEX fails | empty |
SOURCE_KEY | The key the REGEX is applied to | _raw |
Processing Tiers: Where Transformations Execute
Index-time TRANSFORMS run in the typing pipeline, in the regex-replacement processor. The pipelines in order are parsing (line breaking), merging (timestamps and line merging), typing (TRANSFORMS, SEDCMD), and index.
Universal forwarder (default)
input -> (character encoding, input-time settings) -> tcpout to 9997
* no line breaker, aggregator, or regex-replacement processor
* TRANSFORMS in its props.conf have no effect
|
v (unparsed data)
Heavy forwarder or indexer
parsingQueue -> line breaking (LINE_BREAKER, TRUNCATE)
aggQueue -> timestamps, line merging
typingQueue -> TRANSFORMS / SEDCMD (regex replacement)
queue = nullQueue -> event dropped
_MetaData:Index -> index changed
indexQueue -> written to the index (or forwarded, on a heavy forwarder)
The Tier Rule
- Universal forwarders: by default, a UF does not break lines, merge lines, or run regex replacement, so
TRANSFORMS-<class>in itsprops.confdoes nothing, and the data leaves unmodified. The exception is the UF-onlyprops.confsettingforce_local_processing = true. For that source type, it makes the forwarder run the line breaker, aggregator, and regex-replacement processors locally, at the cost of extra CPU and memory. - Heavy forwarders: a full Splunk instance parses data and applies
TRANSFORMSbefore forwarding. Masking, filtering, host overrides, and routing done there reach the indexers already applied. - Indexers: apply
TRANSFORMSto data that arrives unparsed, for example from universal forwarders.props.conf.specnotes that the processor ignoresTRANSFORMSon an indexer if they were already processed on the heavy forwarder. The newerRULESET-<class>setting (ingest actions) differs: it is processed on both tiers.
Transformation Execution Sequence and Chaining
Enterprise architectures frequently require applying several discrete transformations to a single event stream—for example, scrubbing an API key, overriding the host header, and routing high-priority errors to a dedicated index. Splunk provides two distinct mechanisms for chaining multiple transforms:
Chaining Method 1: Comma-Separated List in a Single Directive
Administrators can list multiple transform stanzas separated by commas under a single TRANSFORMS-<class> key in props.conf:
# props.conf
[cisco:asa]
TRANSFORMS-pipeline = strip_syslog_header, override_asa_host, route_security_index
Evaluation Rule: Splunk executes comma-separated transforms strictly in left-to-right order:
strip_syslog_headerexecutes first.override_asa_hostexecutes second, operating on the event state produced by the first transform.route_security_indexexecutes third, evaluating against the modified payload and metadata.
Chaining Method 2: Multiple Directives via Class Suffixes
Alternatively, administrators can declare multiple TRANSFORMS keys, each with a unique <class> identifier:
# props.conf
[cisco:asa]
TRANSFORMS-01_strip_header = strip_syslog_header
TRANSFORMS-02_override_host = override_asa_host
TRANSFORMS-03_route_index = route_security_index
Evaluation rule: when a stanza has several TRANSFORMS-<class> settings, they are applied in ASCII (lexicographic) order of the <class> names, not in the order they are written in the file:
01_strip_headerexecutes before02_override_host.02_override_hostexecutes before03_route_index.
Best practice: when order matters, put the transforms in one comma-separated list, which
props.conf.specguarantees is applied in list order. If you use separate classes, give them sortable names (01_,02_, …). OtherwiseTRANSFORMS-cleanruns beforeTRANSFORMS-routesimply becausecsorts beforer.
Sequential Event State Mutation
Transformations are not isolated evaluations; they mutate the event in memory as it traverses the typingQueue. Consider an event entering the pipeline:
Raw Ingest: "2026-09-23 10:14:02 gw01.corp ASA-6-302013: Built inbound TCP connection..."
- Step 1 (
strip_syslog_header): Modifies_rawby removing the timestamp and relay prefix. The event in memory is now:"ASA-6-302013: Built inbound TCP connection..." - Step 2 (
override_asa_host): setsDEST_KEY = MetaData:HostwithFORMAT = host::$1. The value must carry thehost::prefix. Because Step 1 already removed the host from_raw, this transform must capture it before Step 1 runs, or read it from another key withSOURCE_KEY. Order matters. - Step 3 (
route_security_index): EvaluatesREGEX = ^ASA-6-against the current state of_raw. Because the syslog header was already stripped in Step 1, the regex matches immediately at index 0 (^). If Step 1 had not executed, an unanchored regex would have been required.
Advanced transforms.conf Attributes: LOOKAHEAD & WRITE_META
Beyond basic regex matching, fine-tuning transformation behavior requires mastery of operational attributes:
1. The LOOKAHEAD Setting
By default, LOOKAHEAD = 4096, so REGEX searches only the first 4,096 characters of the event.
- If an event is a large stack trace or a 32 KB XML or JSON payload, and the text needed for routing or masking appears at character 8,000, the transform does not match.
- In such scenarios, administrators must explicitly raise
LOOKAHEADintransforms.conf:
[route_large_payloads]
REGEX = "audit_classification":\s*"RESTRICTED"
DEST_KEY = _MetaData:Index
FORMAT = compliance_vault
LOOKAHEAD = 32768
2. WRITE_META and Indexed Fields
Setting WRITE_META = true writes the FORMAT result (for example FORMAT = err_code::$1) into the event's metadata as an indexed field, stored in the index files alongside the other indexed terms. The spec recommends WRITE_META = true rather than DEST_KEY = _meta.
- Search Impact: Allows searches like
fieldname::valueto be resolved entirely at the indexer storage layer. - Cost: every indexed field adds to the index files and to indexing work. Splunk's general guidance is to prefer search-time extractions and to create indexed fields only where they bring a clear benefit, such as frequently used fields in very large data sets.
Administrative Traps and Troubleshooting
| Failure Scenario | Underlying Administrative Trap | Diagnostic Technique & Resolution |
|---|---|---|
| Transform does not trigger on a universal forwarder | props.conf and transforms.conf placed on a UF, which does not run the typing pipeline by default | Put them on the heavy forwarder or indexer tier (or, rarely, use force_local_processing on the UF) |
| Transform fails on long events | Regex target is located past byte 4,096, exceeding the default LOOKAHEAD. | Increase LOOKAHEAD in transforms.conf (e.g., LOOKAHEAD = 16384). |
| Chained rules run in the wrong order | Separate TRANSFORMS-<class> settings run in ASCII order of class names | Use one comma-separated list, or rename the classes so they sort correctly |
| Host or source override has no effect | FORMAT value missing its host::, source::, or sourcetype:: prefix | Use for example FORMAT = host::$1 |
| Transform ignored on the indexer | The data was already parsed by a heavy forwarder | Put the transform on the heavy forwarder |
| Transform modifies search-time instead of index-time | Using REPORT-<class> instead of TRANSFORMS-<class>. REPORT operates strictly on Search Heads at search time. | Use TRANSFORMS-<class> for index-time pipeline operations. |
Which Splunk Enterprise component tier is architecturally capable of executing index-time transformations configured via TRANSFORMS-<class> in props.conf and transforms.conf?
An administrator defines multiple transformation rules within the same props.conf stanza for a critical web sourcetype: TRANSFORMS-b_route = route_idx and TRANSFORMS-a_clean = clean_hdr. In what sequence will Splunk execute these rules?
An administrator deploys props.conf containing TRANSFORMS-mask = redact_payload directly to a Universal Forwarder monitoring local application logs. What is the operational outcome?