Introduction
Qwilt tracks HTTP transactions through transaction logs, which can be used for auditing, performance analysis, or debugging. These logs are collected across the entire Qwilt deployment. To enroll in our Log Delivery Service, open a support ticket by emailing us at support@qwilt.com.
Qwilt supports sending these logs to:
- Your Amazon Web Services (AWS) bucket.
- Your Google Cloud Services (GCS) bucket.
- Your Datadog, via the Datadog API.
- Your Hydrolix account.
By default, the MDL files include all logging data. However, the transaction log output can be customized to meet your needs. Contact us at support@qwilt.com to configure any of the following log parameters:
- Log file format: For AWS and GCS users, logs can be delivered in TSV (default) or JSON format. Logs shipped to Datadog are always in JSON format. This option is not relevant to Hydrolix users.
- Filter by field: By default, all log fields are exported. You can choose to export only specified fields.
- Filter by field value: Filter log output based on any field values. For example, you can filter for data relevant to specified Delivery Services (available to Content Publishers) and/or response code classes (2xx/3xx/4xx/5xx).
- Log Sampling: Receive a subset of log data based on your preferred sampling percentage.
Our backend retains Personally Identifiable Information (PII) from Media Delivery Logs for 30 days. After this period, the data is automatically deleted.
Setting the Endpoint
Click the links below to expand and learn more about each option.
AWS/S3
The Log Pusher to S3 automatically sends the logs to service providers and content providers who are enrolled in a log delivery service for their service or deployment.To set up the Log Pusher to S3, configure an AWS IAM Role to enable Qwilt to push logs to your Amazon S3 bucket, and then share the Role ARN value with Qwilt.
The following procedure provides step-by-step instructions on how to do this.
- Log in to your AWS account. Choose All services and search for IAM. Open IAM.

- In IAM, open the Roles page. Select Create role.

- Under Trusted entity type, select Custom trust Policy. In the Custom trust policy field, paste the Trusted Policy you received from Qwilt. Below you can find the example to use.

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::052466545929:root"
},
"Action": "sts:AssumeRole"
}
]
}
Click Next.
- On the Add Permissions page, click Next without defining anything. On the Name, review, and create page, enter a Role name and then select Create role.

-
In the IAM Roles page, search for the new role you created and open it.
-
In the Permissions tab, select Add permissions, and then from the drop-down menu, select Create Inline Policy.

- In the Specify permissions page, click the JSON button. In the Policy editor, enable the following actions:
“putObject”
“putObjectAcl”
We recommend specifying these actions as well, to facilitate troubleshooting if/when needed:
“getObject”
"getObjectAcl"
“listBucket”
Define as Resources the Arn of the S3 bucket you want to share with Qwilt (e.g., "arn:aws:s3:::qwilt_logs"), and specify all objects (e.g., "arn:aws:s3:::qwiltlogs/").
Example:
{
"Version": "2012-10-17",
"Statement": [
{ "Effect":
"Allow",
"Action": [
"s3:PutObject",
"s3:GetObjectAcl",
"s3:GetObject",
"s3:ListBucket",
"s3:PutObjectAcl"
],
"Resource": [
"arn:aws:s3:::qwilt_logs",
"arn:aws:s3:::qwilt_logs/*"
]
}
]
}
- Click Next.
- Review the policy. Add a name to the policy that makes sense, and then select Create Policy.
- In the Role page, under Summary, the Role ARN is displayed.
This ARN value needs to be shared with Qwilt in order to complete the setup.
Copy the value and share it with Qwilt, via a secure method of your choice.

GCS
The Log Pusher to GCS bucket automatically sends the logs to service providers and content providers who are enrolled in a log delivery service for their service or deployment. Transaction logs are pushed as soon as they are created; however, in the event of heavy traffic or communication issues, they may arrive up to 15 minutes later.To set up the Log Pusher to GCS, you will need to create a service account and assign it to your GCS bucket with the correct role, and then obtain and share the access object with Qwilt.
The following procedure provides step-by-step instructions on how to do this.
- Create the Service Account. At this point you will not have to grant it with any permissions. Name the account and click Done.

- Search for the desired bucket. In this example, the search was filtered for “mdl”.

- Click the desired bucket, and then click the Permissions tab.

- Assign the bucket with the relevant service account and then grant it with the Storage Object User role.


- Your credentials will need to be shared with Qwilt in order to complete the setup. Secrets should be transferred over an encrypted channel (and received secrets will be kept encrypted). Your Qwilt representative will suggest the correct method for sharing your credentials.

