OTTL Transformation Generator & Simulator avatar

OTTL Transformation Generator & Simulator

Pricing

from $100.00 / 1,000 ottl request processeds

Go to Apify Store
OTTL Transformation Generator & Simulator

OTTL Transformation Generator & Simulator

Plain-English requests → OpenTelemetry OTTL (transform/filter processors), an OTTL linter with 56 rules, and a simulator that shows before/after diffs on sample traces, logs and metrics. Deterministic, no LLM; Standby HTTP for agents.

Pricing

from $100.00 / 1,000 ottl request processeds

Rating

0.0

(0)

Developer

Justin Hagerty

Justin Hagerty

Maintained by Community

Actor stats

0

Bookmarked

1

Total users

0

Monthly active users

11 days ago

Last modified

Share

Describe the telemetry change you want in plain English and get a ready-to-paste OpenTelemetry Collector transform / filter processor in OTTL, linted by 66 rules and executed against sample traces, logs and metrics with before/after diffs — before it ever touches production.

The OpenTelemetry Transformation Language (OTTL) is how the Collector redacts PII, drops noisy spans, renames attributes and reshapes metrics. It is also easy to get subtly wrong: a regex written for Python instead of RE2, a $ that YAML expansion eats, a statement in the wrong context, a delete_key that runs before the set that needed the value, a filter condition that silently drops 90% of your traces, error_mode: propagate dropping whole batches. None of these fail loudly.

This Actor is a deterministic OTTL studio: it recognises 61 common transformation intents, generates OTTL in the syntax your Collector version understands, validates both generated and hand-written OTTL, and simulates the statements on realistic telemetry so you see exactly what changes, what gets dropped and what breaks. No LLM, no network calls, same answer every time — as a normal Apify run, in CI, or over Standby HTTP for AI coding agents and MCP clients.


Why use it

  • Plain English → correct OTTL. "Remove Kubernetes pod UIDs, redact user email addresses, and drop successful health-check spans" becomes a transform processor on the right contexts (resource, span, span event, log, data point), a filter processor that keeps failing probes, $$-escaped YAML and the pipeline order to use.
  • Lint OTTL you already have. Paste statements or a whole Collector config: syntax errors with line/column, unknown functions and wrong arity, paths that don't exist in the context, RE2-incompatible regexes, unescaped $, ordering bugs (read after delete), always-true filters, legacy filter config, error_mode risks and version incompatibilities.
  • See the effect before deploying. The simulator runs the statements on your sample telemetry (or bundled realistic samples) and returns per-item diffs, per-statement hit counts, byte-size reduction and danger findings: filters that drop too much, service.name or trace context removed, dead rules, empty bodies, runtime errors.
  • Honest about limits. Phrases it doesn't understand are listed with the closest supported intents, functions it can't simulate are reported as not simulated — nothing is guessed or faked.

Who it's for

  • Platform and observability engineers writing redaction, cost-reduction and normalisation rules for a shared Collector fleet.
  • Teams cutting telemetry bills who want to know how much a filter drops before rolling it out.
  • AI coding agents and MCP clients that edit Collector configs: generate, validate and simulate in a loop through one cheap HTTP call.
  • CI pipelines that lint every change to otel-collector.yaml (failOnSeverity, SARIF) — a fast pre-check before otelcol validate.

How to use

In Apify Console

  1. Write what you want in Requirements (the prefill is a working example), and/or paste OTTL into Statements or a full Collector config.
  2. Optionally paste Sample telemetry (OTLP/JSON) — otherwise the bundled samples (HTTP/DB/health-check spans, logs with PII, metrics) are used.
  3. Set your Collector version so the syntax matches your release, then click Start. The Output tab has four views: Overview, Generated config, Findings and Simulation.

Via the API (curl)

curl -s -X POST \
"https://api.apify.com/v2/acts/michiganpublictech~ottl-studio/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"requirements": "Redact emails in logs; drop debug logs; rename `user_id` to `user.id`",
"collectorVersion": "0.150.0"}' \
| jq -r '.[0].data.generated.collector_yaml'

