CaptureDecorator

Captures request and response messages in SLF4J logs, named in this format:

org.forgerock.openig.decoration.capture.CaptureDecorator.<decoratorName>.<decoratedObjectName>

If the decorated object is not named, the object path is used in the log.

During debugging, consider using a CaptureDecorator to capture the entity and context of requests and responses. However, increased logging consumes resources, such as disk space, and can cause performance issues. In production, reduce logging by disabling the CaptureDecorator properties captureEntity and captureContext, or setting maxEntityLength.

Decorator Usage

{
  "name": string,
  "type": "CaptureDecorator",
  "config": {
    "captureEntity": configuration expression<boolean>,
    "captureContext": configuration expression<boolean>,
    "maxEntityLength": configuration expression<integer>,
    "masks": object
  }
}
"captureEntity": configuration expression<boolean>, optional

When true, capture the message entity and write it to the logs. The message entity is the body of the HTTP message, which can be a JSON document, XML, HTML, image, or other information.

When false, do not capture the message entity, and write nothing to the logs.

The decorator excludes binary entities from the capture, instead of writing a [binary entity] marker.

Default: false

"captureContext": configuration expression<boolean>, optional

When true, capture the the context as JSON. The context chain is used when processing the request inside IG in the filters and handlers.

Default: false

"maxEntityLength": configuration expression<integer>, optional

The maximum number of bytes that can be captured for an entity. This property is used when captureEntity is true.

If the captured entity is bigger than maxEntityLength, everything up to maxEntityLength is captured, and an [entity truncated] message is written in the log.

Set maxEntityLength to be big enough to allow capture of normal entities, but small enough to prevent excessive memory use or OutOfMemoryError errors. Setting maxEntityLength to 2 GB or more causes an exception at startup.

Default: 524 288 bytes (512 KB)

"masks": object, optional

The configuration to mask the values of headers and attributes in the logs.

{
  "masks": {
    "headers": configuration expression<array>,
    "attributes": configuration expression<array>,
    "mask": configuration expression<string>
  }
}
"headers": configuration expression<array>, optional

Name of one or more headers whose value to mask in the logs.

The header name can be a regular expression, and is case-insensitive. The following value would mask headers called X-OpenAM-Username, X-OpenAM-Password and x-openam-token:

"headers": ["X-OpenAM-.*"]

Default: None

"attributes": configuration expression<array>, optional

Name of one or more attributes whose value to mask in the logs.

The attribute name can be a regular expression, and is case-insensitive.

Default: None

"mask": configuration expression<string>, optional

Text to replace the masked header value or attribute value in the logs.

Default: *****

Decorated Object Usage

{
    "name": string,
    "type": string,
    "config": object,
    decorator name: capture point(s)
}
"name": string, required except for inline objects

The unique name of the decorated object.

"type": string, required

The class name of the decorated object, which must be either a Filter or a Handler. See also Filters and Handlers.

"config": object, required unless empty

The configuration of the decorated object, as documented in the object reference page.

decorator name: capture point(s), optional

The decorator name must match the name of the CaptureDecorator. For example, if the CaptureDecorator has "name": "capture", then decorator name is capture.

The capture point(s) are either a single string, or an array of strings. The strings are documented here in lowercase, but are not case-sensitive:

"all"

Capture at all available capture points.

"none"

Disable capture. If none is configured with other capture points, none takes precedence.

"request"

Capture the request as it enters the Filter or Handler.

"filtered_request"

Capture the request as it leaves the Filter. Only applies to Filters.

"response"

Capture the response as it enters the Filter or leaves the Handler.

"filtered_response"

Capture the response as it leaves the Filter. Only applies to Filters.

Examples

Logging the Entity

The following example decorator is configured to log the entity:

{
    "name": "capture",
    "type": "CaptureDecorator",
    "config": {
        "captureEntity": true
    }
}
Not Logging the Entity

The following example decorator is configured not to log the entity:

{
  "name": "capture",
  "type": "CaptureDecorator"
}
Logging the Context

The following example decorator is configured to log the context in JSON format, excluding the request and the response:

{
  "name": "capture",
  "type": "CaptureDecorator",
  "config": {
    "captureContext": true
  }
}
Logging Requests and Responses With the Entity

The following example decorator is configured to log requests and responses with the entity, before sending the request and before returning the response:

{
  "heap": [
    {
      "name": "capture",
      "type": "CaptureDecorator",
      "config": {
        "captureEntity": true
      }
    },
    {
      "name": "ReverseProxyHandler",
      "type": "ReverseProxyHandler",
      "capture": [
        "request",
        "response"
      ]
    }
  ],
  "handler": "ReverseProxyHandler"
}
Capturing Transformed Requests and Responses

The following example uses the default CaptureDecorator to capture transformed requests and responses, as they leave filters:

{
  "handler": {
    "type": "Chain",
    "config": {
      "filters": [{
        "type": "HeaderFilter",
        "config": {
          "messageType": "REQUEST",
          "add": {
            "X-RequestHeader": [
              "Capture at filtered_request point",
              "And at filtered_response point"
            ]
          }
        }
      },
        {
          "type": "HeaderFilter",
          "config": {
            "messageType": "RESPONSE",
            "add": {
              "X-ResponseHeader": [
                "Capture at filtered_response point"
              ]
            }
          }
        }
      ],
      "handler": {
        "type": "StaticResponseHandler",
        "config": {
          "status": 200,
          "reason": "OK",
          "headers": {
            "Content-Type": [ "text/html" ]
          },
          "entity": "<html><body><p>Hello world!</p></body></html>"
        }
      }
    }
  },
  "capture": [
    "filtered_request",
    "filtered_response"
  ]
}
Capturing the Context As JSON

The following example captures the context as JSON, excluding the request and response, before sending the request and before returning the response:

{
  "heap": [
    {
      "name": "capture",
      "type": "CaptureDecorator",
      "config": {
        "captureContext": true
      }
    },
    {
      "name": "ReverseProxyHandler",
      "type": "ReverseProxyHandler",
      "capture": [
        "request",
        "response"
      ]
    }
  ],
  "handler": "ReverseProxyHandler"
}
Masking Values of Headers and Attributes

This example captures the context, and then masks the value of the cookies and credentials in the logs. To try it, set up the example in Log In With Credentials From a File, replace that route with the following route, and search the route log file for the text MASKED:

{
  "heap": [{
    "name": "maskedCapture",
    "type": "CaptureDecorator",
    "config": {
      "captureContext": true,
      "masks": {
        "headers": [ "cookie*", "set-cookie*" ],
        "attributes": [ "credentials" ],
        "mask": "MASKED"
      }
    }
  }],
  "name": "02-file-masked",
  "condition": "${matches(request.uri.path, '^/profile')}",
  "maskedCapture": "all",
  "handler": {
    "type": "Chain",
    "baseURI": "http://app.example.com:8081",
    "config": {
      "filters": [
        {
          "type": "PasswordReplayFilter",
          "config": {
            "loginPage": "${matches(request.uri.path, '^/profile/george') and (request.method == 'GET')}",
            "credentials": {
              "type": "FileAttributesFilter",
              "config": {
                "file": "/tmp/userfile.txt",
                "key": "email",
                "value": "george@example.com",
                "target": "${attributes.credentials}"
              }
            },
            "request": {
              "method": "POST",
              "uri": "http://app.example.com:8081/login",
              "form": {
                "username": [
                  "${attributes.credentials.username}"
                ],
                "password": [
                  "${attributes.credentials.password}"
                ]
              }
            }
          }
        }
      ],
      "handler": "ReverseProxyHandler"
    }
  }
}