Datadog
Transaction logs can be shipped to Datadog via Datadog API. To enable this, contact us at support@qwilt.com.Hydrolix
Log shipping to Hydrolix is based on a pull API by Hydrolix that pulls the needed transaction logs from a dedicated bucket from within Qwilt Cloud services.
Please contact us at support@qwilt.com to enable this.
Setup the workflow by defining and linking the following in Hydrolix:
- User credentials for accessing Qwilt resources.
- A table to ingest the log data.
- The log fields schema, to specify which of the log fields are parsed and exposed in the table.
STEP 1: Prerequisites
- An active Hydrolix account.
- Qwilt Cloud (QC) Services user credentials. (Provided by Qwilt.)
- JSON file with the needed log fields schema. (Provided by Qwilt.)
STEP 2: Set the QC Services User Credentials in Hydrolix
-
From the Hydrolix navigation bar, select Add New and then choose Credential.
-
In the New credential dialog, define the following:
-
Name - Assign the Credential any name.
-
Description - Any description.
-
Cloud Provider Type - Choose AWS Access Keys.
-
Access Key Id - Enter the Access Key ID provided by Qwilt.
-
Secret Access Key - Enter the Secret Access Key provided by Qwilt.

-
-
Select Create credential.
STEP 3: Create a Table in Hydrolix to Ingest the Log Data
-
From the Hydrolix navigation bar, select Add new and then choose Table.
-
In the New table dialog, select a project and enter a Table name and Description.

-
Select Create table.
STEP 4: Define the Log Fields Schema in Hydrolix
-
From the Hydrolix navigation bar, select Add new and then choose Table Transform.
-
In the New ingest transform dialog, define the following:
-
Select table - Select the table you created in the previous step.
-
Transform name - Assign the Table Transform any name.

-
-
Select the Upload method.
-
Browse to and select the JSON file provided by Qwilt.
-
Select Upload Transform.
STEP 5: Create the Ingest Source
-
From the Hydrolix navigation bar, select Add new and then choose Table Source.
-
In the Select Table field, choose the table you created previously.
-
Under Source type, choose Auto Ingest.

