# Logging — Portal Policy Template Bundles

This folder contains **logging** policy template bundles for the Layer7 API Developer Portal. The Portal Templates Guide places this kind of template in the **Logging** category (described as "Optional and Flexible" in the example category layout) — it is not required for governance, but it gives operations and security teams a per-request audit trail that can be ingested by SIEM / log-aggregation tooling without depending on the Portal analytics pipeline.

## Bundles in this folder

### `CaptureLogInfo-1.0.0.json`

Installs the **`CaptureLogInfo-1.0`** template. Emits a single **JSON-line audit detail** per request via the Layer7 Gateway's `Audit Detail` assertion at `INFO` level, suitable for tail/grep/SIEM-ingestion. The audit-detail content is publisher-selectable via a `Log Verbosity` drop-down with three profiles:

| Verbosity | Fields included |
| :--- | :--- |
| `minimal` | `ts`, `api_id`, `method`, `action`, `response_code`, `routing_time_ms` |
| `standard` | minimal + `auth_type`, `apikey_id`, `app_name`, `org_name`, `source_ip` |
| `verbose` | standard + `service_id`, `app_uuid`, `org_uuid`, `host`, `user_agent` |

A fourth fallback branch emits the `standard` line if the publisher's drop-down value is missing or unrecognised, so a misconfigured template never silently drops a request from the audit log.

**Example** `standard`-verbosity log line (one line per request, formatted here for readability):

```json
{
  "ts": 1715119872451,
  "api_id": "8c2a...c3f",
  "method": "POST",
  "action": "/v1/orders",
  "auth_type": "OAUTH_2",
  "apikey_id": "a1f3...8e2",
  "app_name": "OrdersFrontend-prod-key1",
  "org_name": "Acme Retail",
  "source_ip": "203.0.113.42",
  "response_code": "201",
  "routing_time_ms": "47"
}
```

The body is built inline in the `Audit Detail` `Detail` field using context-variable interpolation, so no SetVariable / message-builder gymnastics are required. Variables that resolve to empty strings (e.g. `app_name` on a `NO_AUTH` API) appear as `""` in the line — your SIEM ingestion will treat them as null/absent fields.

**What is intentionally NOT logged** — even at verbose verbosity:

- The full `Authorization` header (would leak bearer tokens).
- The request or response body (would leak PII / payload contents and inflate audit volume).
- Cookies (session leakage).
- Full secret-field values (already masked via the `l7.secure.` mechanism upstream).

If you do need to log any of these, add a custom verbosity profile rather than promoting them into the existing `standard` / `verbose` lines.

**Arguments (inputs)**

The 11 hidden inputs match the encass-results published by the bundles in [`../Authentication/`](../Authentication), [`../ThreatProtection/`](../ThreatProtection), [`../RateQuota/`](../RateQuota), and [`../ResponseProcessing/`](../ResponseProcessing) so the parent template wires them in directly.

| Visible / Hidden | Name | Source upstream | Used here for |
| :--- | :--- | :--- | :--- |
| hidden | `portal.managed.service.apiId` | Portal | `verbose` line `service_id` field. |
| hidden | `portal.analytics.action` | upstream / `CaptureMetrics` | log line `action` field. |
| hidden | `portal.analytics.api.uuid` | upstream / `CaptureMetrics` | log line `api_id` field. |
| hidden | `portal.analytics.apikey` | upstream auth | log line `apikey_id` field. |
| hidden | `portal.analytics.application.name` | upstream auth | log line `app_name` field. |
| hidden | `portal.analytics.application.uuid` | upstream auth | `verbose` line `app_uuid` field. |
| hidden | `portal.analytics.authType` | upstream auth / `CaptureMetrics` | log line `auth_type` field. |
| hidden | `portal.analytics.organization.name` | upstream auth | log line `org_name` field. |
| hidden | `portal.analytics.organization.uuid` | upstream auth | `verbose` line `org_uuid` field. |
| hidden | `portal.analytics.response.code` | upstream / `CaptureMetrics` | log line `response_code` field. |
| hidden | `portal.analytics.routingTotalTime` | `CaptureMetrics` | log line `routing_time_ms` field. |
| **visible** | `l7.enum.verbosity *` | publisher | drop-down: `minimal` / `standard` / `verbose`. |

