Ingress Events
ALB
Instrumentify sets up your AWS application load balancers to forward its access logs to S3. Additionally it sets up a lambda that automatically gets triggered whenever new logs are added onto the S3 bucket. The access logs are stored in a gzipped file, in batches, in approximately 5min intervals, so there's always a 5 to 6 minute lag between requests being replied, and when they will appear in CloudWatch, as structured JSON objects.
Instrumentify's lambda structures the original access log line format into a JSON object, which can be filtered with Cloudwatch or can be used to create custom metrics with CloudWatch Metric filters.
Location
All ALB access log events are pushed as JSON lines to the cloudwatch log group named after the lambda that processes the original logs stored in S3. The log group name should be in the format:
/aws/lambda/${ALBToCloudwatchLambdaName}
Event Format
{
"alb": {...},
"req": {...},
"res": {...}
}
Attributes
The attributes under the alb.* key are parsed straight out of the original access log source, and cast to number, when appropriate.
Attributes under req.* and res.* may include transformations over the original alb.* values, which facilitate advanced filtering and custom metrics.
| Name | Type | Description |
|---|---|---|
| alb.type | String |
Type of request. The possible values are: http, https, h2, grpcs, ws and wss. |
| alb.time | String |
Time when the load balancer generated a response to the client, in ISO 8601 format. |
| alb.elb | String |
Resource ID of the load balancer. |
| alb.client | String |
IP address and port of the requesting client, in the format ip:port. |
| alb.target | String |
IP address and port of the target that processed the event, in the format ip:port. If the event was not forwarded, or the target is a lambda function, the value is set to -. |
| alb.request_processing_time | Number |
Total time elapsed (seconds, with millisecond precision) from the time that the load balancer received the request until it is forwarded to a target. -1 if the request is not forwarded, or the target does not respond before the idle timeout. |
| alb.target_processing_time | Number |
Total time elapsed (seconds, with millisecond precision) from the time the load balancer sent the request to a target, until the target started to send the response headers. -1 if the load balancer didn't forward the request, or no target responds before the idle timeout. |
| alb.response_processing_time | Number |
Total time elapsed (seconds, with millisecond precision) from the time the load balancer recieved the response header from the target, until it started to send the response to the client. This includes queueing time in the load balancer and connection acquisition to the client. Value set to -1 if no response is received from the target. |
| alb.elb_status_code | Number |
HTTP status code of the response from the load balancer. Should match alb.target_status_code, if exists. |
| alb.target_status_code | Optional[Number] |
HTTP status code of the response from the target. null if no valid reply from target (due to timeout, or the request not being forwarded). |
| alb.received_bytes | Number |
Size of the request, in bytes, received from the client. |
| alb.sent_bytes | Number |
Size of the response, in bytes, sent to the client. |
| alb.request_method | String |
HTTP method of the request. |
| alb.request_url | String |
URL of the request sent by the client, in the format protocol://host:port/uri. |
| alb.user_agent | String |
User-Agent string that identifies the client that originated the request. |
| alb.ssl_cipher | String |
[HTTPS listener] The SSL cipher. - if the listener is not HTTPS. |
| alb.ssl_protocol | String |
[HTTPS listener] The SSL protocol. -if the listener is not HTTPS. |
| alb.target_group_arn | String |
ARN of the target group. |
| alb.trace_id | String |
Contents of the X-Amazn-Trace-Id header |
| alb.domain_name | String |
[HTTPS listener] SNI domain providede by the client during the TLS handshake. - if the client does not support SNI or the domain doesn't match the certificate presented to the client. |
| alb.chosen_cert_arn | String |
[HTTPS listener] ARN of the certificate presented to the client. session-reused if the session is reused, -if the listener is not an HTTPS listener. |
| alb.matched_rule_priority | Number |
Priority value of the rule that matched the request. Values from 1 to 50000. If the default action was taken, this value is set to 0``. If an error ocurrs during rule evaluation, value set to-1`. |
| alb.request_creation_time | String |
Time when the load balancer received the request from the client, in ISO 8601 format. |
| alb.actions_executed | String |
Actions taken when processing the request in the form of comma-separated values. Valid strings: authenticate, fixed-response, forward, redirect, waf, waf-failed. |
| alb.redirect_url | String |
URL of the redirect target, if actions_taken is redirect. If no redirects, the value is set to -. |
| alb.error_reason | String |
Error reason code, if the request failed at the load balancer level. If the actions taken do not include an authenticate action, or the target is not a lambda, this value is set to -. |
| alb.target_port_list | List[String] |
A list of ip:port strings for the targets that processed this request. Currently this list should only contain 1 item, if the request is forwarded. Otherwise the list will be empty. |
| alb.target_status_code_list | List[Number] |
A list of status codes from the target responses. Currently this list should only contain 1 item, if the request is forwarded. Otherwise the list will be empty. |
| alb.classification | String |
The classification for desync migitaction. If the request does not comply with RFC 7230, the possible values are Acceptable, Ambiguous, and Severe. If the request complies, the value is set to -. |
| alb.classification_reason | String |
Classification reason code, if the request does not comply with RFC 7230. If the request complies, this value is set to -. |
| req.client | {"addr": String, "port": Number} |
Value from alb.client parsed into an object with independent addr and port keys. Contains the IP and port of the client originating the request. |
| req.target | Optional[{"addr": String, "port": Number}] |
Value from alb.target parsed into an object with independent addr and port keys. Contains the IP and port of the target originating the response. If the request was not forwarded, or the target didn't respond before the idle connection timeout, the field will be set to null. |
| req.summary | String |
Value composed of the following fields: {req.method} {req.pathname}. Useful to setup as a dimension in a custom chart or metric, when you want to compare usage and statistics between different REST endpoints. |
| req.method | String |
HTTP method of the request. Value extracted from alb.request_method. |
| req.href | String |
URL of the request sent by the client, in the format protocol://host:port/uri. Value extracted from alb.request_url. |
| req.origin | String |
URL of the request sent by the client, in the format protocol://host:port/uri. |
| req.protocol | String |
String containing the protocol scheme of the URL, including the final :. From javascript's URL.protocol property. |
| req.username | String |
String containing the username specified before the domain name, if any. From javascript's URL.username property. |
| req.password | String |
String containing the password specified before the domain name, if any. From javascript's URL.password property. |
| req.host | String |
String containing the hostname and the port, if any. From javascript's URL.host property. |
| req.hostname | String |
String containing the hostname. From javascript's URL.hostname property. |
| req.domain | String |
Top level domain name from the request url. For example, from http://www.google.com, req.domain = google.com. If the URL contains an IP address, it's value will be set to IP. |
| req.subdomain | Optional[String] |
Subdomain from the request url. For example, from http://www.google.com, req.subdomain = www. If the URL only contains a top level domain, or an IP, it's value will be null. |
| req.port | Number |
Number containing the port of the URL, if any. Otherwise null. From javascript's URL.port property. |
| req.pathname | String |
String containing all the paths segments of the URL. For example, from https://example.org/users?sort=modified, req.pathname = /users. From javascript's URL.pathname property. |
| req.search | String |
String containing the querystring from the URL, if any. For example, from https://example.org/users?sort=modified, req.search = ?sort=modified. From javascript's URL.search property. |
| req.search_params | Optional[Object] |
Object parsing req.search parameters into a Key-Value object. null if req.search is empty. For example, from https://example.org/users?sort=modified, req.searchParams = {"sort": "modified"}. |
| res.processing_time | Number |
Response time from the target, corresponding to alb.target_processing_time. The value is returned as an integer, in milliseconds. -1 if the request was not forwarded to a target, or it timed out. |
| res.status_code | Number |
HTTP status code of the response. Corresponds to alb.elb_status_code. |
| res.status_class | String |
HTTP status class. Derived from res.status_code. Its valid values are 2xx, 3xx, 4xx and 5xx. Groups status codes from res.status_codeinto those 4 classes. This is a useful field to generate custom metrics upon which to build alerts to keep track of API health. |
Examples
HTTPS request forwarded to target
{
"alb": {
"type": "https",
"time": "2023-09-15T15:29:59.137592Z",
"elb": "app/k8s-default-apichart-be0d348986/244108bde4c2e4ab",
"client": "46.6.33.173:23461",
"target": "10.1.16.21:8000",
"request_processing_time": 0,
"target_processing_time": 0.003,
"response_processing_time": 0,
"elb_status_code": 200,
"target_status_code": 200,
"received_bytes": 150,
"sent_bytes": 174,
"request_method": "GET",
"request_url": "https://apichart.instrumentify.io:443/?param1=value1¶m2=value2",
"user_agent": "hey/0.0.1",
"ssl_cipher": "ECDHE-RSA-AES128-GCM-SHA256",
"ssl_protocol": "TLSv1.2",
"target_group_arn": "arn:aws:elasticloadbalancing:eu-west-3:022747666878:targetgroup/k8s-default-apichart-5f3a7e13bd/6a3e1160ef8a0468",
"trace_id": "Root=1-65047877-57a0847a4cee0d6375de4659",
"domain_name": "apichart.instrumentify.io",
"chosen_cert_arn": "arn:aws:acm:eu-west-3:022747666878:certificate/64daee1d-efc5-4caf-9dfe-b1660f96e0c4",
"matched_rule_priority": 1,
"request_creation_time": "2023-09-15T15:29:59.134000Z",
"actions_executed": "forward",
"redirect_url": "-",
"error_reason": "-",
"target_port_list": [
"10.1.16.21:8000"
],
"target_status_code_list": [
200
],
"classification": "-",
"classification_reason": "-"
},
"req": {
"client": {
"addr": "46.6.33.173",
"port": 23461
},
"target": {
"addr": "10.1.16.21",
"port": 8000
},
"summary": "GET /",
"method": "GET",
"href": "https://apichart.instrumentify.io/?param1=value1¶m2=value2",
"origin": "https://apichart.instrumentify.io",
"protocol": "https:",
"username": "",
"password": "",
"host": "apichart.instrumentify.io",
"hostname": "apichart.instrumentify.io",
"domain": "instrumentify.io",
"subdomain": "apichart",
"port": null,
"pathname": "/",
"search": "?param1=value1¶m2=value2",
"search_params": {
"param1": "value1",
"param2": "value2"
},
"hash": ""
},
"res": {
"processing_time": 3,
"status_code": 200,
"status_class": "2xx"
}
}
HTTPS request with fixed-response, no forward
{
"alb": {
"type": "https",
"time": "2023-09-15T12:54:19.702592Z",
"elb": "app/k8s-default-apichart-be0d348986/244108bde4c2e4ab",
"client": "52.200.22.20:60336",
"target": "-",
"request_processing_time": -1,
"target_processing_time": -1,
"response_processing_time": -1,
"elb_status_code": 404,
"target_status_code": null,
"received_bytes": 230,
"sent_bytes": 167,
"request_method": "GET",
"request_url": "https://13.36.23.220:443/test.php",
"user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36",
"ssl_cipher": "ECDHE-RSA-AES128-GCM-SHA256",
"ssl_protocol": "TLSv1.2",
"target_group_arn": "-",
"trace_id": "Root=1-650453fb-1878874d0b9cb6af7d6f804d",
"domain_name": "-",
"chosen_cert_arn": "arn:aws:acm:eu-west-3:022747666878:certificate/64daee1d-efc5-4caf-9dfe-b1660f96e0c4",
"matched_rule_priority": 0,
"request_creation_time": "2023-09-15T12:54:19.702000Z",
"actions_executed": "fixed-response",
"redirect_url": "-",
"error_reason": "-",
"target_port_list": [],
"target_status_code_list": [],
"classification": "-",
"classification_reason": "-"
},
"req": {
"client": {
"addr": "52.200.22.20",
"port": 60336
},
"target": {
"addr": "-",
"port": null
},
"summary": "GET /test.php",
"method": "GET",
"href": "https://13.36.23.220/test.php",
"origin": "https://13.36.23.220",
"protocol": "https:",
"username": "",
"password": "",
"host": "13.36.23.220",
"hostname": "13.36.23.220",
"domain": "IP",
"subdomain": null,
"port": null,
"pathname": "/test.php",
"search": "",
"search_params": {},
"hash": ""
},
"res": {
"processing_time": -1,
"status_code": 404,
"status_class": "4xx"
}
}