Python (apify-client)

from apify_client import ApifyClient
client = ApifyClient("<APIFY_TOKEN>")
run = client.actor("michiganpublictech/ottl-studio").call(
run_input={
"mode": "validate",
"collectorConfig": open("otel-collector.yaml").read(),
"failOnSeverity": "high",
}
)
for report in client.dataset(run["defaultDatasetId"]).iterate_items():
for f in report["findings"]:
print(f["severity"], f["rule_id"], f["location"], f["title"])

CI: lint the Collector config on every change

- name: Lint OTTL
run: |
jq -n --rawfile cfg otel-collector.yaml '{mode: "all", collectorConfig: $cfg, failOnSeverity: "high"}' > input.json
curl -sf -X POST "https://api.apify.com/v2/acts/michiganpublictech~ottl-studio/runs?token=${{ secrets.APIFY_TOKEN }}&waitForFinish=300" \
-H "Content-Type: application/json" -d @input.json | jq -e '.data.status == "SUCCEEDED"'

With failOnSeverity the run fails when any finding (lint or simulation) is at or above that severity. REPORT.sarif in the run's key-value store can be uploaded to GitHub code scanning.

Standby mode: HTTP API for AI agents and MCP clients

The Actor supports Actor Standby: a warm instance behind an HTTPS endpoint answers in milliseconds with no container start-up. Charging is identical to batch runs, and nothing is written to a dataset — the report comes back in the HTTP response.

BASE=https://michiganpublictech--ottl-studio.apify.actor
AUTH="Authorization: Bearer $APIFY_TOKEN"
# Free: capabilities, intent catalog, function catalog, rule catalog
curl -s "$BASE/" -H "$AUTH"
curl -s "$BASE/intents" -H "$AUTH"
curl -s "$BASE/functions?verbose=1" -H "$AUTH"
curl -s "$BASE/rules" -H "$AUTH"
# Generate (no simulation → only transform-generated is charged)
curl -s -X POST "$BASE/generate" -H "$AUTH" -H "Content-Type: application/json" \
-d '{"requirements": "Hash user IDs with SHA256 and drop spans for /internal/*", "collectorVersion": "0.110.0"}'
# Validate your own OTTL
curl -s -X POST "$BASE/validate" -H "$AUTH" -H "Content-Type: application/json" \
-d '{"statementContext": "span", "statements": ["delete_key(attributes, \"user.email\")",
"set(attributes[\"email_domain\"], Split(attributes[\"user.email\"], \"@\")[1])"]}'
# → OTTL-SEM-004: user.email is read after an earlier statement deleted it
# Simulate on your telemetry (statements, a collectorConfig or requirements)
curl -s -X POST "$BASE/simulate" -H "$AUTH" -H "Content-Type: application/json" \
-d '{"requirements": "Drop successful health-check spans",
"sampleTelemetry": {"spans": [{"name": "GET /healthz", "attributes": {"http.route": "/healthz"}},
{"name": "GET /healthz", "status": {"code": "error"}, "attributes": {"http.route": "/healthz"}}]}}'

Endpoints: GET / (help; the platform readiness probe gets a plain ok), GET /intents, GET /functions, GET /rules, POST /generate, POST /validate, POST /simulate, POST /run (alias POST /; full Actor input, mode defaults to all). Options set on the Standby run's input (collectorVersion, syntaxStyle, processorName, signals, errorMode, …) act as defaults; the request body overrides them. Bodies are limited to 4 MB. Invalid input returns 400; a request that cannot be paid for (spending limit) returns 402; requests whose requirements are not understood return status: "failed" and are free.

From an MCP client

The Apify MCP server exposes the Actor as a tool (input schema = tool definition):

{
"mcpServers": {
"ottl-studio": {
"url": "https://mcp.apify.com?tools=michiganpublictech/ottl-studio",
"headers": { "Authorization": "Bearer YOUR_APIFY_TOKEN" }
}
}
}