-
Define the following:
- Name - Assign any name.
- Queue Name - The SQS queue name provided by Qwilt.
- Regex filter - The regex provided by Qwilt.
- Select transform - Choose Default.
- Source Credential - Choose the credential you created to allow access to Qwilt resources.
- Bucket Credential - Choose the credential you created to allow access to Qwilt resources.
-
Select Add source.
Your Qwilt-Hydrolix log shipping is now set up, and you can start reading new transaction logs as they are ingested into the table you created. Data should start arriving within approximately 5 minutes. If you do not see data within this timeframe, please contact support@qwilt.com.
Setting the Log Shipping File Format
When shipped into a cloud bucket (either S3 or GCS), the transaction logs can be stored as either a TSV (tab-separated values) or a JSON file, compressed in gzip format.
Note: Logs are created every five minutes, or as soon as the log contains 50MB of data - whichever comes first. Transaction logs are pushed as soon as they are created; however, in the event of heavy traffic or communication issues, they could arrive up to 24 hours later.
The transaction logs’ file naming convention is as follows:
<file start timestamp(YYYYMMDD-HHmmSS)>.<service-name>.<unique Id>.log.gz
For example:
20230801-055130.example-service.03495.log.gz
Log Shipping MDL Field Descriptions
The following tables describe the log fields.
Note that when a field value is not available, a hyphen (-) appears instead.
For large transactions (also known as Content Range Requests or segmented transactions), Qwilt's log shipping defaults to transferring only the client transactions, referred to as 'main transactions.' These main transaction records aggregate information from all slices. Each main transaction log details the total volume served (sentBytes), the total volume read from the Origin (fetchedBytes), and the duration of all slices/sub-transactions (durationMilli). The NumSliceSubTrx field indicates the number of slices used, while NumSliceMisses represents the slices that were missed.
While this article describes the full set of MDL fields, the fields included in your MDL files may vary based on the version of the Qwilt CDN serving your traffic (CP) or in your network (SP). Additionally, the order of fields in the log data may differ.
To receive the precise set of metadata fields and their order for your specific log shipping pipeline, please contact us at support@qwilt.com.
Global Fields
This table presents the full set of MDL fields in the order in which they normally appear in the transaction log. However, your log files may differ.
If your site configuration uses custom URL tokenization, additional MDL fields will be present.
To receive the precise set of metadata fields and their order for your specific log shipping pipeline, please contact us at support@qwilt.com.
| Field | Data Type | Example Value(s) | Description |
|---|---|---|---|
| startTime | String | 13-04-23 18:17:02.883 | Formatted timestamp when the MDL record was created. Same instant as EpochTimeMsec, human-readable. |
| startTimeEpochMilli | Integer | 1681409822883 | Record creation time as Unix epoch in milliseconds. |
| durationMilli | Integer | 52 | Transaction duration in milliseconds. |
| source | String | dynamic-cdn-extension | Indicates where the content was served from — origin proxy, local cache, or self-generated error. Valid Values: dynamic-cdn-extension - The content was provided by an origin server. cache-delivery - The content was provided by the Qwilt CDN cache. self-generated - The response was generated by the Qwilt CDN cache. This value only applies to HTTP error responses. |
| flowSymmetry | String | symmetric | Reserved and relevant only in transparent caching Indicates the traffic flow direction relative to the subscriber. Valid Values: subscriber-side (1), server-side (2), symmetric (3), unknown (other). Source: F-record flowSymmetry field. |
| siteName | String | c7081-acme-live | Logical site name for reporting. System defined site name assigned automatically per Delivery Service. |
| clientIp | String: IPv4 or IPv6 address | IP address of the requesting client. May be obfuscated or empty for subrequests. | |
| clientPort | Integer | 49222 | TCP source port of the client connection. Zero for subrequests. |
| serverIp | String: IPv4 or IPv6 address | 211.94.171.32 | The IP address of the CDN cache, from which the content was delivered. |
| serverPort | Integer | 443 | The server-side TCP port. |
| sentBytes | Integer | 853222 | The number of L7 bytes transferred to the client including both content and HTTP response headers (L7Goodput). |
| httpResponseCode | Integer | 200 | HTTP status code returned to the client. |
| redirectType | String | back-to-content-provider | Indicates if/how a transaction was redirected. Valid Values: '—' (no redirect), back-to-content-provider (delivery refused to serve), on-demand-ingest (TC proxy redirect), cache-delivery (TC redirect for QN delivery). |
| requestRange | String | bytes=795346685-795548657 | Byte range from the HTTP Range request header (if present). |
| responseRange | String | bytes 795346685-795548657/986715125 | Content-Range from the HTTP response header. |
| isTransactionLastRecord | String | 1 | Indicates whether this is the final MDL record for a long-running transaction. Valid Values: 1 = final record for this transaction. 0 = intermediate record (emitted every 30s for long transactions when intermediate reporting is enabled in CLI). |
| isConsideredForQoe | String | 1 | Whether the transaction counts toward QoE calculations. A delivery service (Open Caching) can specify that its transactions should not be used for QoE calculations, causing this field to have the value 0. The normal value is 1. |
| userAgent | String | Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.5 Mobile/15E148 Safari/604.1 | HTTP User-Agent header from the client request, if the user-agent header was included in the request. |
| altDeviceName | String | N | Alternate Device Name Header from a custom/proprietary header like X-Device that only certain content providers include. Defaults to 'N' (or '-') when not present. |
| origServerIp | String | 100.100.100.100 | IP address of the origin server contacted for content fetch. |
| qServiceIdentifier | String | cds | Caching type — distinguishes the service mode of the transaction. Valid Values: tc (Transparent Caching), cds (Content Delivery Sharing / Open Caching), isp-cdn (ISP CDN, not currently used). Source: internal cachingType field. |
| dsgIdentifier | String | dsg1 | Name of the Delivery Service Group — a resource-management construct. Source: dsgName. Example: dsg1. |
| trxPrecedence | String | isp-cdn | Resource Manager priority class for this transaction. Values: preferred, reserved (per RM definitions), or falls back to the qServiceIdentifier value. |
| trxStatus | String | accepted | Transaction Status indicates whether the transaction was served or rejected. Valid Values: accepted - the Transaction was served. rejected - the Transaction was rejected. |
| trxStatusReason | String | physical-capacity-exceeded | Provides more detail about the transaction status. If rejection, provides reason or '-' if the transaction was accepted. Valid Values: ‘-’ when the transaction was accepted. physical-capacity-exceeded - The transaction was rejected because the link capacity was reached. efficiency-policy - Transaction was rejected because resources are dedicated to prioritized transactions. set-limit-exceeded - Transaction was rejected because a configured global limit was reached. |
| referer | String | https://www.geeksforgeeks.org/ | HTTP Referer header — the page that linked to the requested resource. |
| httpMethod | String | GET | HTTP method of the client request used in the transaction. Valid Values: GET, HEAD, OPTIONS |
| fetchedBytes | Integer | 853393 | The size of the HTTP content payload plus response headers for the upstream/origin transaction (L7UpstreamGoodput). |
| httpVersion | String | 1.1 | HTTP protocol version used for the client connection. |
| hostName | String | origin1.com | The HTTP Host header hostname of the request received by the OCN. Never obfuscated. Contains '—' for transactions not served by the OCN. Source: origHostName. |
| trigger | String | external | What initiated this transaction — an external client or internally by the CDN. Valid Values: external - An external client initiated the transaction. cdn - The CDN triggered the transaction. |
| serviceToken | String | r7gy-c7819-cp | System defined service token assigned per Delivery Service for Open Caching transactions. Contains '—' for non-OC transactions. |
| originHostNames | String | origin1.com,origin2.com,origin3.com | Comma-separated list of origin hostnames contacted by the CDN (in order of attempt). Shows failover sequence. This field is relevant when the ‘source’ is 'dynamic-cdn-extension.' |
| originHttpResponseCodes | String | 503,-,200 | Comma-separated HTTP response codes from each origin in OriginHostNames field. '-' in list means no response received. This field is relevant when the ‘source’ is 'dynamic-cdn-extension.' |
| cacheStatus | String | hit | How this specific cache node handled the transaction (hit/miss/etc.). Valid Values: hit: The content was served from the local cache. miss: The content was served from the origin server, because it was not available in the cache. bypass: The content was served from the origin server, even though it was available in the cache, due to configuration or specific request information. expired: The content was served from the origin server, because the content in the cache was stale. stale: Stale content was served from the local cache. updating: Stale content was served from the local cache, while a background request was sent to the origin server to update it. revalidated: Stale content was revalidated against the origin server and served from the local cache, as it was still up-to-date. |
| requestHeaders | String | key1:value1, key2:value2 | Selected HTTP request headers as key-value pairs. Delivery Service configuration determines which headers are reported (if any). Format: key1:value1,key2:value2,.... Special chars (tab, :, ,, %) are URL-encoded. '—' if none reported. |
| responseHeaders | String | key1:value1, key2:value2 | Selected HTTP response headers as key-value pairs. Delivery Service configuration determines which headers are reported (if any). Format: key1:value1, key2:value2,.... Special chars (tab, :, ,, %) are URL-encoded. '—' if none reported. |
| deliveryHopCount | Integer | 1 | Contains the position of this cache in the chain of caches, from the Edge server to origin (including the Edge). The position always starts from 1, where 1 indicates that this cache is the Edge. |
| deliveryChainLength | Integer | 2 | Total number of CDN nodes in the delivery chain from edge to origin. Valid Values 1 = single tier, 2 = edge + mid. Chain length is always at least 1. |
| fetchType | String | peer | Indicates how content was acquired upstream. Valid Values: peer - fetched from another Cache Node in same cache group / intra-PoP parent - fetched from Cache Node in different cache group / multi-tier '—' - fetched from origin or served locally. |
| transactionId | String | 10000000397c92e9 | Globally unique ID for a transaction delivered by the Cache Node (from cache or fetched). '-' for non-delivered transactions. |
| transactionChainId | String | 10000000397c92e9-88108992-592 | Shared ID linking all MDL records across multiple Cache Nodes handling the same client request in a transaction chain (multi-tier). |
| sslVersion | String | TLSv1.3 | The TLS/SSL version negotiated between the Cache Node and the client. Example values: TLSv1.1, TLSv1.2, TLSv1.3. Source: cSslVersion. '—' if not available. |
| timeToFirstByte | Integer | 300 | Time (in milliseconds) from request processing start (Cache Node received the request) to first response byte written to socket. |
| clientAsn | Integer | 23456 | Autonomous System Number (ASN) of the client IP. |
| clientCountryCode | String | UK | Two-letter country code of the client IP (ISO 3166-1 alpha-2 lowercase). |
| dataType | String | content | Whether this is a regular content transaction or a router-proxy transaction. |
| downstreamType | String | peer | Relationship of downstream to this Cache Node: Valid Values: peer (same Cache Group), child (different Cache Group), internal (sub-transaction), or '-' (client). |
| triggerTrxId | String | 10000004776d9176-3-1 | Client transaction ID. |
| subTrxType | String | slice | The transaction type. Valid Values: slice: indicating that this is a slice/sub-transaction. other: Non slice. main-sliced: client-facing transaction which was sliced. |
| sliceRange | String | 1024-1535 | Byte range this slice sub-transaction handles (for a triggered sub-transaction of the type slice). |
| cdnCacheStatus | String | miss | Aggregated cache status across ALL slices. Reports 'miss' if ANY sub-transaction fetched from origin. Provides a single rolled-up view vs. per-slice CacheStatus. |
| edgeLocation | String | UK | Two-letter country code of the serving Cache Node (ISO 3166-1 alpha-2 lowercase). |
| xForwardedFor | String | 203.0.113, 198.51.100, 192.0.2 | If the client request includes the HTTP X-Forwarded-For header, this field contains the list of IP addresses specified by the HTTP X-Forwarded-For request header, with the first IP address masked (shown as a subnet in CIDR notation, e.g., <ip-address>/<mask-size>). For non-obfuscated first IP address, see field 'xForwardedForPlainText'. |
| originTTFB | Integer | 200 | Time (in milliseconds) from sending the request to origin until receiving the first response byte from origin. |
| cmcdSid | String | 6e2fb550—c457—11e9—bb97—0800200c9a66 | CMCD (Common Media Client Data) Session ID from the client request, if CMCD is enabled. |
| numSliceSubTrx | Number | 20 | Number of slice sub-transactions triggered by this client-facing transaction (for client-facing large transactions). |
| numSliceMisses | Number | 1 | Number of slice sub-transactions that were cache misses for client-facing large transactions (miss/bypass/expired). If not client facing large transaction, then it holds 0. |
| topTierCacheStatus | String | - | Cache status from the top-most (furthest upstream) cache in a multi-tier chain. Contains '-' if no other Cache Node was involved in the transaction. |
| subnet | String | 83.219.163.0/24 | Client IP masked to a subnet (mask size set by config). |
| responseRttMsec | Integer | 42 | TCP round-trip time (RTT) measured after the socket handshake with the client (milliseconds). |
| contentType | String | text/html;charset=utf-8 | Content-Type HTTP header of the response. |
| cSslCipher | String | TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 | SSL/TLS cipher suite negotiated for the client connection. |
| cacheKey | String | 6e2fb550—c457—11e9—bb97—0800200c9a66 | Unique hash key identifier of a Cache Node. For non-caching components like static content and test objects, the value may be empty or NA. |
| upstreamDuration | Numeric | 1 | The time (milliseconds) it took to receive content from the previous tier (another cache or the origin). This value reflects a single hop. It is measured from the receipt of the first byte to the receipt of the last byte. |
| clientIpPlaintext | String | 83.219.163.0 | Client IP in plaintext (unmasked). This is in addition to the clientIp field that displays the obfuscated IP. Legal approval is required to expose the IP in plaintext. If privacy regulations prevent sharing the IP in plaintext, the field value is -. |
| xForwardedForPlaintext | String | 203.0.113.1, 198.51.100.1, 192.0.2.1 | If the client request includes the HTTP X-Forwarded-For header, this field displays the original IP address of the client in plaintext. This is in addition to the xForwardedFor field which shows the original client IP address in subnet form. Legal approval is required to expose the IP in plaintext. If privacy regulations prevent sharing the IP in plaintext, the field value is -. |
| serviceType | String | vod | The type of service used to deliver the content (e.g. VOD, Live, Software Download). Maps to the traffic-type value in the site configuration JSON. |
Fields Relevant Only to Content Providers
| Field | Data Type | Example Value(s) | Description |
|---|---|---|---|
| l7Throughput | Integer | 131265 | The calculated L7 throughput of the transaction in bits per second (bps). |
| uri | String | http://qb1.my—cdn.com/some/path | The client URL. |
| LocationAclRuleName | String | DENY:anyonymous-users ALLOW:123.211.0.0/16, 60.10.128.0/18, 192.123.80.0/20 |
Includes the action applied (allow or deny) and a description of the match condition defined by the ACL rule, such as the specified named list, CIDR blocks, ASNs, or country codes. |
Fields Relevant Only to Service Providers
| Field | Data Type | Example Value(s) | Description |
|---|---|---|---|
| downstreamSystemId | String | XYZ123 | The System ID of the downstream system. |
| upstreamSystemId | String | XYZ124 | The System ID of the upstream system. |