Share via

How to record failed request body correctly in API Management

jinxjer lee 40 Reputation points
2026-04-08T02:26:23.6133333+00:00

I am using Azure API Management (Developer/Premium tier) deployed in an Internal VNET. I am trying to capture the Request Body only when a backend error occurs (specifically 400 errors) and log it into the ApiManagementGatewayLogs table. (or any other places if you can provide alternatives)

Policy Configuration: I have implemented the following policy to preserve the body in the inbound section and log it in the on-error section:

XML


<policies>

    <inbound>
        <base />
        <set-variable name="savedBody" value="@(context.Request.Body.As<string>(preserveContent: true))" />
    </inbound>

    <backend>
        <base />
    </backend>

    <outbound>
        <base />
    </outbound>

    <on-error>
        <base />
        <choose>
            <when condition="@(context.Response.StatusCode == 500)">
                <trace source="Manual-Body-Capture">
                    <message>@(String.Format("Status: 500 | URL: {0} | Body: {1}", 
                                        context.Request.Url.Path, 
                                        (string)context.Variables["savedBody"]))</message>
                </trace>
            </when>
        </choose>
    </on-error>
</policies>

__屏幕截图 2026-04-08 101955

Question:
according to: https://learn.microsoft.com/en-us/azure/azure-monitor/reference/tables/apimanagementgatewaylogs, traceRecords should save the related information, but I found nothing trace records saved in ApiManagementGatewayLogs.

What I need:
400 errors happend in production side, but I cannot add logging logic in the code base currently. I need to find a way to save the wrong request body in Azure api management. Since request body size is large, I only want to record the wrong request body to reduece the potential cost

Azure API Management
Azure API Management

An Azure service that provides a hybrid, multi-cloud management platform for APIs.


2 answers