**Exported context variables (outputs):** none. This template is a pure side-effect — its only output is the audit-detail entries written to the Gateway audit log. Everything the publisher / Portal need is already exported by `CaptureMetrics` upstream.

## Where this template fits in a Portal policy chain

The intended order in a Portal policy template chain is:

1. **Threat Protection** ([`../ThreatProtection/ThreatProtection-1.0`](../ThreatProtection)) — first.
2. **Authentication** ([`../Authentication/*`](../Authentication)).
3. **Per-Organization rate / quota** ([`../RateQuota/OrgLevelRLQ-1.0`](../RateQuota)).
4. **Per-API rate / quota** — Portal Standard system templates.
5. **Request validation** ([`../RequestValidation/`](../RequestValidation)).
6. **Routing** ([`../Routing/RouteRequest-1.0`](../Routing)).
7. **Response Processing** ([`../ResponseProcessing/CaptureMetrics-1.0`](../ResponseProcessing)) — finalises every `portal.analytics.*` variable.
8. **`CaptureLogInfo-1.0`** *(this bundle)* — runs **last**. Reads the finalised analytics variables and writes one JSON-line audit detail.

Placing logging after `CaptureMetrics` guarantees the line includes the SOAP-vs-REST-aware `action`, the routing time, and the final response code — even on the happy path where no upstream template explicitly set `response.code`.

## Customising the bundle

- **Add a custom verbosity profile.** Copy one of the existing `<wsp:All>` branches inside the `<wsp:OneOrMore>` block, change the `<L7p:RightValue>` of the `<L7p:ComparisonAssertion>` to the new profile name (e.g. `pci`), edit the `<L7p:Detail>` body to include / omit fields as needed, and add a matching `l7.enum.verbosity.<name>` argument to the encass JSON so the new option appears in the publisher drop-down.
- **Switch source IP to the proxy-aware header.** `${request.tcp.remoteAddress}` returns the directly-connected peer (which is your load balancer if the Gateway sits behind one). For requests that traversed an edge proxy or CDN, replace it with the first entry of `${request.http.header.x-forwarded-for}` (e.g. via a `Compare Variable` / token-extraction step earlier in the fragment).
- **Add a request-correlation ID.** Layer7 Gateway exposes the per-request UUID via `${requestId}` (or `${request.id}` on some versions); add it as a `request_id` field in the `verbose` body once you've confirmed which name your Gateway version supports.
- **Promote to WARNING on rejected requests.** Add a fifth branch above the existing four whose `<L7p:ComparisonAssertion>` matches `${portal.analytics.response.code}` `>=` `400` and emits the same body at `WARNING` level — useful for alerting rules in your SIEM that filter on log severity.
- **Switch from Audit Detail to a syslog forward.** If your audit pipeline is via `Send via Syslog` rather than the Gateway audit log, swap the `<L7p:AuditDetailAssertion>` for the appropriate Layer7 transport assertion in each branch.

## Conventions

Bundles in this folder follow the same conventions as the other Portal Templates in this repository:

- Distributed as a `*-<version>.json` policy bundle (schema `11.2.1`, `defaultAction: NEW_OR_UPDATE`).
- Installs a policy fragment under `/Portal Templates/` and an encapsulated assertion in the Policy Manager assertion palette (`customAssertions` palette folder).
- Designed to be composed with the bundles under [`../ThreatProtection/`](../ThreatProtection), [`../Authentication/`](../Authentication), [`../RateQuota/`](../RateQuota), [`../RequestValidation/`](../RequestValidation), [`../Routing/`](../Routing), and [`../ResponseProcessing/`](../ResponseProcessing) so that logging runs last in the chain and reads the finalised analytics state.

Contributions are welcome — drop a new `*-<version>.json` bundle into this folder and add a corresponding entry in the **Bundles in this folder** section above.
