> AI agents: this is one page from PostHog's docs. Full index of Markdown docs for LLMs: https://posthog.com/llms.txt # Logs configuration Canonical URL: https://posthog.com/docs/logs/logs-config ## On this page - [Fields](#fields) - [Propagation](#propagation) - [JSON body parsing limits](#json-body-parsing-limits) - [JSON attribute parsing configuration](#json-attribute-parsing-configuration) Each project has a logs configuration that tells PostHog how to read your log records. It holds the attribute keys that link logs to people and sessions, and the JSON keys that log patterns are derived from. Read or change it through the `logs_config` endpoint. You need to be a project admin to change it. ```text GET /api/projects/:project_id/logs_config/ PATCH /api/projects/:project_id/logs_config/ ``` A `PATCH` is partial. Send only the fields you want to change and the rest keep their values. ## Fields All three list fields share the same rules: - Keys are matched literally at the top level. A dot is part of the key name, not a path into nested objects. `log.message` names one key called `log.message`. - Order matters. PostHog checks keys in list order, and the first key that holds a value wins. - Whitespace around a key is trimmed. Blank keys and duplicate keys are rejected. - A list holds at most 10 keys, each up to 200 characters. ### `logs_distinct_id_attribute_keys` Log attribute keys whose values hold a PostHog distinct ID. Default: `["posthogDistinctId"]`. Used to link a log to a person, so the log appears on that person's Logs tab and the `query-logs` MCP tool can filter logs to one user. Add keys only if your pipeline emits the person identifier under a different attribute. See [linking logs to session replay](https://posthog.com/docs/logs/link-session-replay.md). The list can't be empty. ### `logs_session_id_attribute_keys` Log attribute keys whose values hold a PostHog session ID. Default: `["sessionId"]`. Used to link a log to a session replay or an error tracking session. Add keys only if your pipeline emits the session ID under a different attribute. The list can't be empty. ### `logs_pattern_message_keys` JSON keys whose value is the message text that log patterns are derived from. Default: `["message", "msg", "event"]`. In **Settings > Project > Logs > Pattern message extraction**, add or remove keys, drag them into priority order, and select **Save**. Project admins can edit this setting. Clear all keys and save to turn message extraction off. When a log body is a JSON object, PostHog looks up these keys in order and uses the first one whose value is a string as the text to build the pattern from. A key whose value is a number or an object is skipped, and the search continues down the list. A key whose value is an empty string still counts as a match, so those logs share an empty pattern. If no key matches, the pattern is built from the object's set of top-level key names instead, so records with the same shape group together. An empty list turns message extraction off. Every JSON body then groups by its key set. This setting changes only how patterns are derived. The stored log body is never changed. ## Propagation A change reaches log ingestion within a few minutes. Records ingested in that window keep the patterns derived from the previous configuration. ## JSON body parsing limits **JSON parse logs** flattens a JSON body into up to 50 attributes. Sender-supplied attributes win on key collisions. Nested objects use dotted paths; arrays in the body keep their existing indexed paths, such as `items.0.name`. The original body is retained. Body enrichment has a 1 MiB byte budget for the body, severity text, and the keys and values in both log and resource attributes combined. This is a logical content limit, not the size of the encoded Avro record. The same accounting applies to log transformations. A body can fit on its own but exceed this limit when its extracted fields are added. Flattening visits at most 10,000 JSON nodes, allows a depth of 128, and budgets at most 1 MiB for generated paths and separately for retained serialized attributes. It retains at most 50 fields but continues the bounded traversal to preserve later collisions with those fields. If a size or traversal budget is exceeded, enrichment adds no fields and leaves the input record unchanged. Other configured ingestion stages still apply. Operators can monitor `logs_ingestion_json_enrichment_skipped_total` with `reason` values `input_size`, `flatten_budget`, and `output_size`. A `source` label separates `body` skips from `selected_attribute` skips. These counts cover enrichment skips, not dropped logs. Each source is checked independently, so a record can increment the counter more than once. ## JSON attribute parsing configuration In **Settings > Project > Logs > JSON parse log attribute**, enter one attribute key and select **Save**. Clear the key and save to turn it off. This setting is independent of **JSON parse logs**, which parses the log body. The control uses the same project-admin and Logs Editor restrictions as the other Logs settings. The `logs-json-attribute-parsing` feature flag controls the setting's visibility. Saving or clearing the key uses the existing project API permissions and does not require that flag. The project API stores the key in `logs_settings.json_parse_logs_attribute_key`, separately from the `logs_config` endpoint above: - The value must be a string. Leading and trailing whitespace is trimmed, and the trimmed key can be at most 200 characters. - The key is matched literally, including dots. - The default is off. An empty string disables parsing. - The API replaces the entire `logs_settings` object. Include the other current settings when updating it, as the UI does. Sending `logs_settings: null` clears all Logs settings. ### Extract fields from a JSON attribute Extraction requires both a saved key and an operator rollout gate: `LOGS_JSON_ATTRIBUTE_EXTRACTION_ENABLED_TEAMS`. This new gate defaults to empty (disabled), accepts comma-separated project IDs or `*`, and runs only on logs, never traces. The existing probe allowlist does not enable extraction. When enabled, PostHog parses the selected log attribute as a JSON object, including an object wrapped in a JSON string. Nested fields become dotted attributes prefixed with the selected key. For example, `payload` containing `{"user":{"id":"example-user"}}` adds `payload.user.id`. - Sender-supplied attributes take precedence, followed by fields extracted from the selected attribute, then fields extracted from the JSON body. - Nested arrays remain one JSON-string attribute at their object path. `payload` containing `{"flags":["alpha","beta"]}` adds `payload.flags`, not `payload.flags.0` or `payload.flags.1`. Array order and element types are preserved; this does not add native membership filtering. - A top-level array, primitive, invalid JSON, or missing key adds no fields. String-wrapped top-level arrays also add no fields. - Extraction does not remove or rewrite the source attribute. If PII scrubbing is enabled, it runs first and extraction uses the scrubbed source. Configured transformations and drop rules still run afterwards. Each selected object contributes at most 50 fields; each nested array counts as one field. The depth, node, and byte budgets described above apply, including validation of the contents of retained arrays. Over-budget extraction adds no fields from that source. The combined record must remain within the shared 1 MiB content budget. Extracted fields are available to log-based metrics and subsequent ingestion stages. To link logs to people or sessions, set **Link to person** or **Link to session** to the extracted path, such as `payload.personId` or `payload.sessionId`. Enabling extraction moves otherwise-passthrough traffic to Avro decoding and re-encoding. Roll out to a small project allowlist and monitor ingestion lag, CPU, and enrichment skips. The `logs_ingestion_processing_duration_seconds` metric carries an `attribute_extraction_enabled` label, so you can separate this cost from body parsing and PII scrubbing. Clearing the new extraction allowlist disables extraction without disabling the probe. ### Measure-only ingestion probe Operators can set `LOGS_JSON_ATTRIBUTE_PARSING_ENABLED_TEAMS` to a comma-separated list of project IDs to enable a JSON-prefix probe. The default is empty (disabled); `*` enables all projects with a nonempty `json_parse_logs_attribute_key`. This probe runs only on logs, never traces. The Prometheus counter `logs_ingestion_json_attribute_sniff_total` has `team_id` and `outcome` labels. Each inspected record increments one outcome: - `missing_key`: the configured key is absent from the log attributes. - `looks_like_json`: the first 64 characters start with `{` or `[`, allowing leading whitespace and a JSON string's opening quote and escaped whitespace. - `other`: the key exists but does not match that prefix. This is a heuristic, not JSON validation. The probe does not parse JSON, flatten values, extract fields, or change records. Attribute names and values are not included in the metric. Records are inspected after existing PII scrubbing and body enrichment, before drop rules. Enabling the probe can add Avro decoding for traffic that would otherwise pass through untouched. Probe-only batches retain their original bytes without re-encoding. Use a small project allowlist to limit that cost and metric cardinality. To break down the observed record rate by project and outcome in Grafana: ```promql sum by (team_id, outcome) (rate(logs_ingestion_json_attribute_sniff_total[5m])) ```