Share via

How to record failed request body correctly in API Management

jinxjer lee 20 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://dori-uw-1.kuma-moon.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.

0 comments No comments

2 answers

Sort by: Most helpful
  1. Siddhesh Desai 5,130 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.


  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:

    0 comments No comments

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.