Sort by: Most helpful
  1. Siddhesh Desai 6,080 Reputation points Microsoft External Staff Moderator
    2026-04-08T03:19:18.5733333+00:00

    Hi @jinxjer lee

    Thank you for reaching out to Microsoft Q&A.

    You’re preserving the request body in inbound and then trying to emit it on error via a <trace> call unfortunately, those <trace> messages don’t end up in the ApiManagementGatewayLogs table by default. Here are a couple of ways to capture only the 400-error payloads:

    Enable built-in diagnostics for “Frontend Request” bodies

    In your APIM instance, go to “Diagnostic Logs” > “Azure Monitor.”

    Enable logging, check Always log errors, and under Additional settings tick Frontend Request (and optionally Backend Request).

    Save.

    Now every error call (400, 500, etc.) will write the request/response bodies (up to 8 KB each, and a 32 KB total entry limit) into ApiManagementGatewayLogs.

    In Log Analytics, you can then run:

    ApiManagementGatewayLogs
    | where HttpStatusCode == 400
    | project TimeGenerated, OperationName, RequestBody, ResponseBody
    

    Use a real logger policy instead of <trace>

    Define an Event Hub or Application Insights logger in APIM (under Settings > Loggers).

    In your <on-error> block change your <trace> to:

    <log-to-eventhub logger-id="your-eventhub-logger">
      {
        "status": @(context.Response.StatusCode),
        "url": "@(context.Request.Url.Path)",
        "body": "@(context.Variables["savedBody"])"
      }
    </log-to-eventhub>
    

    Or similarly use <log-to-application-insights> if you prefer App Insights.

    This will stream your error payloads off to the configured logger, bypassing the 32 KB diagnostics limit in Azure Monitor.

    Adjust your error condition to catch 400s

    You currently only log on StatusCode == 500. Change your <when> to:

    <when condition="@(context.Response.StatusCode == 400)">
      …your log-to-eventhub or trace…
    </when>
    

    Notes:

    APIM enforces an 8 KB limit on logged request/response bodies and 32 KB per entry. If your payloads exceed that, they’ll get trimmed.

    Traces (<trace>) show up in the Test Console but aren’t pushed into the gateway diagnostic logs table.

    The key point to understand is that once the request body goes beyond ~32 KB, Azure API Management no longer provides a supported or reliable way to capture and persist the full request body inside the APIM gateway itself. This limitation applies even when the body is conditionally logged (for example, only on HTTP 400 responses). The restriction is not tied to one specific policy but comes from the APIM gateway design, memory constraints, and downstream integration limits. As a result, solutions such as Azure Monitor, Application Insights, Event Hub logging, or send-one-way-request cannot safely or consistently handle very large request payloads (1 MB–10 MB) directly from APIM. While Event Hub or Blob Storage can store large data, APIM cannot reliably forward such large payloads to them, which is the core blocker in this scenario.

    Refer below points to resolve this issue or use these as workarounds:

    There is no supported way for APIM to record the full request body (>32 KB) directly APIM policies can read the request body, but buffering and forwarding very large payloads (1 MB–10 MB) inside the gateway is not supported or recommended. Any approach that tries to push the entire body out of APIM (to Event Hub, email, Logic Apps, or webhooks) risks truncation, performance degradation, or gateway instability. This is a product design boundary rather than a configuration issue.

    Event Hub cannot be used from APIM to save full large request bodies Although Event Hub itself can work with large messages (using batching or external storage), the APIM log-to-eventhub policy enforces a strict size limit (approximately 200 KB). APIM still has to serialize and send the payload, which means you cannot use Event Hub as an escape hatch to persist 1–10 MB request bodies directly from APIM.

    The correct pattern is to store the full request body outside of APIM (backend‑side) The recommended architecture is to let the backend service handle large payload logging. When the backend detects a 400 error, it can save the full request body to Azure Blob Storage, Data Lake, or another storage service without size limitations. APIM should only pass through the request and optionally inject a correlation ID for traceability.

    Use correlation IDs and metadata logging in APIM instead of full payloads from APIM, log only metadata such as correlation ID, content length, headers, request ID, and error code. The backend stores the large payload and associates it with the same correlation ID. This allows you to “find” and “check” large, failed requests without forcing APIM to store them.

    For very large bodies, upload first and pass a reference through APIM If large payloads are expected (1 MB–10 MB), a robust design is:

    • Client uploads the payload directly to Blob Storage.
    • Client calls the API through APIM with a Blob reference (URL or ID). In case of a 400 error, the payload is already safely stored and can be reviewed without involving APIM body logging at all.

    Sending large request bodies from APIM to storage, email, or webhooks is not recommended Using send-one-way-request or similar policies to forward full payloads is unsafe for large bodies and introduces risks such as gateway memory pressure, higher latency, throttling, and potential security or compliance issues. This approach is strongly discouraged for production workloads.

    Summary

    For request bodies larger than 32 KB (and especially 1–10 MB), there is no supported way to record the full request body directly in Azure API Management, including via Event Hub. The only reliable and scalable solution is to move full payload capture to the backend or to storage referenced by the request, while APIM is limited to correlation and metadata logging.

    If the resolution was helpful, kindly take a moment to click on 210246-screenshot-2021-12-10-121802.pngand click on Yes for was this answer helpful. And, if you have any further query do let us know.


  2. Q&A Assist
    2026-04-08T02:26:48.5333333+00:00
    AI generated content. This question contains an answer created with AI from Q&A Assist. Learn more

    ApiManagementGatewayLogs does not store the full request body by default, and traceRecords in this table is not intended to contain arbitrary payloads preserved from policies. To capture failed request bodies selectively (for example, only when a 400 error occurs), use API Management policies and send the body to another sink (such as Application Insights or Event Hubs) from the on-error section.

    Key points from the platform behavior:

    • Policy execution is split into inbound, backend, outbound, and on-error sections. When an error occurs, remaining steps are skipped and execution jumps to on-error.
    • In on-error, only a specific set of policies is allowed, including set-variable, send-request, send-one-way-request, log-to-eventhub, trace, return-response, and others.
    • Errors that trigger on-error, or responses with 4xx/5xx status codes, are emitted as Exception telemetry to Application Insights when that integration is enabled.

    Given these behaviors, a working pattern to capture failed request bodies while minimizing volume is:

    1. In the inbound section, buffer the request body into a variable so it can be reused later.
    2. Let the request proceed to the backend as usual.
    3. In the on-error section, check context.LastError (or response status) and, only when the backend response is 400, send the buffered body to an external sink using an allowed policy.

    Conceptually:

    <policies>
      <inbound>
        <!-- Preserve request body in a variable -->
        <!-- Example: use set-variable with a policy expression that reads the body -->
      </inbound>
    
      <backend>
        <!-- Call backend as usual -->
      </backend>
    
      <outbound>
        <!-- Normal response handling -->
      </outbound>
    
      <on-error>
        <!-- Inspect error -->
        <!-- context.LastError provides details such as reason, section, scope -->
    
        <!-- Conditionally log only failed requests -->
        <!-- For example, use choose + send-request/log-to-eventhub/trace to emit
             the preserved body when the backend returned 400. -->
      </on-error>
    </policies>
    

    Because ApiManagementGatewayLogs has fixed columns (such as BackendRequestBody, BackendResponseBody, Errors, LastErrorMessage, LastErrorReason, etc.), it is not possible to push arbitrary trace records with large request bodies into that table via policy. Instead, use:

    • Application Insights integration, which records:
      • Request telemetry for frontend calls.
      • Dependency telemetry for backend calls.
      • Exception telemetry for failed requests, including those that triggered on-error or returned 4xx/5xx.
    • A trace policy in on-error to emit structured trace messages (including the preserved request body or a truncated version) to Application Insights. Ensure the severity in the trace policy is at least the verbosity configured in Application Insights so traces are ingested.
    • Alternatively, log-to-eventhub in on-error to send the failed request body to Event Hubs, where it can be processed or archived.

    To keep costs under control for large payloads:

    • Only emit logs from on-error when the backend response code is 400.
    • Truncate or summarize the body before logging (for example, log only the first N characters or selected fields).
    • Use sampling or filters at the sink (Application Insights or Event Hubs consumer) if needed.

    ApiManagementGatewayLogs remains useful to identify failures and correlate them (for example, via LastErrorReason, LastErrorMessage, IsRequestSuccess, BackendResponseCode), but the full failed request body should be captured through policies and logged to Application Insights or Event Hubs rather than relying on traceRecords in this table.


    References:

Your answer

Answers can be marked as 'Accepted' by the question author and 'Recommended' by moderators, which helps users know the answer solved the author's problem.