Suggested agent instruction: "Before editing transform/filter processors in the Collector config, describe the change to the ottl-studio tool (or send the edited config with mode=all). Fix every high/critical finding and check data.simulation.stats for unexpected drops before committing." Agents that can make plain HTTP calls can use the Standby endpoints directly for the lowest latency.

Supported intents

61 deterministic intents. Each understands variations of the phrasing (synonyms, quoted or dotted attribute names, aliases such as "pod UIDs" → k8s.pod.uid, signal scoping such as "in logs", numbers, routes, /regex/). Several requests can be combined in one sentence, separated by commas, and, ;, new lines or bullet points. GET /intents returns the same list with more examples.

CategoryIntentWhat it doesProcessorExample phrasing
redactiondrop_sensitive_keysDelete secret-looking attributestransformRemove sensitive attributes like passwords and API keys
redactiondrop_stack_tracesRemove exception stack tracestransformDrop exception stack traces
redactionhash_attributesHash attribute values (SHA256)transformHash user IDs with SHA256
redactionmask_attribute_valueMask an attribute's valuetransformMask the value of user.email
redactionredact_bearer_tokensRedact bearer tokenstransformRedact bearer tokens
redactionredact_credit_cardsRedact credit-card numberstransformRedact credit card numbers
redactionredact_emailsRedact e-mail addressestransformRedact user email addresses
redactionredact_ip_addressesRedact IPv4 addressestransformMask client IP addresses
redactionredact_jwtsRedact JWTstransformRedact JWTs
redactionredact_phone_numbersRedact phone numberstransformRedact phone numbers
redactionredact_ssnsRedact US social security numberstransformRedact SSNs
redactionstrip_query_stringsStrip URL query stringstransformStrip query strings from url.full
redactionstrip_url_credentialsStrip credentials embedded in URLstransformRemove passwords embedded in URLs
attributesadd_environmentTag telemetry with the deployment environmenttransformAdd environment=production to everything
attributescopy_attributeCopy an attribute to a new keytransformCopy http.route to route.template
attributescopy_resource_attributeCopy resource attributes onto itemstransformCopy resource attribute service.version to span attributes
attributesdefault_service_nameDefault service.name when missingtransformDefault service.name to "legacy-batch" when missing
attributesdelete_attributesDelete attributestransformRemove Kubernetes pod UIDs
attributesdelete_matching_attributesDelete attributes matching a patterntransformRemove all attributes starting with "http.request.header."
attributeskeep_matching_attributesKeep only attributes matching a patterntransformKeep only attributes starting with "http."
attributeskeep_only_attributesKeep only listed attributestransformKeep only http.route and http.response.status_code on spans
attributesmigrate_semconvMigrate legacy semantic conventionstransformMigrate legacy HTTP semantic conventions to the stable names
attributesrename_attributeRename an attributetransformRename user_id to user.id
attributesset_attributeSet an attribute to a static valuetransformSet team = "payments" on all spans
attributesset_attribute_from_envSet an attribute from an environment variabletransformSet k8s.cluster.name from env var CLUSTER_NAME
attributesset_attribute_if_missingSet an attribute only when missingtransformAdd team = "payments" if missing
sizelimit_attribute_countLimit the number of attributestransformLimit spans to 64 attributes
sizetruncate_attribute_valuesTruncate attribute valuestransformTruncate attribute values to 256 characters
sizetruncate_log_bodyTruncate log bodiestransformTruncate log bodies to 1024 characters
normalizationflatten_attributesFlatten nested mapstransformFlatten nested attributes
normalizationlowercase_attributeLowercase attribute valuestransformLowercase http.request.method
normalizationnormalize_http_routeNormalize http.routetransformNormalize http.route, replacing numeric IDs with {id}
normalizationnormalize_span_namesTemplate IDs in span namestransformReplace IDs and UUIDs in span names with placeholders
normalizationuppercase_attributeUppercase attribute valuestransformUppercase http.request.method
parsingextract_with_regexExtract fields with a regextransformExtract fields from the log body using regex /user=(?P
parsingparse_json_bodyParse JSON log bodies into attributestransformParse JSON log bodies into attributes
parsingparse_key_value_bodyParse key=value log bodiestransformParse key=value pairs from log bodies into attributes
parsingseverity_from_bodyDerive log severity from the body texttransformSet log severity from the body text
filteringdrop_by_namespaceDrop telemetry from a Kubernetes namespacefilterDrop everything from namespace kube-system
filteringdrop_by_serviceDrop telemetry from a servicefilterDrop all telemetry from service "load-generator"
filteringdrop_datapoints_by_attributeDrop metric data points by attributefilterDrop datapoints where http.route = "/healthz"
filteringdrop_debug_logsDrop DEBUG/TRACE logsfilterDrop debug logs
filteringdrop_health_checksDrop health-check spansfilterDrop health check spans
filteringdrop_logs_below_severityDrop logs below a severityfilterDrop logs below WARN
filteringdrop_logs_matchingDrop logs whose body matches textfilterDrop logs containing "connection reset by peer"
filteringdrop_metrics_by_nameDrop metrics by namefilterDrop the metric process.runtime.cpython.gc_count
filteringdrop_metrics_by_prefixDrop metrics by name prefix/regexfilterDrop metrics starting with "debug."
filteringdrop_short_spansDrop very short spansfilterDrop spans shorter than 1ms
filteringdrop_span_events_by_nameDrop span events by namefilterDrop span events named "message"
filteringdrop_spans_by_attributeDrop spans by attribute valuefilterDrop spans where db.system = "redis"
filteringdrop_spans_by_kindDrop spans of a kindfilterDrop internal spans
filteringdrop_spans_by_nameDrop spans by namefilterDrop spans named "GET /favicon.ico"
filteringdrop_spans_by_routeDrop spans for a routefilterDrop spans for /internal/*
filteringdrop_successful_health_checksDrop successful health-check spansfilterDrop successful health-check spans
filteringkeep_only_error_logsKeep only ERROR+ logsfilterKeep only error logs
filteringkeep_only_error_spansKeep only error spansfilterKeep only error spans
tracesspan_status_from_httpSet span status ERROR from HTTP statustransformMark spans with HTTP 5xx responses as errors
metricsconvert_gauge_to_sumConvert gauges to sumstransformConvert gauge jobs.done to a sum
metricsconvert_sum_to_gaugeConvert sums to gaugestransformConvert sum queue.depth to a gauge
metricsrename_metricRename a metrictransformRename metric old.name to new.name
metricsscale_metric_unitConvert a metric's unittransformConvert db.client.latency from ms to s

Something missing? Phrases that match nothing come back under data.unrecognized with the three closest intents; phrases that match an intent but lack a required detail (e.g. "rename the attribute" without names) say what is missing.

Collector versions and syntax style

CollectorGenerated syntaxNotes
< 0.120 (default target 0.110.0)Grouped: trace_statements: [{context: span, statements: [...]}]Works on every OTTL-capable release.
≥ 0.120Flat: trace_statements: ['set(span.attributes["x"], ...)'] with context-prefixed paths (resource., span., spanevent., log., metric., datapoint.)Context inferred per statement. Override with syntaxStyle.

Filter conditions are always emitted in the classic per-context blocks (traces.span, logs.log_record, metrics.datapoint, …), which all OTTL-capable Collectors accept. Regex $ is written as $$ so Collector environment expansion leaves it alone.

Verified with real Collectors: the output of every intent example (all 61 intents, 96 examples) was checked with otelcol-contrib validate — grouped style on 0.115, 0.148 and 0.150.1, flat style on 0.148 and 0.150.1. Flat transform lists are rejected by 0.115 ("expected a map, got 'string'"), which is why older versions get the grouped style. The 0.120 cut-over and the other entries of the bundled version table (error_mode ~0.71, silent ~0.95, named arguments ~0.100, function since hints) are best-effort and labelled as such in the output; confirm against the transformprocessor README of your exact release.

Validation rules

66 rules: 56 lint rules and 10 simulation rules. Full catalog with recommendations: RULES.md.

FamilyRulesWhat they catch
OTTL-SYN syntax4Statements that don't parse (with line:column and hints for 'single quotes', &&, !), conditions where a statement is required and vice versa, empty statements
OTTL-FN functions12Unknown functions, wrong argument count or type, converter used as a statement / editor inside an expression, non-writable targets, unknown named arguments, invalid options and enums, deprecated functions, functions that may need a newer Collector or are not available in the context
OTTL-PATH paths8Unknown paths for the context, paths from another context, writes to read-only fields, attributes.key instead of attributes["key"], parent-context writes from a child context, context-prefixed paths on older Collectors, legacy semconv attributes in conditions, contexts that cannot be inferred
OTTL-RE regex8Regexes that don't compile or use syntax RE2 lacks (look-around, backreferences), \1 instead of $1, ExtractPatterns without named groups, invalid Go string escapes ("\d"), unanchored or unescaped key patterns, redundant .*
OTTL-SEM semantics & ordering13Read after delete, values overwritten before use, deleting service.name, unconditional statements and overwrites, copies that keep the original, duplicates, constant where clauses, typed fields compared with or set to the wrong type, non-positive limits, keep_keys with an empty list, body parsers without a type guard
OTTL-CFG configuration11error_mode: propagate / invalid error_mode, processors not used in any pipeline or without statements for its signal, transform/filter after batch, always-true (drops everything) and always-false filters, invalid statement groups, groups under the wrong signal, unescaped $, legacy include/exclude filters
OTTL-SIM simulation10Filters dropping at least dropWarningPercent of items, service.name or trace context removed, overwritten values, empty bodies or span names, dead rules, patterns that changed every item, runtime errors, read-after-delete observed at runtime, statements that could not be simulated, filters that dropped nothing

Simulation

Statements run in pipeline order (from your collectorConfig, otherwise filter → transform, or transform → filter when the filter reads fields the transform writes) on a copy of the telemetry, following the Collector's semantics:

  • Transform: statements per context in order; where per statement; group-level conditions (OR); nil values make set a no-op; resource/scope statements apply once per resource/scope.
  • Filter: conditions are OR-ed; matching spans, span events, log records, metrics or data points are dropped, and metrics left without data points are removed — as the filter processor does.
  • error_mode: ignore/silent skip the failing statement for that item; propagate rejects the signal's whole batch (reported as a finding).
  • Output: data.simulation holds per-signal items_in/out/dropped, estimated bytes before/after and reduction %, attributes added/removed, values changed, up to maxDiffItems item diffs (removed, added, changed with before/after, dropped), and per-statement stats (evaluated, matched, changed, errors).

Simulated (48 functions): editors set, delete_key, delete_matching_keys, keep_keys, keep_matching_keys, limit, merge_maps, replace_pattern, replace_all_patterns, replace_match, replace_all_matches, truncate_all, flatten, append, convert_sum_to_gauge, convert_gauge_to_sum, scale_metric; converters Concat, ConvertCase, Double, ExtractPatterns, FNV, Format, Hex, Int, IsBool, IsDouble, IsInt, IsList, IsMap, IsMatch, IsRootSpan, IsString, Len, Log, MD5, ParseJSON, ParseKeyValue, SHA1, SHA256, SHA512, Sort, SpanID, Split, String, Substring, TraceID, UUID.

Not simulated (validated, but reported as not simulated when executed — OTTL-SIM-009): time and duration functions (Time, Now, Duration, FormatTime, TruncateTime, Unix*, Hour…Weekday, Hours…Nanoseconds), URL, UserAgent, ParseCSV, ParseXML, ParseSimplifiedXML, Decode, Base64Decode, ExtractGrokPatterns, SliceToMap, ToKeyValueString, extract_sum_metric, extract_count_metric, copy_metric, aggregate_on_attributes, convert_summary_*, routing connector route() execution, trace_state keys, scale_metric on histograms/summaries.

Known approximations: limit keeps keys in insertion order (Go map order is unspecified); truncate_all and Substring count characters (Go counts bytes, which differs for non-ASCII); Concat with nil and ConvertCase(…, "camel") edge cases.

Input

FieldTypeDefaultDescription
modestringallgenerate (English → OTTL), validate (lint OTTL/config), simulate (run on telemetry), all.
requirementsstring or array—Plain-English requests (max 20,000 characters).
statementsarray—Your OTTL: strings, or {"context": "span", "statements": [...]} / {"context": "log", "conditions": [...]} objects (max 500).
statementContextstringautoContext for plain string statements; auto infers it from the paths.
statementTypestringtransformtransform (editor statements) or filter (boolean conditions).
collectorConfigstring—Full Collector YAML (max 2 MiB): transform/filter processors and routing connector statements are extracted, linted and simulated in pipeline order.
sampleTelemetryobjectbundledOTLP/JSON (resourceSpans / resourceLogs / resourceMetrics, camelCase or snake_case) or simplified {"resource": {...}, "spans": [], "logs": [], "metrics": []}. Up to 2,000 items per signal are simulated.
requestsarray—Several requests (objects with the fields above plus name); one dataset item and charge each.
collectorVersionstring0.110.0Your Collector version; picks the syntax style and version checks.
syntaxStylestringautoauto, grouped or flat.
processorNamestringgeneratedGenerated processors are transform/<name> and filter/<name>.
signalsarrayallSignals that requirements apply to when they don't name one.
errorModestringignoreerror_mode of generated processors and default for simulation.
useBundledSamplesbooleantrueSimulate on bundled telemetry when sampleTelemetry is empty.
dropWarningPercentinteger50OTTL-SIM-001 threshold.
maxDiffItemsinteger25Maximum item diffs in the output (0–500).
failOnSeveritystringnoneCI gate: fail the run when a finding is at or above this severity.
disabledRulesarray—Rule IDs to skip (custom-rules feature).
licenseKeystring (secret)—Enterprise / prepaid license.

Output

One dataset item per request, in the same Report format as every Michigan Public Technology Actor (shortened):

{
"product": "ottl-studio",
"target": "request",
"status": "ok",
"score": 100,
"summary": {"critical": 0, "high": 0, "medium": 0, "low": 0, "info": 4},
"findings": [{"rule_id": "OTTL-SEM-001", "severity": "info",
"location": "processors.transform/generated.log_statements[1].statements[0]", "…": "…"}],
"data": {
"mode": "all",
"collector_version": "0.110.0",
"syntax_style": {"style": "grouped", "reason": "classic `context:` groups work on every Collector release with OTTL"},
"intents": [{"intent": "delete_attributes", "phrase": "Remove Kubernetes pod UIDs", "confidence": 0.85, "…": "…"}],
"unrecognized": [],
"generated": {
"collector_yaml": "processors:\n filter/generated:\n error_mode: ignore\n traces:\n span:\n - '(IsMatch(attributes[\"http.route\"], \"^/(health|healthz|…)/?$$\") …) and status.code != STATUS_CODE_ERROR'\n transform/generated:\n error_mode: ignore\n trace_statements:\n - context: resource\n statements:\n - 'delete_key(attributes, \"k8s.pod.uid\")'\n …",
"pipeline_snippet": "service:\n pipelines:\n traces:\n processors: [memory_limiter, filter/generated, transform/generated, batch]\n …",
"statements": [{"signal": "traces", "context": "resource", "statement": "delete_key(attributes, \"k8s.pod.uid\")", "intent": "delete_attributes"}],
"revalidation_findings": []
},
"simulation": {
"telemetry_source": "bundled",
"processor_order": {"traces": ["filter/generated", "transform/generated"]},
"stats": {"signals": {"traces": {"items_in": 10, "items_out": 8, "items_dropped": 2, "bytes_reduction_pct": 16.3}},
"attributes_removed": 21, "values_changed": 10, "bytes_reduction_pct": 10.9},
"diffs": [{"item": "checkout · span 'GET /healthz'", "dropped": true},
{"item": "checkout · span 'SELECT shop.orders'", "removed": ["resource.attributes[\"k8s.pod.uid\"]"],
"changed": {"attributes[\"db.statement\"]": {"before": "… 'jane.doe@example.com' …", "after": "… '[REDACTED_EMAIL]' …"}}}]
},
"warnings": [],
"version_notes": {"disclaimer": "…", "notes": [{"version": "~0.120", "component": "transform", "note": "…"}]}
}
}

validate requests add data.validation (statements with their inferred contexts, processors and pipelines from your config). The key-value store gets OUTPUT (run summary), REPORT.md and REPORT.sarif.

Pricing

Pay per event — you only pay for successful work:

EventPriceWhen
transform-generated$0.10 per requestOTTL was generated from requirements and/or your statements / config were validated (batch run or any Standby POST).
simulation-run$0.05 per requestIn addition, only when a simulation actually ran (mode simulate or all).

Free: requests where nothing was recognised, unparsable YAML/JSON, simulate-only requests with unusable telemetry, invalid input, GET /, /intents, /functions, /rules. Example: 20 generate-and-simulate requests ≈ 20 × $0.15 = $3. Set a maximum cost per run in Apify: the Actor checks the remaining budget before each request and skips the simulation (with a warning) when only the generation is affordable, so you are never charged beyond it. Standard Apify platform usage applies.

Limits and safety

  • Sizes: requirements 20,000 characters, 500 statements, Collector config 2 MiB, Standby bodies 4 MB, 2,000 simulated items per signal per request, up to 100 requests per batch run on the Store plan.
  • No network, no LLM: the Actor makes no outbound requests; everything runs from bundled catalogs.
  • Your data stays in your run: telemetry and configs are processed in memory in your own Apify run (or the warm Standby instance serving your request); results go only to your dataset / key-value store or the HTTP response.
  • Generated configs are a starting point. Review them and run otelcol validate with your real Collector build before deploying.

Plans and licensing

Store users need nothing beyond an Apify account: pay-per-event pricing applies, and SARIF plus the CI gate are included. Disabling rules (disabledRules) and enterprise/prepaid options are described in ../../docs/licensing.md. Local runs without a license use the evaluation plan (3 requests per run).

Limitations

  • Intent recognition is pattern-based, not an LLM: it covers the 61 intents above and their common phrasings. Complex or conditional requests ("only for service X on weekdays") may need hand-written OTTL — which you can then validate and simulate here.
  • The simulator models a subset of OTTL (see Simulation); unsupported functions are reported, never faked. Time-based logic, grok, URL/User-Agent parsing and metric-creating functions are not executed.
  • Version knowledge is a bundled, best-effort table. Function since versions are approximate; the grouped/flat syntax split was verified on the Collector builds listed above.
  • Lint locations are YAML paths plus statement-relative line:column, not line numbers in your YAML file.
  • Routing connector statements are extracted and linted but not executed.
  • Per-signal pipeline wiring of a supplied config is checked for missing processors, not for every possible misconfiguration.

FAQ

Does it need my Collector or an LLM? No. Everything is deterministic and offline; the same input always gives the same output.

Which syntax should I use? Leave syntaxStyle on auto and set collectorVersion. Flat statements are shorter and easier to read on 0.120+; grouped statements work everywhere.

Why does my regex need $$? The Collector expands ${env:VAR} and $VAR in config files, so a literal $ in a regex anchor must be written $$. Generated YAML already does this, and OTTL-CFG-007 flags it in yours.

Can I simulate my own telemetry? Yes — paste OTLP/JSON (e.g. from the Collector file exporter or OTEL_EXPORTER_OTLP_PROTOCOL=http/json) or the simplified shape into sampleTelemetry.

What if a requirement isn't understood? It is listed under data.unrecognized with the closest intents and example phrasings. If nothing at all was recognised, the request is marked failed and is free.

What happens when my spending limit is reached? The Actor stops before the next paid request (or skips the simulation if only generation is affordable); results already produced are kept.

Changelog

See CHANGELOG.md.