JSON Log Line Parser
observability.parse_json_logs
Overview
You send newline-delimited JSON and we return one record per line, a report of the lines that did not parse, and a census of which keys appear how often and with which types. Key discovery is bounded: past the tracked-key cap we stop and tell you the line we stopped on, rather than returning a report as unbounded as the log that produced it. Distinct values are collected only for fields you name, so a free-form message field never becomes a content dump. We hold nothing, forward nothing and read no clock.
A JSON validator tells you whether a line parses; it does not tell you that 40 percent of your lines are missing the trace field or that duration arrives sometimes as a number and sometimes as a string.
Reliability
Fully deterministic: the same body and options always give you the same records and the same census, independent of locale, default time zone and default charset. Numbers are preserved exactly as written rather than passed through a binary double. Failures, none worth retrying unchanged: OBS_JSON_LOGS_INPUT_MISSING means neither logs nor fileRef arrived, so send one of them. OBS_JSON_LOGS_INPUT_INVALID means a property had the wrong type or the body was empty, so check the payload shape. OBS_JSON_LOGS_INPUT_TOO_LARGE means the inline body went past 2097152 bytes, so upload it and pass a file reference instead. OBS_JSON_LOGS_FILE_REF_NOT_ACCESSIBLE means the reference is not readable with these credentials, so upload the object again and use the new reference. OBS_JSON_LOGS_FILE_TOO_LARGE means the referenced object went past 10000000 bytes, so split it and call once per part. OBS_JSON_LOGS_LINE_LIMIT_EXCEEDED means the body carries more lines than your maxLines allows, so raise it up to 50000 or split the body. OBS_JSON_LOGS_DEPTH_EXCEEDED means a line nests deeper than maxDepth, and the answer names that line so you can look at it. OBS_JSON_LOGS_STATS_FIELD_LIMIT_EXCEEDED means statsFields named more than 16 keys, so ask for fewer. OBS_JSON_LOGS_LIMITS_INVALID means one of the limits fell outside its published range, so bring it back inside and send it again. OBS_JSON_LOGS_OPTION_UNSUPPORTED means a property is not one we accept, so drop it. OBS_JSON_LOGS_WORK_REFUSED means the projected work went past 16000000 units, and the answer carries both the projection and the budget. OBS_JSON_LOGS_OUTPUT_TOO_LARGE means the records would not fit the inline response ceiling, so set includeRecords false or send fewer lines.
Context footprint
Echoes every parsed record by default; set includeRecords false when you only want the census and the error report. A successful call costs 3 execution credits.
When to use it
When you hold JSON log lines and want records plus an honest, bounded picture of what the fields actually contain.
When your lines are logfmt, when you want a schema verdict, or when you need the content redacted.
Scope
Non-goals
- Does not store, buffer, index or forward anything between calls
- Does not scrape, poll or receive logs over a network
- Does not redact or mask anything — send the text to security.redact_secrets first
- Does not infer a cause, a root cause or a remediation from what it reads
- Does not accept a caller-supplied regular expression on any field
Properties
Capabilities
Cost & billing
Flat rate per invocation
File handling
File limits
Constraints this skill applies to your input and generated files. The “Limited by” column shows the static source of each cap; none of these can be raised by upgrading your plan.
| Limit | Value | Limited by |
|---|---|---|
| Maximum size per input file | 9.5 MiB | Skill and platform maximum |
| Maximum number of input files | 1 | Skill maximum |
How we handle your data
What this means
This skill may receive sensitive content and may return submitted, transformed, extracted, or reformatted content.
Squillify guarantees
- Runs inside Squillify
- This skill runs inside Squillify.
- Payloads are not sent to an external service by this skill.
- Squillify does not permanently store submitted payloads.
- Telemetry records metadata only, not submitted content.
- Submitted payloads are not used for training.
- Submitted payloads are not reviewed by humans.
- This skill does not target sensitive data.
- The response may contain submitted or transformed content.
Your responsibility
Squillify does not determine whether submitted content is sensitive. Your application is responsible for deciding what data it is allowed to submit. Your application is responsible for securing the response.
Input schema
| Field | Type | Required | Description |
|---|---|---|---|
| logs | string | No | Required unless fileRef is provided. Newline-delimited JSON, 1 to 2097152 UTF-8 bytes. |
| fileRef | string | No | Optional Squillify file reference holding up to 10000000 bytes of JSON lines; omit it to parse the inline logs field. |
| limits | object | No | Closed caps you can tighten; omit it for maxLines 50000, maxLineLength 16384, maxDepth 32, maxFieldsPerRecord 64, maxTrackedKeys 256, maxDistinctValues 20 and maxErrors 500. |
| levelField | string | No | Top-level severity key of 1 to 128 code points; omit it for level. |
| statsFields | array | No | Up to 16 key names whose distinct values you want counted; omit it for an empty list. |
| includeRecords | boolean | No | Whether parsed records are echoed; omit it for true. |
Output schema
| Field | Type | Required | Description |
|---|---|---|---|
| records | array | Yes | One {line, record} object per parsed line; empty when includeRecords is false. |
| errors | array | Yes | Up to maxErrors parse defects, each a closed {line, column, code} object with optional key. |
| summary | object | Yes | Closed run counters: lineCount, recordCount, blankLineCount, errorCount, errorsTruncated, truncated and sourceKind. |
| stats | object | Yes | Closed bounded census with recordCount, fields, levels, levelField, keysTruncated and keyDiscoveryStoppedAtLine. |
API contract
POST /v1/executecurl -sS -X POST "$BASE/v1/execute" -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' -d '{"skillKey":"observability.parse_json_logs","input":{"logs":"{\"level\":\"info\",\"msg\":\"ok\"}\n"}}'import { Squillify } from "@squillify/sdk";
const sq = new Squillify({ apiKey: process.env.SQUILLIFY_API_KEY });
const { output } = await sq.execute("observability.parse_json_logs", {"includeRecords":false,"logs":"{\"level\":\"info\",\"msg\":\"ok\",\"duration_ms\":12}\n{\"level\":\"error\",\"msg\":\"upstream refused\"}\n{\"level\":\"info\",\"msg\":\"ok\"}\n","statsFields":["level"]});Errors
| Code | HTTP | Retryable | Description |
|---|---|---|---|
| VALIDATION_ERROR | 400 | No | The request payload failed schema validation. |
| UNAUTHORIZED | 401 | No | Missing or invalid API key. |
| SKILL_NOT_ACCESSIBLE | 403 | No | The API key is not granted access to this skill. |
| SKILL_NOT_AVAILABLE_FOR_PLAN | 403 | No | This skill is not included in your current plan. |
| PLAN_ELIGIBILITY_UNAVAILABLE | 503 | Yes | Plan eligibility could not be decided. Retry after backoff. |
| SKILL_NOT_FOUND | 404 | No | The requested skill or version does not exist. |
| TENANT_RATE_LIMITED | 429 | Yes | Request rate limit exceeded. Retry after backoff. |
| QUOTA_EXCEEDED | 429 | Yes | Plan quota exhausted. Retryable once the quota period resets. |
| EXECUTION_TIMEOUT | 504 | Yes | Execution exceeded the allowed time budget. |
| OBS_JSON_LOGS_INPUT_MISSING | 400 | No | The request payload failed this skill's input validation. |
| OBS_JSON_LOGS_INPUT_INVALID | 400 | No | The request payload failed this skill's input validation. |
| OBS_JSON_LOGS_INPUT_TOO_LARGE | 429 | No | A size, quota, rate, or time limit was exceeded. |
| OBS_JSON_LOGS_FILE_REF_NOT_ACCESSIBLE | 412 | No | The input is valid but cannot be processed as-is. |
| OBS_JSON_LOGS_FILE_TOO_LARGE | 429 | No | A size, quota, rate, or time limit was exceeded. |
| OBS_JSON_LOGS_LINE_LIMIT_EXCEEDED | 429 | No | A size, quota, rate, or time limit was exceeded. |
| OBS_JSON_LOGS_DEPTH_EXCEEDED | 429 | No | A size, quota, rate, or time limit was exceeded. |
| OBS_JSON_LOGS_STATS_FIELD_LIMIT_EXCEEDED | 429 | No | A size, quota, rate, or time limit was exceeded. |
| OBS_JSON_LOGS_LIMITS_INVALID | 400 | No | The request payload failed this skill's input validation. |
| OBS_JSON_LOGS_OPTION_UNSUPPORTED | 400 | No | The skill does not support the requested variant. |
| OBS_JSON_LOGS_WORK_REFUSED | 429 | No | A size, quota, rate, or time limit was exceeded. |
| OBS_JSON_LOGS_OUTPUT_TOO_LARGE | 429 | No | A size, quota, rate, or time limit was exceeded. |
Other limits
| Limit | Value |
|---|---|
| Recommended inline bytes | 32 KiB |
Limitations
- Does not store, buffer, index or forward anything between calls
- Does not scrape, poll or receive logs over a network
- Does not redact or mask anything — send the text to security.redact_secrets first
- Does not infer a cause, a root cause or a remediation from what it reads
- Does not accept a caller-supplied regular expression on any field
- Reading logfmt text, which observability.parse_logfmt answers
- Validating records against a schema, which schema.validate_ndjson answers
How to call
POST /v1/execute (requires authentication)
{
"skillKey": "observability.parse_json_logs",
"input": {
"includeRecords": false,
"logs": "{\"level\":\"info\",\"msg\":\"ok\",\"duration_ms\":12}\n{\"level\":\"error\",\"msg\":\"upstream refused\"}\n{\"level\":\"info\",\"msg\":\"ok\"}\n",
"statsFields": [
"level"
]
}
}Examples
Three lines with a level census and collected level values
{
"includeRecords": false,
"logs": "{\"level\":\"info\",\"msg\":\"ok\",\"duration_ms\":12}\n{\"level\":\"error\",\"msg\":\"upstream refused\"}\n{\"level\":\"info\",\"msg\":\"ok\"}\n",
"statsFields": [
"level"
]
}{
"summary": {
"lineCount": 3,
"recordCount": 3,
"blankLineCount": 0,
"errorCount": 0,
"errorsTruncated": false,
"truncated": false,
"sourceKind": "INLINE"
},
"stats": {
"recordCount": 3,
"fields": [
{
"key": "level",
"presentCount": 3,
"presenceRatio": 1,
"types": {
"STRING": 3
},
"distinctValues": [
{
"count": 2,
"value": "info"
},
{
"count": 1,
"value": "error"
}
],
"distinctValueCount": 2,
"distinctValuesTruncated": false
},
{
"key": "msg",
"presentCount": 3,
"presenceRatio": 1,
"types": {
"STRING": 3
},
"distinctValues": [],
"distinctValueCount": 0,
"distinctValuesTruncated": false
},
{
"key": "duration_ms",
"presentCount": 1,
"presenceRatio": 0.333333,
"types": {
"NUMBER": 1
},
"distinctValues": [],
"distinctValueCount": 0,
"distinctValuesTruncated": false
}
],
"levels": {
"TRACE": 0,
"DEBUG": 0,
"INFO": 2,
"WARN": 0,
"ERROR": 1,
"FATAL": 0,
"UNMAPPED": 0
},
"levelField": "level",
"keysTruncated": false,
"keyDiscoveryStoppedAtLine": null
},
"records": [],
"errors": []
}One unparseable line reported with its column
{
"includeRecords": false,
"logs": "{\"level\":\"info\"}\n{\"level\":}\n"
}{
"summary": {
"lineCount": 2,
"recordCount": 1,
"blankLineCount": 0,
"errorCount": 1,
"errorsTruncated": false,
"truncated": false,
"sourceKind": "INLINE"
},
"stats": {
"recordCount": 1,
"fields": [
{
"key": "level",
"presentCount": 1,
"presenceRatio": 1,
"types": {
"STRING": 1
},
"distinctValues": [],
"distinctValueCount": 0,
"distinctValuesTruncated": false
}
],
"levels": {
"TRACE": 0,
"DEBUG": 0,
"INFO": 1,
"WARN": 0,
"ERROR": 0,
"FATAL": 0,
"UNMAPPED": 0
},
"levelField": "level",
"keysTruncated": false,
"keyDiscoveryStoppedAtLine": null
},
"records": [],
"errors": [
{
"code": "PARSE_ERROR",
"line": 2,
"column": 10
}
]
}Recommended use cases
- Learn the shape of an unfamiliar JSON log stream before you write queries
- Find which lines a producer emits as malformed JSON, and where
Avoid for
- Reading logfmt text, which observability.parse_logfmt answers
- Validating records against a schema, which schema.validate_ndjson answers
Related skills
Composable with
Safety & privacy
- Runs without calling any external service.
- Your payloads are never stored permanently.
- Telemetry captures metadata only — never your payload contents.
- We never train models on your payloads.
- No human reviews your payloads.