Appearance
Text templates and Unparsed
The recognized formats cover most logs. When your lines have their own fixed layout, a text template turns each one into typed fields, without any code.
An example
Say your API logs one line per call it makes to another service:
text
api.openai.com/v1/chat/completions 1 3
api.stripe.com/v1/payment_intents 0 5The template
text
{endpoint} {success:bool} {attempts:int}turns the first line into:
json
{"message": "api.openai.com/v1/chat/completions 1 3", "props": {"endpoint": "api.openai.com/v1/chat/completions", "success": true, "attempts": 3}}Now the dashboard can chart calls per endpoint, the success rate and the average number of attempts.
Set a template
- Open the environment's Keys tab.
- On the deployment's card, choose Set a template (or Text templates in its menu).
- Pick an example under Start from, or type your own template.
- Check Try it on: it starts with the lines waiting in Unparsed, or paste a few. The preview shows each line's fields, or Doesn't fit. With several templates, a badge says which line each sample fit.
- Save.
On the Unparsed tab, a row a template can fix has a Fix with a template link. It opens the template for that row's deployment, with its waiting lines as samples.
From then on, text/plain bodies sent with that deployment's keys are read with the template instead of the automatic formats.
Several layouts: one template per line
When one deployment sends lines in more than one layout, write one template per line. Each entry is tried against them in order, and the first that fits wins.
text
{endpoint} {success:bool} {attempts:int}
[{ts:datetime}] {level}: {message}- Put specific templates above general ones: a catch-all such as
{message}on line 1 would fit everything. - Up to 10 templates. Blank lines are ignored.
- An entry that fits none goes to Unparsed, as below.
Placeholders
| Placeholder | Captures | Becomes |
|---|---|---|
{name} | As little text as the rest of the template allows | A string |
{name:int} | Digits | A number |
{name:float} | A decimal | A number |
{name:bool} | true, false, yes, no, 1, 0 | A boolean |
{name:datetime} | Any time ts accepts as text (no offset means UTC) | A time |
{name:json} | An object or array | Nested props |
{_} | Anything | Skipped |
- The names
ts,level,message,eventand the identity fields (device_id…) fill their own fields. Every other name is a prop, anda.bnests. - Without
messageorevent, the whole entry is the message. - Literal text matches as written, and any run of spaces matches any run of whitespace.
- Stack-trace lines join their entry, as with the automatic formats.
- Each template is at most 1,000 characters with 30 placeholders. Names use letters, digits,
_and..
A fuller template, for a Monolog-style line with JSON context:
text
[{ts:datetime}] {env}.{level}: {endpoint} {success:bool} {attempts:int} {context:json}Masking still applies
Every captured value is masked like any text line: emails become [email], public IPs [ip], and so on.
Lines that don't fit go to Unparsed
With templates set, a line that fits none of them is not stored as a plain line. It goes to the Unparsed tab as template_mismatch, so nothing is half-read.
When you change the templates, every line waiting in Unparsed for that deployment is read again with the new set. Lines that now fit become rows, at their original times.
The Unparsed tab
Unparsed is the safety net for everything that couldn't become a row. Nothing in it is lost: each item is kept for 7 days with its raw body and a reason, and you get an email when data starts landing there.
Common reasons:
| Reason | What happened | What to do |
|---|---|---|
invalid_json | A JSON body or NDJSON line didn't parse | Fix the sender's JSON |
invalid_utf8 | A text/plain body wasn't UTF-8 | Send UTF-8 |
template_mismatch | A line didn't fit the deployment's template | Fix with a template; Unparsed is read again |
invalid_gzip, decompressed_too_large | A gzip body was corrupt, or inflated past 8 MB | Fix the compression, or send smaller batches |
too_many_events | One request held more than 10,000 events or 20,000 lines | Send smaller batches |
invalid_ts, ts_out_of_range, too_many_props … | One object failed a field check | Fix that field in the sender |
The Read again button re-reads everything waiting, with the current rules and template.
Find patterns
Not sure what template to write? Find patterns on the Unparsed tab groups the held lines that share a shape, per deployment, with numbers, times and ids shown as blanks. Turn into template opens the template dialog with the proposed template added as a new line under your saved ones, and lines from that group, so you can rename fields and check the preview before saving. With 10 templates already saved, it offers to replace them instead. Nothing changes until you save; emails, tokens and public IPs are masked in everything shown.
Next steps
- Text log lines: the formats read without a template.
- Laravel SDK: its
message_fieldssetting does the same split on the sender side. - Troubleshooting: when data doesn't show up.