<?xml version="1.0" encoding="utf-8"?>
<?xml-model href="rfc7991bis.rnc"?>


<!DOCTYPE rfc [
  <!ENTITY nbsp    "&#160;">
  <!ENTITY zwsp   "&#8203;">
  <!ENTITY nbhy   "&#8209;">
  <!ENTITY wj     "&#8288;">
]>

<rfc
  xmlns:xi="http://www.w3.org/2001/XInclude"
  category="std"
  ipr="trust200902"
  obsoletes=""
  updates=""
  submissionType="IETF"
  xml:lang="en"
  version="3"
  tocInclude="true"
  tocDepth="5"
  symRefs="true"
  sortRefs="true"
  consensus="true"
  docName="draft-ietf-cdni-logging-extensions-03"
>
   <front>
      <title abbrev="CDNI Logging Extensions">CDNI Logging Extensions</title>
      <author initials="B." surname="Rosenblum" fullname="Ben Rosenblum">
         <organization>Vecima</organization>
         <address>
            <postal>
               <street />
               <city />
               <region />
               <code />
               <country>US</country>
            </postal>
            <email>ben@rosenblum.dev</email>
         </address>
      </author>
      <author initials="O." surname="Ramadan" fullname="Omar Ramadan">
         <organization>Blockcast</organization>
         <address>
            <postal>
               <street />
               <city />
               <region />
               <code />
               <country></country>
            </postal>
            <email>omar@blockcast.net</email>
         </address>
      </author>
      <author initials="K." surname="Seward" fullname="Kenton Seward">
         <organization>Lumen</organization>
         <address>
            <postal>
               <street />
               <city />
               <region />
               <code />
               <country></country>
            </postal>
            <email>kenton.seward@gmail.com</email>
         </address>
      </author>
      <date />
      <workgroup>Content Delivery Networks Interconnection</workgroup>
      <abstract>
         <t>
                 This document defines a set of extensions to CDNI for
                 supporting transmission of transaction logs in both push and
                 pull operational modes, new log container formats and log record formats,
                 new logging fields, and metadata for describing the transformation,
                 obfuscation, and encryption of log record fields.
        </t>
      </abstract>
   </front>
<middle>
<section anchor="h.3znysh7">
  <name>Introduction</name>
  <figure title="Logging Component Overview, Pull Operational Mode">
   <artset>
      <!--<artwork type="svg" src="figure_1.svg" />-->
      <artwork type="ascii-art"><![CDATA[
        [ TODO: Add ASCII art diagram describing Pull Operational Mode ]
  ]]>
      </artwork>
   </artset>
</figure>

  <figure title="Logging Component Overview, Push Operational Mode">
  <artset>
    <!--<artwork type="svg" src="figure_2.svg" />-->
    <artwork type="ascii-art"><![CDATA[
      [ TODO: Add ASCII art diagram describing Push Operational Mode ]
  ]]>
    </artwork>
  </artset>
</figure>

  <t>The CDNI Logging Interface <xref target="RFC7937"/> defines requirements for the exchange of log data between CDN participants, including a Logging Reference Model, a Logging File Structure, and Logging Records, along with an Atom-based index for retrieving the generated log files. This specification expands on the functionality defined in the CDNI Logging Interface <xref target="RFC7937"/> to provide clear definitions and an expanded set of log record fields, additional mechanisms for transporting logs, new log container formats, log archive files, logging metadata sidecar files, new log record formats, configured inclusion of OPTIONAL request and response header fields, and methods for advertising and configuring transformations of individual log fields.</t>
  <t>In the future, this specification will be expanded to cover other CDN component logs (e.g., request router), custom log container formats, and log record formats, as well as uCDN-controlled configuration for aggregation, filtering, and sampling of log records.</t>
  <t>The following subsections summarize the importance of logging in the context of content acquisition and its delivery by surrogates on behalf of the CDN.</t>
  <section anchor="h.8a40ainghrce">
    <name>Log Generation</name>
    <t>As described in section 2.2.1 of the CDNI Logging Interface <xref target="RFC7937"/>, logging is generated by the downstream CDN (dCDN), which applies to the OCS in this context, to record all significant task completions, events, and failures. It is expected that all devices acting as caching nodes SHALL generate logging information, including devices performing request routing and other control functions. It is to be noted that this does not preclude open caching nodes (OCNs) from generating and reporting transactions that are in progress and not fully completed, such as progressive downloads that may run for a longer duration and are therefore considered good candidates for partial reporting prior to fully completing a transaction. The individual implementations are, however, expected to make a determination as to when it is appropriate to generate reports for partial transactions.</t>
  </section>
  <section anchor="h.3atbty3srhnx">
    <name>Log Retention</name>
    <t>The OCN SHALL retain logs for a specified duration. The specific duration is up to individual implementations to determine. Presumably, in the absence of logs being uploaded to or acquired by the CDN, it is possible the size of logging information would grow exponentially, therefore, it is also RECOMMENDED to define a shorter duration for upload/acquisition, such as a period of a few hours.</t>
  </section>
  <section anchor="h.gq5d9l4fwcdk">
    <name>Log Record Sampling</name>
    <t>To manage the volume of logging data, a dCDN MAY support sampling of log records. When supported, a uCDN can configure a sampling strategy using metadata objects to reduce the number of generated log records while retaining a representative subset of data. Additionally, the dCDN can apply sampling to pull-only logging sources. The dCDN advertises its sampling capabilities via properties on advertisement interface capability objects.</t>
  </section>
  <section anchor="h.9n2daqeekdb1">
    <name>Accounting</name>
    <t>Logging information is essential for accounting. The logging information provided by the OCS enables the CDNs to compute the total amount of traffic delivered by every OCN for a given content service provider (CSP), as well allow the CDNs to determine associated bandwidth usage (peak or percentile) and key performance indicators, such as maximum (or minimum) number of simultaneous sessions over a given period of time.</t>
  </section>
  <section anchor="h.6cghz5syfzqe">
    <name>Analytics And Reporting</name>
    <t>The goals of analytics include gathering all relevant information in order to be able to develop statistics on content download, analyze user behavior, and monitor the performance and quality of content delivery. For instance, logging information enables CDN providers to report on content consumption (e.g., delivered sessions per content) in a specific geographic area.</t>
    <t>The goal of reporting is to gather all relevant information to monitor the performance and quality of content delivery, and allow detection of delivery issues. For instance, reporting could track the average delivery throughput experienced by end users in a given region for a specific CSP or content set over a period of time.</t>
  </section>
  <section anchor="h.8o1ytvpzsap5">
    <name>Content Protection</name>
    <t>The goal of content protection is to monitor and prevent unauthorized access, misuse, modification, and denial of access to content. A set of information is logged in a CDN (by an OCN) for security purposes. In particular, a record of access to content is usually collected to permit the CSP to detect infringements of content delivery policies and other abnormal end-user behaviors.</t>
  </section>
  <section anchor="h.7s6sdqzdnafv">
    <name>Private Data Protection</name>
    <t>As described in section <xref target="h.993807dxae2p">10.4</xref>, the goal of logging transforms is to modify the outputs of fields that may include private information to comply with the confidentiality requirements of both the uCDCN and dCDN. The uCDN configurations are bundled within the transport configuration to support configurations that may be specific to the jurisdictions of individual reporting endpoints and service footprints. Additionally, the dCDN can advertise field transformations that have been applied in the footprint and capabilities interface.</t>
  </section>
</section><section anchor="requirements" title="Requirements">
<t>The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in <xref target="RFC2119"/>.</t>
</section><section anchor="h.whuucahungsa">
  <name>Container File Formats</name>
  <t>For non-streaming logging formats, the container/file format describes the structure of the file that contains the individual log records.</t>
  <t>The following container/file formats are supported:</t>
  <ul>
    <li>
      <t>CDNI Logging File described in section 3 of <xref target="RFC7937"/>, inspired by W3C Extended Log File Format (cdni_v1)</t>
    </li>
    <li>
      <t>JSON (json_v1)</t>
    </li>
    <li>
      <t>Newline delimited records:</t>
      <ul>
        <li>
          <t>\n (linefeed_v1)</t>
        </li>
        <li>
          <t>\n\r (carriage_return_v1)</t>
        </li>
      </ul>
    </li>
    <li>
      <t>Compressed Archive containers (tar.gz) that support inclusion of multiple files in other container formats (tar_v1)</t>
    </li>
    <li>
      <t>protobuf containers, according to the record types definitions in section <xref target="h.2ig4356urxei">10.</xref> </t>
    </li>
  </ul>
  <section anchor="h.jj5kmn1rfme7">
    <name>JSON</name>
    <t>The JSON container/file format consists of a single root object with file metadata, fields common to all records, and an array of log records.</t>
    <t>Where the JSON format MUST be referenced from other configurations (e.g., in the container-format property of MI.LoggingTransport), it MAY be referred to by the string: "json_v1"</t>
    <t>Property: shortname  </t>
    <ul>
      <li>
        <t>Description: A unique identifier for the dCDN.</t>
      </li>
      <li>
        <t>Type: String</t>
      </li>
      <li>
        <t>Mandatory-to-Specify: No</t>
      </li>
    </ul>
    <t>Property: timestamp-start-ns </t>
    <ul>
      <li>
        <t>Description: The starting timestamp for the time range for which the log file was generated. This can be earlier than the timestamp of the file's first log record.</t>
      </li>
      <li>
        <t>Type: Integer time in nanoseconds since Unix epoch</t>
      </li>
      <li>
        <t>Mandatory-to-Specify: No</t>
      </li>
    </ul>
    <t>Property: timestamp-start-iso8601 </t>
    <ul>
      <li>
        <t>Description: The starting timestamp for the time range for which the log file was generated. This can be earlier than the timestamp of the file's first log record.</t>
      </li>
      <li>
        <t>Type: ISO-8601-1:2019</t>
      </li>
      <li>
        <t>Mandatory-to-Specify: No</t>
      </li>
    </ul>
    <t>Property: timestamp-end-ns </t>
    <ul>
      <li>
        <t>Description: The ending timestamp for the time range for which the log file was generated. This can be later than the timestamp of the file's last log record.</t>
      </li>
      <li>
        <t>Type: Integer time in nanoseconds since Unix epoch</t>
      </li>
      <li>
        <t>Mandatory-to-Specify: No</t>
      </li>
    </ul>
    <t>Property: timestamp-end-iso8601 </t>
    <ul>
      <li>
        <t>Description: The ending timestamp for the time range for which the log file was generated. This can be later than the timestamp of the file's last log record.</t>
      </li>
      <li>
        <t>Type: ISO-8601-1:2019</t>
      </li>
      <li>
        <t>Mandatory-to-Specify: No</t>
      </li>
    </ul>
    <t>Property: account-id</t>
    <ul>
      <li>
        <t>Description: Account ID as defined by the account-id property of MI.LoggingMetadata.</t>
      </li>
      <li>
        <t>Type: String</t>
      </li>
      <li>
        <t>Mandatory-to-Specify: No</t>
      </li>
    </ul>
    <t>Property: metadata</t>
    <ul>
      <li>
        <t>Description: Metadata associated with configuration of the logging records, e.g., associated record-type.</t>
      </li>
      <li>
        <t>Type: Object. Valid properties:</t>
        <ul>
          <li>
            <t>record-type: String. The record type string defining the format of included logging records. See section <xref target="h.jdws9pempj0a">6.</xref></t>
          </li>
          <li>
            <t>transforms: Object. The MI.LoggingTransforms object applied to the record set. See section <xref target="h.993807dxae2p">7.4.</xref></t>
          </li>
          <li>
            <t>secret-stores: Array. Any MI.SecretStore objects referenced from MI.LoggingTransforms.</t>
          </li>
          <li>
            <t>include-headers: Array. Any optionally included request or response header fields. See section <xref target="h.doju1kdl0y9l">7.1.</xref></t>
          </li>
        </ul>
      </li>
      <li>
        <t>Mandatory-to-Specify: Yes</t>
      </li>
    </ul>
    <t>Property: records </t>
    <ul>
      <li>
        <t>Description: The log records consisting of an array wherein all properties are valid log record fields as described in Section <xref target="h.coa696tkzovl">5.</xref>  </t>
      </li>
      <li>
        <t>Type: Array of objects</t>
      </li>
      <li>
        <t>Mandatory-to-Specify: Yes</t>
      </li>
    </ul>
    <t>The following is an example of the JSON container format:</t>
    <figure>
      <sourcecode><![CDATA[{
  "shortname": "exampleCDN",
  "timestamp-start-iso8601": "2023-01-15T14:00:00Z",
  "timestamp-end-iso8601": "2023-01-15T14:59:59Z",
  "account-id": "foobarMedia",
  "metadata": {
    "record-type": "opencaching_standard_json_v1",
    "transforms": [
      {
        "record-fields": [
                 "c-ip"
             ],
        "operations": [
                 {
            "type": "MI.LoggingTransformMaskIp",
            "value": {
              "mask-lsb-v4": 4,
              "mask-lsb-v6": 16
            }
          }
             ]
      }
    ]
  },
  "records": [
    {
      "timestamp-ns": 1672938865000000000,
      "cs-uri": "https://cdn.example.com/assets/b874bc5b/index.m3u8",
      "sc-status": "200",
      "sc-total-bytes": 1784,
      "s-upstream-header-size-bytes": 208,
      "s-upstream-total-bytes": 1784
    }
  ]
}]]></sourcecode>
    </figure>
    <section anchor="h.58zpgabthxzl">
      <name>JSON Metadata Sidecar</name>
      <t>A metadata sidecar file MAY be used as a companion to other record container files, allowing detailed metadata as specified in the JSON container format.</t>
      <t>Metadata sidecars are identical to the full JSON container format except that the records property is NOT included.</t>
      <t>Sidecar files are identified by a file naming convention; the corresponding sidecar for a particular log file has the same name as the log file with an appended suffix of ".metadata.json".</t>
      <t>For example, a log file named "2023-01-01-12_00_00-region1.csv" would have a corresponding metadata sidecar file named "2023-01-01_12_00_00-region1.csv.metadata.json".</t>
      <t>dCDN support for metadata sidecars is indicated with the allow-metadata-sidecar property of FCI.LoggingPush Use of metadata sidecars for a particular configuration is indicated by the include-metadata-sidecar boolean property in MI.LoggingTransport and FCI.LoggingPullTransport See section <xref target="h.3gcqfmy6uk0z">7.3</xref> and section <xref target="h.2t1wmmgu4hpb">8.3</xref></t>
    </section>
  </section>
  <section anchor="h.oh46tz42py9k">
    <name>Newline Delimited Records</name>
    <t>A newline delimited container consists of individual log records separated by a linefeed (UNIX) or linefeed plus carriage return (Windows). The format of the log records is specified independently of the record delimiter.</t>
    <table>
      <thead>
        <tr>
          <th>format-type</th>
          <th>Description</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td>linefeed_v1</td>
          <td>Log records separated by linefeed (\n).</td>
        </tr>
        <tr>
          <td>carriage_return_v1</td>
          <td>Log records separated by linefeed, carriage return (\n\r).</td>
        </tr>
      </tbody>
    </table>
    <t>The following is an example of the Newline Delimited container format:</t>
    <figure>
      <sourcecode><![CDATA[1672579231000000,US/TN/MEM/38138,https://cdn.example.com/content/index.m3u8,200,14678,EXAMPLECDN,14678
1672579289000000,US/TN/MEM/38138,https://cdn.example.com/content/1.m4s,200,3887658,EXAMPLECDN,3887658]]></sourcecode>
    </figure>
  </section>
  <section anchor="h.4cvxv7jdkomn">
    <name>Archive Containers</name>
    <t>File-based transport modes MAY bundle a set of log files together in a gzipped tar archive. When utilizing tar_v1 as the container-type, the properties of the MI.LoggingContainerMetadata object refer to the generation of the archive file, and the archive-metadata property of that object contains another MI.LoggingContainerMetadata object that specifies the configuration for the individual files within that archive.</t>
    <t>For example, if the archive container is specified with an interval of 86400, and the inner archive-metadata has an interval of 3600, a tar.gz archive will be produced once a day containing 24 individual log files, each containing one hour of log records.</t>
  </section>
  <section anchor="h.846yu5ttx32k">
    <name>Protobuf Containers</name>
    <t>Each log MAY be serialized using protobuf protocol buffers. This document provides the protobuf definition languages (.proto files) for different record types in Appendix section <xref target="h.pv2ceok296tc">10.1</xref></t>
  </section>
</section><section anchor="h.coa696tkzovl">
  <name>Record Fields</name>
  <t>The following table describes the log record fields that are included in each of the standard log record formats. Y - Yes, N - No, O - Optional.</t>
  <table>
    <thead>
      <tr>
        <th>Field</th>
        <th>Minimal</th>
        <th>Standard</th>
        <th>Extended</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>timestamp-ns</td>
        <td>Y</td>
        <td>Y</td>
        <td>Y</td>
      </tr>
      <tr>
        <td>timestamp-iso8601</td>
        <td>N</td>
        <td>N</td>
        <td>Y</td>
      </tr>
      <tr>
        <td>s-time-total-ms</td>
        <td>N</td>
        <td>Y</td>
        <td>Y</td>
      </tr>
      <tr>
        <td>c-groupid</td>
        <td>N</td>
        <td>N</td>
        <td>Y</td>
      </tr>
      <tr>
        <td>cs-method</td>
        <td>N</td>
        <td>N</td>
        <td>Y</td>
      </tr>
      <tr>
        <td>cs-version</td>
        <td>N</td>
        <td>N</td>
        <td>Y</td>
      </tr>
      <tr>
        <td>cs-uri</td>
        <td>Y</td>
        <td>Y</td>
        <td>Y</td>
      </tr>
      <tr>
        <td>cs-uri-transformed</td>
        <td>N</td>
        <td>N</td>
        <td>Y</td>
      </tr>
      <tr>
        <td>sc-status</td>
        <td>Y</td>
        <td>Y</td>
        <td>Y</td>
      </tr>
      <tr>
        <td>sc-total-bytes</td>
        <td>Y</td>
        <td>Y</td>
        <td>Y</td>
      </tr>
      <tr>
        <td>s-ccid</td>
        <td>Y</td>
        <td>Y</td>
        <td>Y</td>
      </tr>
      <tr>
        <td>s-sid</td>
        <td>N</td>
        <td>Y</td>
        <td>Y</td>
      </tr>
      <tr>
        <td>s-cached</td>
        <td>N</td>
        <td>Y</td>
        <td>Y</td>
      </tr>
      <tr>
        <td>cs-hdr-Referer</td>
        <td>N</td>
        <td>N</td>
        <td>Y</td>
      </tr>
      <tr>
        <td>cs-hdr-Range</td>
        <td>N</td>
        <td>N</td>
        <td>Y</td>
      </tr>
      <tr>
        <td>cs-hdr-User-Agent</td>
        <td>N</td>
        <td>Y</td>
        <td>Y</td>
      </tr>
      <tr>
        <td>sc-hdr-Content-Type</td>
        <td>N</td>
        <td>N</td>
        <td>Y</td>
      </tr>
      <tr>
        <td>sc-header-size-bytes</td>
        <td>N</td>
        <td>N</td>
        <td>Y</td>
      </tr>
      <tr>
        <td>c-ip</td>
        <td>N</td>
        <td>Y</td>
        <td>Y</td>
      </tr>
      <tr>
        <td>s-shortname</td>
        <td>N</td>
        <td>Y</td>
        <td>Y</td>
      </tr>
      <tr>
        <td>s-id</td>
        <td>N</td>
        <td>N</td>
        <td>Y</td>
      </tr>
      <tr>
        <td>s-time-first-ms</td>
        <td>N</td>
        <td>N</td>
        <td>Y</td>
      </tr>
      <tr>
        <td>s-sdur-ms</td>
        <td>N</td>
        <td>N</td>
        <td>Y</td>
      </tr>
      <tr>
        <td>s-time-upstream-ms</td>
        <td>N</td>
        <td>N</td>
        <td>Y</td>
      </tr>
      <tr>
        <td>s-upstream-header-size-bytes</td>
        <td>Y</td>
        <td>Y</td>
        <td>Y</td>
      </tr>
      <tr>
        <td>s-upstream-total-bytes</td>
        <td>Y</td>
        <td>Y</td>
        <td>Y</td>
      </tr>
      <tr>
        <td>s-upstream-status</td>
        <td>N</td>
        <td>N</td>
        <td>Y</td>
      </tr>
      <tr>
        <td>s-upstream-ip</td>
        <td>N</td>
        <td>N</td>
        <td>Y</td>
      </tr>
      <tr>
        <td>sc-hdr-*, cs-hdr-*</td>
        <td>O</td>
        <td>O</td>
        <td>O</td>
      </tr>
    </tbody>
  </table>
  <section anchor="h.9saug1t3kp86">
    <name>Fields</name>
    <section anchor="h.89zt1i7h7ggw">
      <name>timestamp-ns</name>
      <ul>
        <li>
          <t>Description: The timestamp of the first byte read by the OCN application to a best effort of accuracy.</t>
        </li>
        <li>
          <t>Format: Integer time in nanoseconds since Unix epoch</t>
        </li>
        <li>
          <t>Record Types: Minimal, Standard, Extended</t>
        </li>
      </ul>
    </section>
    <section anchor="h.snzz1sc6ysjl">
      <name>timestamp-iso8601</name>
      <ul>
        <li>
          <t>Description: The timestamp of the first byte read by the OCN application to a best effort of accuracy. It is RECOMMENDED that timestamps use Coordinated Universal Time (UTC).</t>
        </li>
        <li>
          <t>Format: Time in ISO-8601/RFC3339 format, milliseconds OPTIONAL (e.g., 1985-04-12T23:20:50.52Z)</t>
        </li>
        <li>
          <t>Record Types: Extended</t>
        </li>
      </ul>
    </section>
    <section anchor="h.3yaam6ntk4cx">
      <name>s-time-total-ms</name>
      <ul>
        <li>
          <t>Description: The elapsed time between reading the first byte of the request and sending the last byte of the response to a best effort of accuracy.</t>
        </li>
        <li>
          <t>Format: Duration, in milliseconds</t>
        </li>
        <li>
          <t>Record Types: Standard, Extended</t>
        </li>
      </ul>
    </section>
    <section anchor="h.q8iqbe4bwnsd">
      <name>c-groupid</name>
      <ul>
        <li>
          <t>Description: An opaque identifier for an aggregate set of clients, derived from the client IPv4 or IPv6 address in the request received by the surrogate and/or other network-level identifying information. See <xref target="RFC7937"/> section 3.4.1.</t>
        </li>
        <li>
          <t>Format: String</t>
        </li>
        <li>
          <t>Record Types: Minimal, Standard, Extended</t>
        </li>
      </ul>
    </section>
    <section anchor="h.t85hlqjhr83y">
      <name>cs-method</name>
      <ul>
        <li>
          <t>Description: The HTTP request method.</t>
        </li>
        <li>
          <t>Format: String</t>
        </li>
        <li>
          <t>Record Types: Extended</t>
        </li>
      </ul>
    </section>
    <section anchor="h.tjxviwg4jafa">
      <name>cs-version</name>
      <ul>
        <li>
          <t>Description: The protocol version (e.g., HTTP/1.1).</t>
        </li>
        <li>
          <t>Format: String</t>
        </li>
        <li>
          <t>Record Types: Extended</t>
        </li>
      </ul>
    </section>
    <section anchor="h.p1ck1z6t07rh">
      <name>cs-uri</name>
      <ul>
        <li>
          <t>Description: Request URI prior to any transformation from MI.ProcessingStages. It is RECOMMENDED to limit the maximum length of this field with an applied MI.LoggingTransformTruncate, allowing at least 2,048 bytes.</t>
        </li>
        <li>
          <t>Format: String</t>
        </li>
        <li>
          <t>Record Types: Minimal, Standard, Extended</t>
        </li>
      </ul>
    </section>
    <section anchor="h.9u25olqzua3w">
      <name>cs-uri-transformed</name>
      <ul>
        <li>
          <t>Description: Full request URI, including query parameters. If this field is identical to cs-uri, the field SHOULD be truncated to an empty string.</t>
        </li>
        <li>
          <t>Format: String</t>
        </li>
        <li>
          <t>Record Types: Extended</t>
        </li>
      </ul>
    </section>
    <section anchor="h.oqz0gt2jvael">
      <name>sc-status</name>
      <ul>
        <li>
          <t>Description: The response status code.</t>
        </li>
        <li>
          <t>Format: String, 3 digit status code</t>
        </li>
        <li>
          <t>Record Types: Minimal, Standard, Extended</t>
        </li>
      </ul>
    </section>
    <section anchor="h.fnjyfruhphnh">
      <name>sc-total-bytes</name>
      <ul>
        <li>
          <t>Description: The response size total bytes, including headers.</t>
        </li>
        <li>
          <t>Format: Integer</t>
        </li>
        <li>
          <t>Record Types: Minimal, Standard, Extended</t>
        </li>
      </ul>
    </section>
    <section anchor="h.mia3cp2owfqj">
      <name>s-ccid (ccid)</name>
      <ul>
        <li>
          <t>Description: The ccid assigned via MI.Grouping or MI.GroupingExtended configuration. For backward compatibility, s-ccid is aliased to ccid, and either identifier may be used to refer to this field.</t>
        </li>
        <li>
          <t>Format: String</t>
        </li>
        <li>
          <t>Record Types: Minimal, Standard, Extended</t>
        </li>
      </ul>
    </section>
    <section anchor="h.4vxi9ibohz2p">
      <name>s-sid</name>
      <ul>
        <li>
          <t>Description: The Session ID assigned by the dCDN. This can be derived from Common Media Client Data (CMCD) or another mechanism, but it is defined by the dCDN, and uCDN allocated session identifiers are outside the scope of this field.</t>
        </li>
        <li>
          <t>Format: String</t>
        </li>
        <li>
          <t>Record Types: Standard, Extended</t>
        </li>
      </ul>
    </section>
    <section anchor="h.yb8mdeu0w9fz">
      <name>s-cached</name>
      <ul>
        <li>
          <t>Description: Indicates a cache hit or cache miss. For this field, a cache hit is defined as a request that does not result in content retrieval from the upstream source.</t>
        </li>
        <li>
          <t>Format: Boolean; a value of "True" indicates a cache hit.</t>
        </li>
        <li>
          <t>Record Types: Standard, Extended</t>
        </li>
      </ul>
    </section>
    <section anchor="h.j8fc8m80h7lk">
      <name>s-upstream-status</name>
      <ul>
        <li>
          <t>Description: The response status code returned by the upstream source in the event of a cache miss.</t>
        </li>
        <li>
          <t>Format: String, 3 digit status code</t>
        </li>
        <li>
          <t>Record Types: Extended</t>
        </li>
      </ul>
    </section>
    <section anchor="h.3un1sdox46yl">
      <name>cs-hdr-*</name>
      <ul>
        <li>
          <t>Description: The request headers as available from the dCDN and specified in FCI.Logging In the standard record type, only the cs-hdr-User-Agent header is included, if available. The extended record type additionally includes cs-hdr-Range and cs-hdr-Referer See the <xref target="h.coa696tkzovl">Record Fields Table</xref> for reference.</t>
        </li>
        <li>
          <t>Format: String or null if the field does not exist (or is not supported by record encoding)</t>
          <ul>
            <li>
              <t>Special care needs to be taken while composing the value of these fields as the requested HTTP header name MAY be present multiple times in the request. When such cases are encountered, implementations are expected to follow the semantics defined for combining HTTP field values in <xref target="RFC9110"/> sections 5.2 and 5.3, in order to compose the final value to be logged for this field in the record.</t>
            </li>
          </ul>
        </li>
        <li>
          <t>Record Types: Standard, Extended</t>
        </li>
      </ul>
    </section>
    <section anchor="h.arh6lx2szrr">
      <name>sc-hdr-*</name>
      <ul>
        <li>
          <t>Description: The response headers as available from the dCDN and specified in FCI.Logging In the extended type, sc-hdr-Content-Type is always included. See the <xref target="h.coa696tkzovl">Record Fields Table</xref> for reference.</t>
        </li>
        <li>
          <t>Format: String or null if the field does not exist (or is not supported by record encoding)</t>
          <ul>
            <li>
              <t>Special care needs to be taken while composing the value of these fields as the requested HTTP header name MAY be present multiple times in the response. When such cases are encountered, implementations are expected to follow the semantics defined for combining HTTP field values in <xref target="RFC9110"/> sections 5.2 and 5.3, in order to compose the final value to be logged for this field in the record.</t>
            </li>
          </ul>
        </li>
        <li>
          <t>Record Types: Standard, Extended </t>
        </li>
      </ul>
    </section>
    <section anchor="h.8v6srcbxvo41">
      <name>sc-header-size-bytes</name>
      <ul>
        <li>
          <t>Description: The response header size bytes.</t>
        </li>
        <li>
          <t>Format: Integer</t>
        </li>
        <li>
          <t>Record Types: Extended</t>
        </li>
      </ul>
    </section>
    <section anchor="h.p7jv809gmvwf">
      <name>c-ip</name>
      <ul>
        <li>
          <t>Description: The client IP address.</t>
        </li>
        <li>
          <t>Format: String</t>
        </li>
        <li>
          <t>Record Types: Standard, Extended</t>
        </li>
      </ul>
    </section>
    <section anchor="h.9mm7ysvgpqls">
      <name>s-shortname</name>
      <ul>
        <li>
          <t>Description: A short name for the dCDN intended to be used as an identifier, e.g. in log records. Example: "CDN Example Company" might have s-shortname of "cdn-ec".</t>
        </li>
        <li>
          <t>Format: String</t>
        </li>
        <li>
          <t>Record Types: Standard, Extended</t>
        </li>
      </ul>
    </section>
    <section anchor="h.cdrq6134bc4z">
      <name>s-id</name>
      <ul>
        <li>
          <t>Description: The server instance ID assigned by the dCDN. This value SHOULD be unique to a particular instance.</t>
        </li>
        <li>
          <t>Format: String</t>
        </li>
        <li>
          <t>Record Types: Extended</t>
        </li>
      </ul>
    </section>
    <section anchor="h.oqj0x6ob1k5x">
      <name>s-time-first-ms</name>
      <ul>
        <li>
          <t>Description: The elapsed time from the first request byte read until the first response byte sent.</t>
        </li>
        <li>
          <t>Format: Integer duration, in milliseconds</t>
        </li>
        <li>
          <t>Record Types: Extended</t>
        </li>
      </ul>
    </section>
    <section anchor="h.yzp5u5cojfs2">
      <name>s-sdur-ms</name>
      <ul>
        <li>
          <t>Description: The session duration as determined by the dCDN.</t>
        </li>
        <li>
          <t>Format: Integer duration, in milliseconds</t>
        </li>
        <li>
          <t>Record Types: Extended</t>
        </li>
      </ul>
    </section>
    <section anchor="h.q27gw1t3lpgx">
      <name>s-time-upstream-ms</name>
      <ul>
        <li>
          <t>Description: The time spent accessing the upstream source. The value of this field is a "best effort". If there are intermediate cache layers, it is understood that this value may not accurately reflect the complete upstream picture.</t>
        </li>
        <li>
          <t>Format: Integer duration, in milliseconds. If no upstream is accessed, the duration is 0.</t>
        </li>
        <li>
          <t>Record Types: Extended</t>
        </li>
      </ul>
    </section>
    <section anchor="h.1b9g3953ovgm">
      <name>s-upstream-header-size-bytes</name>
      <ul>
        <li>
          <t>Description: Content header length from the upstream source. The value of this field is a "best effort". If there are intermediate cache layers, it is understood that this value may not accurately reflect the complete upstream picture.</t>
        </li>
        <li>
          <t>Format: Integer, size in bytes. If no upstream is accessed, the size is 0.</t>
        </li>
        <li>
          <t>Record Types: Minimal, Standard, Extended</t>
        </li>
      </ul>
    </section>
    <section anchor="h.u9unrni2dkoq">
      <name>s-upstream-total-bytes</name>
      <ul>
        <li>
          <t>Description: Total bytes transferred from the upstream source. The value of this field is a "best effort". If there are intermediate cache layers, it is understood that this value may not accurately reflect the complete upstream picture.</t>
        </li>
        <li>
          <t>Format: Integer, size in bytes. If no upstream is accessed, the size is 0.</t>
        </li>
        <li>
          <t>Record Types: Minimal, Standard, Extended</t>
        </li>
      </ul>
    </section>
    <section anchor="h.hv6v8xr9u5e1">
      <name>s-upstream-ip</name>
      <ul>
        <li>
          <t>Description: The IP address of the upstream source.</t>
        </li>
        <li>
          <t>Format: String, IP address</t>
        </li>
        <li>
          <t>Record Types: Extended</t>
        </li>
      </ul>
    </section>
  </section>
</section><section anchor="h.jdws9pempj0a">
  <name>Record Types</name>
  <t>Standard record types consist of a field set, a format, and a version that together form the record type, identified by a string like: "opencaching_minimal_json_v1" or "opencaching_standard_csv_v1" or "opencaching_extended_whitespace_v1".</t>
  <t>The identifier is then referenced in the supported-record-types field of FCI.Logging and the record-type field of MI.LoggingTransport.</t>
  <t>For this draft, we do not support custom record types, but future drafts will provide a way to define custom record types with their own associated identifiers.</t>
  <section anchor="h.rnk3ggi1i00c">
    <name>Log Record Formats</name>
    <section anchor="h.rwycmacempuv">
      <name>JSON</name>
      <t>The JSON log record format consists of a JSON object wherein each defined field of the record type is a property of the JSON object; the field name is the property name and the field value is the property value. If a field is not present in the log data, the property MAY be omitted.</t>
      <t>The following is an example record in the JSON record format:</t>
      <figure>
        <sourcecode><![CDATA[{
  "timestamp-ns": 1672938865000000000,
  "cs-uri": "https://cdn.example.com/assets/b874bc5b/index.m3u8",
  "sc-status": "200",
  "sc-total-bytes": 1784,
  "s-upstream-header-size-bytes": 208,
  "s-upstream-total-bytes": 1784,
}]]></sourcecode>
      </figure>
    </section>
    <section anchor="h.x095lz7enks8">
      <name>CSV</name>
      <t>The comma-separated value record format follows the definition in <xref target="RFC4180"/> with the addition of a special value to handle null values.</t>
      <t>If a field value is null, it MUST be represented by the following unquoted literal value: $NULL$</t>
    </section>
    <section anchor="h.1kvghrelc3tt">
      <name>Whitespace</name>
      <t>The whitespace delimited record format consists of field values enclosed in QUOTATION MARK (U+0022: ") characters, with characters separated by any number of SPACE (U+0020) or TAB (U+0009) characters.</t>
      <t>If a field value is null, it must be represented by the following unquoted literal value: $NULL$</t>
    </section>
    <section anchor="h.31571pm0nlae">
      <name>Protobuf</name>
      <t>Appendix section <xref target="h.pv2ceok296tc">10.1</xref> provides the protobuf definitions in the proto file format for the protobuf record types defined.</t>
    </section>
  </section>
  <section anchor="h.ykuxdwfiq2j9">
    <name>Predefined Log Record Types</name>
    <ul>
      <li>
        <t>A predefined log record type is a set of fields in a defined textual or binary format (JSON, CSV, whitespace delimited, or protobuf). This specification includes three predefined record types:</t>
        <ul>
          <li>
            <t>A ‘minimal' type optimized for data size, including only the fields necessary to perform billing functions.</t>
          </li>
          <li>
            <t>A ‘standard' type with a set of fields likely to be useful for operational monitoring.</t>
          </li>
          <li>
            <t>An ‘extended' type with an exhaustive field set likely to cover most operational and troubleshooting use cases.</t>
          </li>
        </ul>
      </li>
    </ul>
    <section anchor="h.vvukcn4vic4v">
      <name>Minimal</name>
      <table>
        <thead>
          <tr>
            <th>Order</th>
            <th>Field</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td>1</td>
            <td>timestamp-ns</td>
          </tr>
          <tr>
            <td>2</td>
            <td>c-groupid</td>
          </tr>
          <tr>
            <td>3</td>
            <td>cs-uri</td>
          </tr>
          <tr>
            <td>4</td>
            <td>sc-status</td>
          </tr>
          <tr>
            <td>5</td>
            <td>sc-total-bytes</td>
          </tr>
          <tr>
            <td>6</td>
            <td>s-ccid</td>
          </tr>
          <tr>
            <td>7</td>
            <td>s-upstream-header-size-bytes</td>
          </tr>
          <tr>
            <td>8</td>
            <td>s-upstream-total-bytes</td>
          </tr>
          <tr>
            <td>9+</td>
            <td>sc-hdr-*, cs-hdr-*</td>
          </tr>
        </tbody>
      </table>
      <t>The Minimal log record type field set consists of the fields denoted as 'Minimal' in section <xref target="h.coa696tkzovl">5.</xref> </t>
      <ul>
        <li>
          <t>JSON format: 'opencaching_minimal_json_v1'</t>
        </li>
        <li>
          <t>CSV format: 'opencaching_minimal_csv_v1'</t>
        </li>
        <li>
          <t>Whitespace delimited format:'opencaching_minimal_whitespace_v1'</t>
        </li>
      </ul>
    </section>
    <section anchor="h.c9m449qz5tej">
      <name>Standard</name>
      <table>
        <thead>
          <tr>
            <th>Order</th>
            <th>Field</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td>1</td>
            <td>timestamp-ns</td>
          </tr>
          <tr>
            <td>2</td>
            <td>s-time-total-ms</td>
          </tr>
          <tr>
            <td>3</td>
            <td>c-groupid</td>
          </tr>
          <tr>
            <td>4</td>
            <td>cs-uri</td>
          </tr>
          <tr>
            <td>5</td>
            <td>sc-status</td>
          </tr>
          <tr>
            <td>6</td>
            <td>sc-total-bytes</td>
          </tr>
          <tr>
            <td>7</td>
            <td>s-ccid </td>
          </tr>
          <tr>
            <td>8</td>
            <td>s-sid</td>
          </tr>
          <tr>
            <td>9</td>
            <td>s-cached</td>
          </tr>
          <tr>
            <td>10</td>
            <td>s-shortname</td>
          </tr>
          <tr>
            <td>11</td>
            <td>s-upstream-header-size-bytes</td>
          </tr>
          <tr>
            <td>12</td>
            <td>s-upstream-total-bytes</td>
          </tr>
          <tr>
            <td>13</td>
            <td>cs-hdr-User-Agent</td>
          </tr>
          <tr>
            <td>14+</td>
            <td>sc-hdr-*, cs-hdr-*</td>
          </tr>
        </tbody>
      </table>
      <t>The Standard log record type field set consists of the fields denoted as 'Standard' in section <xref target="h.coa696tkzovl">5.</xref></t>
      <ul>
        <li>
          <t>JSON format: 'opencaching_standard_json_v1'</t>
        </li>
        <li>
          <t>CSV format: 'opencaching_standard_csv_v1'</t>
        </li>
        <li>
          <t>Whitespace delimited format: 'opencaching_standard_whitespace_v1'</t>
        </li>
      </ul>
    </section>
    <section anchor="h.ly9ig82gctl9">
      <name>Extended</name>
      <table>
        <thead>
          <tr>
            <th>Order</th>
            <th>Field</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td>1</td>
            <td>timestamp-ns</td>
          </tr>
          <tr>
            <td>2</td>
            <td>timestamp-iso8601</td>
          </tr>
          <tr>
            <td>3</td>
            <td>s-time-total-ms</td>
          </tr>
          <tr>
            <td>4</td>
            <td>c-groupid</td>
          </tr>
          <tr>
            <td>5</td>
            <td>cs-method</td>
          </tr>
          <tr>
            <td>6</td>
            <td>cs-version</td>
          </tr>
          <tr>
            <td>7</td>
            <td>cs-uri</td>
          </tr>
          <tr>
            <td>8</td>
            <td>cs-uri-transformed</td>
          </tr>
          <tr>
            <td>9</td>
            <td>sc-status</td>
          </tr>
          <tr>
            <td>10</td>
            <td>sc-total-bytes</td>
          </tr>
          <tr>
            <td>11</td>
            <td>s-ccid</td>
          </tr>
          <tr>
            <td>12</td>
            <td>s-sid</td>
          </tr>
          <tr>
            <td>13</td>
            <td>s-cached</td>
          </tr>
          <tr>
            <td>14</td>
            <td>c-ip</td>
          </tr>
          <tr>
            <td>15</td>
            <td>s-shortname</td>
          </tr>
          <tr>
            <td>16</td>
            <td>s-id</td>
          </tr>
          <tr>
            <td>17</td>
            <td>s-time-first-ms</td>
          </tr>
          <tr>
            <td>18</td>
            <td>s-sdur-ms</td>
          </tr>
          <tr>
            <td>19</td>
            <td>s-time-upstream-ms</td>
          </tr>
          <tr>
            <td>20</td>
            <td>s-upstream-header-size-bytes</td>
          </tr>
          <tr>
            <td>21</td>
            <td>s-upstream-total-bytes</td>
          </tr>
          <tr>
            <td>22</td>
            <td>s-upstream-status</td>
          </tr>
          <tr>
            <td>23</td>
            <td>s-upstream-ip</td>
          </tr>
          <tr>
            <td>24</td>
            <td>cs-hdr-Referer</td>
          </tr>
          <tr>
            <td>25</td>
            <td>cs-hdr-Range</td>
          </tr>
          <tr>
            <td>26</td>
            <td>cs-hdr-User-Agent</td>
          </tr>
          <tr>
            <td>27</td>
            <td>sc-hdr-Content-Type</td>
          </tr>
          <tr>
            <td>28</td>
            <td>sc-header-size-bytes</td>
          </tr>
          <tr>
            <td>29+</td>
            <td>sc-hdr-*, cs-hdr-*</td>
          </tr>
        </tbody>
      </table>
      <t>The Extended log record type field set consists of the fields denoted as 'Extended' in section <xref target="h.coa696tkzovl">5.</xref></t>
      <ul>
        <li>
          <t>JSON format: 'opencaching_extended_json_v1'</t>
        </li>
        <li>
          <t>CSV format: 'opencaching_extended_csv_v1'</t>
        </li>
        <li>
          <t>Whitespace delimited format: 'opencaching_extended_whitespace_v1'</t>
        </li>
        <li>
          <t>Protobuf format: ‘opencaching_protobuf_v1' as defined in section <xref target="h.pv2ceok296tc">10.1.</xref> The protobuf fields are optional, so this protobuf format may be used for the use cases covered by minimal and standard.</t>
        </li>
      </ul>
    </section>
    <section anchor="h.jkq66tn7d1w9">
      <name>uCDN Configured Header Inclusion</name>
      <t>If the dCDN advertises the  allow-include-headers property in FCI.LoggingPush (see section <xref target="h.jmqpjn51en3o">8.1</xref>), a uCDN can configure additional request/response headers as custom fields to be included with the MI.LoggingTransport property include-headers (see section <xref target="h.3gcqfmy6uk0z">7.3</xref>). Included headers are added to the log records in the order defined, with names cs-hdr-* and  sc-hdr-*, to indicate request and response headers respectively. Per the text in the specification of cs-hdr-*, duplicate header fields should be concatenated per <xref target="RFC9110"/> sections 5.2 and 5.3.</t>
      <t>The following is an example of MI.LoggingMetadata that includes headers:</t>
      <figure>
        <sourcecode><![CDATA[{
    "generic-metadata-type": "MI.LoggingMetadata",
    "generic-metadata-value": {
            "transports": [{
                    "type": "MI.LoggingTransportKafka",
                    "record-type": "opencaching_standard_json_v1",
                    "include-headers": ["cs-hdr-Range", "sc-hdr-Content-Length"],
                    "account-id": "dCDN1",
                    "endpoint": {
                            "bootstrap-servers": ["kafka.ucdn.example.com:9092"],
                            "topic": "dcdn-logs"
                    }
            }]
    }
}]]></sourcecode>
      </figure>
    </section>
  </section>
  <section anchor="h.jlw67xrmuzmi">
    <name>Custom Record Types</name>
    <t>It is permitted with the out-of-band agreement of the participants to use a record type not defined in this specification. In this case, the format of the record type is entirely dependent on cooperation between the uCDN and dCDN outside the scope of this specification. A unique identifier SHOULD be assigned for every custom record type, and it MAY be utilized in place of record types defined in this specification wherever a record-type value is required.</t>
    <section anchor="h.sx0qhr2jusnn">
      <name>uCDN Configured Field Inclusion</name>
      <t>By specifying the include-fields property of MI.LoggingTransport, additional fields can be appended to an existing record type. The dCDN must advertise allow-include-fields as true for this operation to be permitted. Fields appended by include-fields are always after the predefined fields of the record-type and any additional fields advertised by the dCDN in the included-fields property of FCI.LoggingPush.</t>
    </section>
    <section anchor="h.p5ckzx91djzd">
      <name>Record Type 'Empty'</name>
      <t>The record-type value of "empty" is defined as the empty set. If this record-type is advertised, the only fields present in the logging records will be defined by include-fields and included-fields.</t>
    </section>
  </section>
</section><section anchor="h.1z066cfewwq1">
  <name>Logging Metadata</name>
  <t>The MI.LoggingMetadata object provides configuration that instructs the dCDN how to transmit log files or streams of log records to the uCDN.</t>
  <t>MI.LoggingTransport objects are enclosed in the MI.LoggingMetadata object described below.</t>
  <t>The dCDN specifies supported logging transports in the FCI.LoggingSource advertisement as specified in <xref target="h.tsu3kwem5jjd">Section 8.2.</xref></t>
  <section anchor="h.doju1kdl0y9l">
    <name>MI.LoggingMetadata</name>
    <t>Property: transports</t>
    <ul>
      <li>
        <t>Description: An array of MI.LoggingTransport objects.  </t>
      </li>
      <li>
        <t>Type: Array</t>
      </li>
      <li>
        <t>Mandatory-to-Specify: No</t>
      </li>
    </ul>
    <t>Property: pull-transport-metadata</t>
    <ul>
      <li>
        <t>Description: An object that can be used to configure certain properties of dCDN pull transports.  </t>
      </li>
      <li>
        <t>Type: MI.LoggingPullTransportMetadata</t>
      </li>
      <li>
        <t>Mandatory-to-Specify: No</t>
      </li>
    </ul>
  </section>
  <section anchor="h.sj67qhj3o60v">
    <name>MI.LoggingPullTransportMetadata</name>
    <t>Property: sftp-public-keys</t>
    <ul>
      <li>
        <t>Description: An array of SSH public keys that the dCDN SHOULD authorize on SFTP logging endpoints.</t>
      </li>
      <li>
        <t>Type: Array of strings</t>
      </li>
      <li>
        <t>Mandatory-to-Specify: No</t>
      </li>
    </ul>
  </section>
  <section anchor="h.3gcqfmy6uk0z">
    <name>MI.LoggingTransport</name>
    <t>MI.LoggingTransport objects are enclosed in the MI.LoggingMetadata object and instruct the dCDN how to transmit log files or streams to the uCDN.</t>
    <t>Property: transport-type</t>
    <ul>
      <li>
        <t>Description: The type discriminator for the endpoint property. The following values are supported:</t>
        <ul>
          <li>
            <t>MI.LoggingTransportKafka</t>
          </li>
          <li>
            <t>MI.LoggingTransportSFTP</t>
          </li>
          <li>
            <t>MI.LoggingTransportS3</t>
          </li>
        </ul>
      </li>
      <li>
        <t>Type: String</t>
      </li>
      <li>
        <t>Mandatory-to-Specify: Yes</t>
      </li>
    </ul>
    <t>Property: record-type</t>
    <ul>
      <li>
        <t>Description: The format of the individual log records. This MUST be a valid value as defined in Section <xref target="h.jdws9pempj0a">6</xref></t>
      </li>
      <li>
        <t>Type: String</t>
      </li>
      <li>
        <t>Mandatory-to-Specify: Yes</t>
      </li>
    </ul>
    <t>Property: container-metadata</t>
    <ul>
      <li>
        <t>Description: The configuration for the generation of log files when operating in file-based modes.</t>
      </li>
      <li>
        <t>Type: MI.LoggingContainerMetadata</t>
      </li>
      <li>
        <t>Mandatory-to-Specify:</t>
        <ul>
          <li>
            <t>File or object-based transports: Yes</t>
          </li>
          <li>
            <t>Streaming transports: No</t>
          </li>
        </ul>
      </li>
    </ul>
    <t>Property: include-headers</t>
    <ul>
      <li>
        <t>Description: Additional header fields to include, dependent on dCDN support via the allow-include-headers flag of MI.LoggingPush.</t>
      </li>
      <li>
        <t>Type: Ordered array of header fields in sc-hdr-* and cs-hdr-* naming format</t>
      </li>
      <li>
        <t>Mandatory-to-Specify: No</t>
      </li>
    </ul>
    <t>Property: include-fields</t>
    <ul>
      <li>
        <t>Description: Additional fields to include, dependent on dCDN support via the allow-include-fields flag of MI.LoggingPush.</t>
      </li>
      <li>
        <t>Type: Ordered array of field names</t>
      </li>
      <li>
        <t>Mandatory-to-Specify: No</t>
      </li>
    </ul>
    <t>Property: account-id</t>
    <ul>
      <li>
        <t>Description: An account identifier value that the dCDN must include in log records.</t>
      </li>
      <li>
        <t>Type: String</t>
      </li>
      <li>
        <t>Mandatory-to-Specify: No</t>
      </li>
    </ul>
    <t>Property: endpoint</t>
    <ul>
      <li>
        <t>Description: An object with any configuration necessary for the specific logging transport, discriminated on the transport-type property. Objects for each transport type are described in the sections below.</t>
      </li>
      <li>
        <t>Type: Object</t>
      </li>
      <li>
        <t>Mandatory-to-Specify: Yes</t>
      </li>
    </ul>
    <t>Property: transforms</t>
    <ul>
      <li>
        <t>Description: Transformations on sets of fields as defined in an  MI.LoggingTransform. The sets of record-fields field names MUST NOT  overlap for the transport. </t>
      </li>
      <li>
        <t>Type: Array of MI.LoggingTransforms objects</t>
      </li>
      <li>
        <t>Mandatory-to-Specify: No</t>
      </li>
    </ul>
    <t>Property: sampling</t>
    <ul>
      <li>
        <t>Description: An object defining the sampling configuration to be applied to the log records for this transport. If omitted, no sampling is applied.</t>
      </li>
      <li>
        <t>Type: MI.LoggingSampler object</t>
      </li>
      <li>
        <t>Mandatory-to-Specify: No</t>
      </li>
    </ul>
    <section anchor="h.hem6p3nkzil7">
      <name>MI.LoggingTransportKafka</name>
      <t>Property: bootstrap-servers</t>
      <ul>
        <li>
          <t>Description: An array of the bootstrap connection servers in host:port format.</t>
        </li>
        <li>
          <t>Type: Array of strings</t>
        </li>
        <li>
          <t>Mandatory-to-Specify: Yes</t>
        </li>
      </ul>
      <t>Property: topic</t>
      <ul>
        <li>
          <t>Description: The Kafka topic to which log records SHOULD be published.</t>
        </li>
        <li>
          <t>Type: String</t>
        </li>
        <li>
          <t>Mandatory-to-Specify: Yes</t>
        </li>
      </ul>
      <t>The following is an MI.LoggingMetadata Apache Kafka example:</t>
      <figure>
        <sourcecode><![CDATA[{
    "generic-metadata-type": "MI.LoggingMetadata",
    "generic-metadata-value": {
            "transports": [{
                    "transport-type": "MI.LoggingTransportKafka",
                    "record-type": "opencaching_standard_json_v1",
                    "account-id": "dCDN1",
                    "endpoint": {
                            "bootstrap-servers": ["kafka.ucdn.example.com:9092"],
                            "topic": "dcdn-logs"
                    }
            }]
    }
}]]></sourcecode>
      </figure>
    </section>
    <section anchor="h.rro5fia8z4t1">
      <name>MI.LoggingTransportSFTP</name>
      <t>Property: host</t>
      <ul>
        <li>
          <t>Description: The hostname of the SFTP server, which MAY be specified as either hostname or hostname:port.</t>
        </li>
        <li>
          <t>Type: String</t>
        </li>
        <li>
          <t>Mandatory-to-Specify: Yes</t>
        </li>
      </ul>
      <t>Property: username</t>
      <ul>
        <li>
          <t>Description: The remote username to use for connecting to the SFTP server.</t>
        </li>
        <li>
          <t>Type: String</t>
        </li>
        <li>
          <t>Mandatory-to-Specify: Yes</t>
        </li>
      </ul>
      <t>Property: password</t>
      <ul>
        <li>
          <t>Description: The remote password to use for connecting to the SFTP server when using password-based authentication.</t>
        </li>
        <li>
          <t>Type: MI.SecretValue</t>
        </li>
        <li>
          <t>Mandatory-to-Specify: No</t>
        </li>
      </ul>
      <t>The following is an MI.LoggingTransportSFTP example:</t>
      <figure>
        <sourcecode><![CDATA[{
    "generic-metadata-type": "MI.LoggingMetadata",
    "generic-metadata-value": {
            "transports": [{
                    "type": "MI.LoggingTransportSFTP",
                    "record-type": "opencaching_standard_json_v1",
                    "container-metadata": {
                            "container-type": "json_v1",
                            "filename-template": "uCDN/logs/%Y-%m/%d-access.log.%n",
                            "interval": 86400,
                            "maximum-size": "100M"
                    },
                    "account-id": "dCDN1",
                    "endpoint": {
                            "host": "logs.ucdn.example.com:22",
                            "username": "dcdnlogs"
                    }
            }]
    }
}]]></sourcecode>
      </figure>
    </section>
    <section anchor="h.y684x2ygagck">
      <name>MI.LoggingTransportS3API</name>
      <t>Property: host</t>
      <ul>
        <li>
          <t>Description: The hostname to connect to the S3-compatible API.</t>
        </li>
        <li>
          <t>Type: String</t>
        </li>
        <li>
          <t>Mandatory-to-Specify: Yes</t>
        </li>
      </ul>
      <t>Property: access-key-id</t>
      <ul>
        <li>
          <t>Description: The access key ID to use when connecting to the API.</t>
        </li>
        <li>
          <t>Type: String</t>
        </li>
        <li>
          <t>Mandatory-to-Specify: No</t>
        </li>
      </ul>
      <t>Property: access-key-secret</t>
      <ul>
        <li>
          <t>Description: The access key secret to use when connecting to the API.</t>
        </li>
        <li>
          <t>Type: MI.SecretValue</t>
        </li>
        <li>
          <t>Mandatory-to-Specify: No</t>
        </li>
      </ul>
      <t>Property: bucket-name</t>
      <ul>
        <li>
          <t>Description: The bucket name to use for pushed log file objects.</t>
        </li>
        <li>
          <t>Type: String</t>
        </li>
        <li>
          <t>Mandatory-to-Specify: Yes</t>
        </li>
      </ul>
      <t>The following is an MI.LoggingTransportS3API example:</t>
      <figure>
        <sourcecode><![CDATA[{
    "generic-metadata-type": "MI.LoggingMetadata",
    "generic-metadata-value": {
            "transports": [{
                    "type": "MI.LoggingTransportS3API",
                    "record-type": "opencaching_standard_json_v1",
                    "container-metadata": {
                            "container-type": "json_v1",
                            "name-template": "%Y-%m-%d-access.log",
                            "interval": 86400
                    },
                    "account-id": "dCDN1",
                    "endpoint": {
                            "host": "s3.amazonaws.com",
                            "access-key-id": "xxxxxxxxx",
                            "access-key-secret": {
                                    "secret-store-id": "store-1",
                                    "secret-store-path": "/logging/dCDN1/dcdnlogs/s3"
                            },
                            "bucket-name": "dcdnlogs"
                    }
            }]
    }
}]]></sourcecode>
      </figure>
    </section>
  </section>
  <section anchor="h.993807dxae2p">
    <name>MI.LoggingTransforms</name>
    <t>Logging fields can contain personal user information that cannot be shared upstream or stored in the operating jurisdictions. These operations allow for obfuscation and anonymization of logging record fields.</t>
    <t>An array of MI.LoggingTransforms objects can be enclosed in the MI.LoggingTransport object transform field for uCDN configured obfuscation.</t>
    <t>The dCDN specifies privacy obfuscations in the FCI.Logging advertisement applied-transforms field.</t>
    <t>Property: record-fields</t>
    <ul>
      <li>
        <t>Description: The list of privacy-sensitive log record fields for which obfuscation is applied.</t>
      </li>
      <li>
        <t>Type: Array of strings</t>
      </li>
      <li>
        <t>Mandatory-to-Specify: Yes</t>
      </li>
    </ul>
    <t>Property: transforms</t>
    <ul>
      <li>
        <t>Description: The transform operations to apply on the record fields, in order.</t>
      </li>
      <li>
        <t>Type: Array of  MI.LoggingTransform objects</t>
      </li>
      <li>
        <t>Mandatory-to-Specify: Yes</t>
      </li>
    </ul>
    <section anchor="h.sjwhvqb1uaak">
      <name>MI.LoggingTransform</name>
      <t>Property: type</t>
      <ul>
        <li>
          <t>Description: The type discriminator for the value property. The following values are supported:</t>
          <ul>
            <li>
              <t>MI.LoggingTransformReplace</t>
            </li>
            <li>
              <t>MI.LoggingTransformTruncate</t>
            </li>
            <li>
              <t>MI.LoggingTransformUrlRemoveParam</t>
            </li>
            <li>
              <t>MI.LoggingTransformUrlStripParams</t>
            </li>
            <li>
              <t>MI.LoggingTransformMaskIp</t>
            </li>
            <li>
              <t>MI.LoggingTransformEncrypt</t>
            </li>
            <li>
              <t>MI.LoggingTransformHash</t>
            </li>
            <li>
              <t>MI.LoggingTransformEncode</t>
            </li>
          </ul>
        </li>
        <li>
          <t>Type: String</t>
        </li>
        <li>
          <t>Mandatory-to-Specify: Yes</t>
        </li>
      </ul>
      <t>Property: value</t>
      <ul>
        <li>
          <t>Description: The privacy operation configuration value.</t>
        </li>
        <li>
          <t>Type: Object of type </t>
        </li>
        <li>
          <t>Mandatory-to-Specify: Yes</t>
        </li>
      </ul>
    </section>
    <section anchor="h.tu30og4dhl16">
      <name>MI.LoggingTransformReplace</name>
      <t>Property: regex</t>
      <ul>
        <li>
          <t>Description: A POSIX substitution expression.</t>
        </li>
        <li>
          <t>Type: String</t>
        </li>
        <li>
          <t>Mandatory-to-Specify: Yes</t>
        </li>
      </ul>
      <t>Property: value</t>
      <ul>
        <li>
          <t>Description: The privacy operation configuration value.</t>
        </li>
        <li>
          <t>Type: String </t>
        </li>
        <li>
          <t>Mandatory-to-Specify: Yes</t>
        </li>
      </ul>
    </section>
    <section anchor="h.emgtr2daonkb">
      <name>MI.LoggingTransformTruncate</name>
      <t>This object removes characters from the end of a field value if it exceeds a defined length.</t>
      <t>Property: length</t>
      <ul>
        <li>
          <t>Description: The maximum field length.</t>
        </li>
        <li>
          <t>Type: Number</t>
        </li>
        <li>
          <t>Mandatory-to-Specify: Yes</t>
        </li>
      </ul>
    </section>
    <section anchor="h.nhnen0hervla">
      <name>MI.LoggingTransformUrlRemoveParam</name>
      <t>This object parses record-fields  values as URLs, stripping specified query parameters along with their values.</t>
      <t>Property: remove-param</t>
      <ul>
        <li>
          <t>Description: Omits URL query parameters whose name match a regular expression.</t>
        </li>
        <li>
          <t>Type: POSIX Regular expression string</t>
        </li>
        <li>
          <t>Mandatory-to-Specify: Yes</t>
        </li>
      </ul>
    </section>
    <section anchor="h.6i6pxz6ntg10">
      <name>MI.LoggingTransformUrlStripParams</name>
      <t>Property: strip-params</t>
      <ul>
        <li>
          <t>Description: Strips all query parameters from the URL.</t>
        </li>
        <li>
          <t>Type: Boolean</t>
        </li>
        <li>
          <t>Mandatory-to-Specify: Yes</t>
        </li>
      </ul>
      <t>The following is an MI.LoggingTransformUrlParam example:</t>
      <figure>
        <sourcecode><![CDATA[{
    "generic-metadata-type": "MI.LoggingMetadata",
    "generic-metadata-value": {
            "transforms": [{
                    "record-fields": [
                            "c-uri",
                            "c-hdr-Referer"
                    ],
                    "transforms": [{
                            "type": "MI.LoggingTransformUrlParam",
                            "value": {
                                    "replace-param": "^.?[iI][dD]$|.*_[iI][dD]$",
                                    "strip-params": False
                            }
                    }]
            }]
    }
}]]></sourcecode>
      </figure>
    </section>
    <section anchor="h.8c50ss247ka4">
      <name>MI.LoggingTransformUrlPath</name>
      <t>This object parses record-fields  values as URLs, and can replace or truncate path elements from the front or end. At least one of replace-path or path-trim MUST be present.</t>
      <t>Property: replace-path</t>
      <ul>
        <li>
          <t>Description: Modify URL path elements with a regular expression.</t>
        </li>
        <li>
          <t>Type: String. POSIX</t>
        </li>
        <li>
          <t>Mandatory-to-Specify: No, if URL parameter modification is not desired.</t>
        </li>
      </ul>
      <t>Property: path-trim</t>
      <ul>
        <li>
          <t>Description: Trims elements from URL paths to a specified depth. A positive value will truncate the specified number of path elements from the end. A negative value will truncate from the front.</t>
        </li>
        <li>
          <t>Type: Number</t>
        </li>
        <li>
          <t>Mandatory-to-Specify: No, if path truncation is not desired.</t>
        </li>
      </ul>
      <t>The following is an MI.LoggingTransformUrlPath example:</t>
      <figure>
        <sourcecode><![CDATA[{
    "generic-metadata-type": "MI.LoggingMetadata",
    "generic-metadata-value": {
            "transforms": [{
                    "record-fields": [
                            "c-uri",
                            "c-hdr-Referer"
                    ],
                    "transforms": [{
                            "type": "MI.LoggingTransformUrlPath",
                            "value": {
                                    "path-trim": -1,
                                    "replace-path": "s/sid=[^\/]+/sid-XXXX/g"
                            }
                    }]
            }]
    }
}]]></sourcecode>
      </figure>
    </section>
    <section anchor="h.4opb57euudyr">
      <name>MI.LoggingTransformMaskIp</name>
      <t>This object parses values as IPv4 or IPv6 strings, and outputs an address that strips the indicated number of bits.</t>
      <t>Property: mask-lsb-v4</t>
      <ul>
        <li>
          <t>Description: The number of bits to strip from the end of an IPv4 address.</t>
        </li>
        <li>
          <t>Type: Number, maximum 32 for IPv4</t>
        </li>
        <li>
          <t>Mandatory-to-Specify: No, if IPv4 masking is not desired</t>
        </li>
      </ul>
      <t>Property: mask-lsb-v6</t>
      <ul>
        <li>
          <t>Description: The number of bits to strip from the end of an IPv6 address.</t>
        </li>
        <li>
          <t>Type: Number, maximum 128 for IPv6</t>
        </li>
        <li>
          <t>Mandatory-to-Specify: No, if IPv6 masking is not desired</t>
        </li>
      </ul>
      <t>The following is an MI.LoggingTransformMaskIp example:</t>
      <figure>
        <sourcecode><![CDATA[{
    "generic-metadata-type": "MI.LoggingMetadata",
    "generic-metadata-value": {
            "transforms": [{
                    "record-fields": [
                            "c-ip"
                    ],
                    "transforms": [{
                            "type": "MI.LoggingTransformMaskIp",
                            "value": {
                                    "mask-lsb-v4": 4,
                                    "mask-lsb-v6": 16
                            }
                    }]
            }]
    }
}]]></sourcecode>
      </figure>
    </section>
    <section anchor="h.3iaokain3n5s">
      <name>MI.LoggingTransformHash</name>
      <t>This object replaces record-fields values with their Hash-Based Message Authentication Code (HMAC).</t>
      <t>Property: function</t>
      <ul>
        <li>
          <t>Description: The hashing function to use in computing the HMAC of the private field value.</t>
        </li>
        <li>
          <t>Type: String. The following values are supported:</t>
          <ul>
            <li>
              <t>MD5</t>
            </li>
            <li>
              <t>SHA256</t>
            </li>
          </ul>
        </li>
        <li>
          <t>Mandatory-to-Specify: Yes</t>
        </li>
      </ul>
      <t>Property: key</t>
      <ul>
        <li>
          <t>Description: The key used in computing the HMAC.</t>
        </li>
        <li>
          <t>Type: MI.SecretValue</t>
        </li>
        <li>
          <t>Mandatory-to-Specify: No, if no key is used or an empty string, if the key is confidential to the dCDN</t>
        </li>
      </ul>
      <t>The following is an MI.LoggingTransformHash example:</t>
      <figure>
        <sourcecode><![CDATA[{
    "generic-metadata-type": "MI.LoggingMetadata",
    "generic-metadata-value": {
            "transforms": [{
                    "record-fields": [
                            "c-ip"
                    ],
                    "transforms": [{
                            "type": "MI.LoggingTransformHash",
                            "value": {
                                    "function": "MD5",
                                    "key": {
                                            "secret-store-id": "store-1",
                                            "secret-path": "/logging/hmac/c-ip"
                                    }
                            }
                    }]
            }]
    }
}]]></sourcecode>
      </figure>
    </section>
    <section anchor="h.bpqj1aputagz">
      <name>MI.LoggingTransformEncrypt</name>
      <t>With this object, fields are encrypted with the selected scheme, and the nonce used is prepended to the outputted ciphertext.</t>
      <t>The encryption key is specified as an MI.SecretValue and is transmitted in the transforms field with log records in the metadata  header or as a sidecar. When a new key is configured, a new file and sidecar must be created with an updated key reference.</t>
      <t>Property: algorithm</t>
      <ul>
        <li>
          <t>Description: The algorithm to use in encrypting the private field value.</t>
        </li>
        <li>
          <t>Type: String. The following values are supported:</t>
          <ul>
            <li>
              <t>AEAD-XCHACHA20-POLY1305-IETF</t>
            </li>
            <li>
              <t>AEAD-CHACHA20-POLY1305-IETF</t>
            </li>
            <li>
              <t>AEAD-AES256-GCM</t>
            </li>
            <li>
              <t>RC4</t>
            </li>
          </ul>
        </li>
        <li>
          <t>Mandatory-to-Specify: Yes</t>
        </li>
      </ul>
      <t>Property: key</t>
      <ul>
        <li>
          <t>Description: A reference to an externally defined encryption key.</t>
        </li>
        <li>
          <t>Type: MI.SecretValue </t>
        </li>
        <li>
          <t>Mandatory-to-Specify: No</t>
        </li>
      </ul>
      <t>The following is an MI.LoggingTransformEncrypt example:</t>
      <figure>
        <sourcecode><![CDATA[{
    "generic-metadata-type": "MI.LoggingMetadata",
    "generic-metadata-value": {
            "transforms": [{
                    "record-fields": [
                            "c-ip"
                    ],
                    "transforms": [{
                            "type": "MI.LoggingTransformEncrypt",
                            "value": {
                                    "algorithm": "AEAD-XCHACHA20-POLY1305-IETF",
                                    "key": {
                                            "secret-store-id": "store-1",
                                            "secret-path": "/logging/encrypt/c-ip"
                                    }
                            }
                    }]
            }]
    }
}]]></sourcecode>
      </figure>
    </section>
    <section anchor="h.gw0s4zsyl8sp">
      <name>MI.LoggingTransformEncode</name>
      <t>Property: encoding</t>
      <ul>
        <li>
          <t>Description: The encoding to use for the obfuscated value.</t>
        </li>
        <li>
          <t>Type: String. The following values are supported:</t>
          <ul>
            <li>
              <t>BASE64</t>
            </li>
            <li>
              <t>BIN2HEX</t>
            </li>
          </ul>
        </li>
        <li>
          <t>Mandatory-to-Specify: Yes</t>
        </li>
      </ul>
      <t>The following is an MI.LoggingTransformEncode example:</t>
      <figure>
        <sourcecode><![CDATA[{
  "generic-metadata-type": "MI.LoggingMetadata",
  "generic-metadata-value": {
    "transports": [
      {
        "type": "MI.LoggingTransportS3API",
        "record-type": "opencaching_standard_json_v1",
        "container-metadata": {
          "container-type": "json_v1",
          "name-template": "%Y-%m-%d-access.log",
          "interval": 86400
        },
        "endpoint": {
          "host": "s3.amazonaws.com",
          "bucket-name": "dcdnlogs"
        },
        "transforms": [
          {
            "transforms": [
              {
                "record-fields": [
                  "c-ip"
                ],
                "operations": [
                  {
                    "type": "MI.LoggingTransformEncrypt",
                    "value": {
                      "algorithm": "RC4",
                      "key": {
                        "secret-store-id": "store-1",
                        "secret-path": "/logging/encrypt/c-ip"
                      },
                      "id": 622,
                      "wrap-id": true
                    }
                  },
                  {
                    "type": "MI.LoggingTransformEncode",
                    "value": {
                      "encoding": "BASE64"
                    }
                  }
                ]
              }
            ]
          }
        ]
      }
    ]
  }
}]]></sourcecode>
      </figure>
    </section>
  </section>
  <section anchor="h.6dmr0bxgaksp">
    <name>MI.LoggingFilenameTemplate</name>
    <t>The MI.LoggingFilenameTemplate type is used to specify the path and filename for files created by batch mode MI.LoggingTransport and FCI.LoggingTransport services.</t>
    <t>If the resulting filename is longer than 255 characters, it will be truncated to 255 characters in length, including the filename extension that will be retained. Fields that MAY be truncated are noted in the table below.</t>
    <t>It is the responsibility of the dCDN to ensure that file names are unique. It is strongly RECOMMENDED that the %id field is used to ensure the uniqueness requirement is satisfied.</t>
    <t>The overall path length is limited to 32 KiB, and each path component is limited to 255 characters. Any element exceeding the length limitation will be truncated to the maximum length.</t>
    <t>An instance of MI.LoggingFilenameTemplate is a string with the following supported substitution variables:</t>
    <table>
      <thead>
        <tr>
          <th>Variable</th>
          <th>Definition</th>
          <th>Length</th>
          <th>Values</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td>%cY</td>
          <td>File creation timestamp: Year.</td>
          <td>4</td>
          <td>Example: 2023</td>
        </tr>
        <tr>
          <td>%cm</td>
          <td>File creation timestamp: Month. Numeric. Length 2. 01-12.</td>
          <td>2</td>
          <td>01-12</td>
        </tr>
        <tr>
          <td>%cd</td>
          <td>File creation timestamp: Day. Length 2. 0-31.</td>
          <td>2</td>
          <td>0-31</td>
        </tr>
        <tr>
          <td>%cH</td>
          <td>File creation timestamp: Hour. Length 2. 00-23.</td>
          <td>2</td>
          <td>00-23</td>
        </tr>
        <tr>
          <td>%cM</td>
          <td>File creation timestamp: Minutes. Length 2. 00-59.</td>
          <td>2</td>
          <td>00-59</td>
        </tr>
        <tr>
          <td>%cS</td>
          <td>File creation timestamp: Seconds. 00-59.</td>
          <td>2</td>
          <td>0-59</td>
        </tr>
        <tr>
          <td>%sY</td>
          <td>Timestamp of first record: Year. Length 4. Example: 2023.</td>
          <td>4</td>
          <td>Example: 2023</td>
        </tr>
        <tr>
          <td>%sm</td>
          <td>Timestamp of first record: Month. Numeric. Length 2. 01-12.</td>
          <td>2</td>
          <td>01-12</td>
        </tr>
        <tr>
          <td>%sd</td>
          <td>Timestamp of first record: Day. Length 2. 0-31.</td>
          <td>2</td>
          <td>00-31</td>
        </tr>
        <tr>
          <td>%sH</td>
          <td>Timestamp of first record: Hour. Length 2. 00-23.</td>
          <td>2</td>
          <td>00-23</td>
        </tr>
        <tr>
          <td>%sM</td>
          <td>Timestamp of first record: Minutes. Length 2. 00-59.</td>
          <td>2</td>
          <td>00-59</td>
        </tr>
        <tr>
          <td>%sS</td>
          <td>Timestamp of first record: Seconds. 00-59.</td>
          <td>2</td>
          <td>00-59</td>
        </tr>
        <tr>
          <td>%eY</td>
          <td>Timestamp of last record: Year. Length 4. Example: 2023.</td>
          <td>4</td>
          <td>Example: 2023</td>
        </tr>
        <tr>
          <td>%em</td>
          <td>Timestamp of last record: Month. Numeric. Length 2. 01-12.</td>
          <td>2</td>
          <td>01-12</td>
        </tr>
        <tr>
          <td>%ed</td>
          <td>Timestamp of last record: Day. Length 2. 0-31.</td>
          <td>2</td>
          <td>0-31</td>
        </tr>
        <tr>
          <td>%eH</td>
          <td>Timestamp of last record: Hour. Length 2. 00-23.</td>
          <td>2</td>
          <td>00-23</td>
        </tr>
        <tr>
          <td>%eM</td>
          <td>Timestamp of last record: Minutes. Length 2. 00-59.</td>
          <td>2</td>
          <td>00-59</td>
        </tr>
        <tr>
          <td>%eS</td>
          <td>Timestamp of last record: Seconds. 00-59.</td>
          <td>2</td>
          <td>00-59</td>
        </tr>
        <tr>
          <td>%source</td>
          <td>Logging source identifier.MAY be truncated. </td>
          <td>Variable</td>
          <td>Alphanumeric:[A-Za-z0-9]+</td>
        </tr>
        <tr>
          <td>%account</td>
          <td>Account ID as configured via MI.LoggingTransport. MAY be truncated.</td>
          <td>Variable</td>
          <td>Alphanumeric: [A-Za-z0-9\-]+</td>
        </tr>
        <tr>
          <td>%id</td>
          <td>A unique identifier for this file. MAY be truncated. </td>
          <td>Variable</td>
          <td>Alphanumeric: [A-Za-z0-9\-]+</td>
        </tr>
        <tr>
          <td>%ccid</td>
          <td>ccid assigned via MI.Grouping configuration. MAY be truncated.</td>
          <td>Variable</td>
          <td>Alphanumeric: [A-Za-z0-9\-]+</td>
        </tr>
      </tbody>
    </table>
    <section anchor="h.4tk7eqxc4ozk">
      <name>MI.LoggingFilenameTemplate Examples</name>
      <table>
        <thead>
          <tr>
            <th>Template</th>
            <th>Example Filename</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td>/%cY-%cM-%cD/%cH_%cM_%cS.%source.%id.json</td>
            <td>/2023-01-21/11_23_58.region1.fbdc303a.json</td>
          </tr>
          <tr>
            <td>/%source/%id.csv</td>
            <td>/region1/fbdc303a.csv</td>
          </tr>
        </tbody>
      </table>
    </section>
  </section>
  <section anchor="h.rq21jotczw3k">
    <name>MI.LoggingContainerMetadata</name>
    <t>This object defines configuration parameters used in file-based logging transports to define how files are created and formatted.</t>
    <t>Property: container-type</t>
    <ul>
      <li>
        <t>Description: The container type as defined in section <xref target="h.whuucahungsa">7</xref></t>
      </li>
      <li>
        <t>Type: String. The following values are supported:</t>
        <ul>
          <li>
            <t>cdni_v1</t>
          </li>
          <li>
            <t>json_v1</t>
          </li>
          <li>
            <t>linefeed_v1</t>
          </li>
          <li>
            <t>carriage_return_v1</t>
          </li>
          <li>
            <t>tar_v1</t>
          </li>
        </ul>
      </li>
      <li>
        <t>Mandatory-to-Specify: Yes</t>
      </li>
    </ul>
    <t>Property: filename-template</t>
    <ul>
      <li>
        <t>Description: The template to be used for files pushed by a batch mode transport.</t>
      </li>
      <li>
        <t>Type: MI.LoggingFilenameTemplate</t>
      </li>
      <li>
        <t>Mandatory-to-Specify: No</t>
      </li>
    </ul>
    <t>Property: include-metadata-sidecar</t>
    <ul>
      <li>
        <t>Description: Used to specify if a metadata sidecar file is generated for each log file. See section <xref target="h.58zpgabthxzl">7.1.1</xref></t>
      </li>
      <li>
        <t>Type: Boolean</t>
      </li>
      <li>
        <t>Mandatory-to-Specify: No</t>
      </li>
    </ul>
    <t>Property: interval</t>
    <ul>
      <li>
        <t>Description: For file-based logging transports, the time period for which a single log file SHOULD cover before being pushed to the endpoint, with the duration in seconds.</t>
      </li>
      <li>
        <t>Type: Integer</t>
      </li>
      <li>
        <t>Mandatory-to-Specify: No</t>
      </li>
    </ul>
    <t>Property: maximum-size</t>
    <ul>
      <li>
        <t>Description: For batch-type logging transports, the maximum file or object size before the log file SHOULD be pushed to the endpoint and a new file created for further entries. The value of the format is "&lt;Integer&gt;&lt;Specifier&gt;" where Specifier is one of the following:</t>
        <ul>
          <li>
            <t>M: megabyte</t>
          </li>
          <li>
            <t>G: gigabyte</t>
          </li>
        </ul>
      </li>
      <li>
        <t>Type: String</t>
      </li>
      <li>
        <t>Mandatory-to-Specify: No</t>
      </li>
    </ul>
    <t>Property: archive-metadata</t>
    <ul>
      <li>
        <t>Description: Used with the tar_v1 container type, it specifies the container metadata for files inside of the archive.</t>
      </li>
      <li>
        <t>Type: MI.LoggingContainerMetadata</t>
      </li>
      <li>
        <t>Mandatory-to-Specify: No</t>
      </li>
    </ul>
  </section>
  <section anchor="h.gqgncrrr3fz2">
    <name>MI.LoggingSampler</name>
    <t>This object defines the sampling configuration for a logging transport. It acts as a container for a specific sampler type and its corresponding configuration.</t>
    <t>Property: sampler-type</t>
    <ul>
      <li>
        <t>Description: The type discriminator for the value property.</t>
      </li>
      <li>
        <t>Type: String. The following values are supported:</t>
        <ul>
          <li>
            <t>MI.LoggingSamplerPercentage</t>
          </li>
          <li>
            <t>MI.LoggingSamplerConditional</t>
          </li>
          <li>
            <t>MI.LoggingSamplerSession</t>
          </li>
          <li>
            <t>MI.LoggingSamplerDebug</t>
          </li>
        </ul>
      </li>
      <li>
        <t>Mandatory-to-Specify: Yes</t>
      </li>
    </ul>
    <t>Property: value</t>
    <ul>
      <li>
        <t>Description: UThe configuration object for the specified sampler type.</t>
      </li>
      <li>
        <t>Type: Object</t>
      </li>
      <li>
        <t>Mandatory-to-Specify: Yes</t>
      </li>
    </ul>
    <t>The following is an example of an MI.LoggingSampler object in the context of MI.LoggingTransport:</t>
    <figure>
      <sourcecode><![CDATA["transports": [{
    "transport-type": "MI.LoggingTransportKafka",
    "record-type": "opencaching_standard_json_v1",
    "sampling": {
        "sampler-type": "MI.LoggingSamplerPercentage",
        "value": {
            "rate": 0.1
        }
    },
    "...": "..."
}]]]></sourcecode>
    </figure>
  </section>
  <section anchor="h.d3lxx6ab1t96">
    <name>MI.LoggingSamplerPercentage</name>
    <t>This object defines the configuration for random, percentage-based sampling.</t>
    <t>Property: rate</t>
    <ul>
      <li>
        <t>Description: The fraction of total records to log, expressed as a value between 0.0 and 1.0.</t>
      </li>
      <li>
        <t>Type: Number</t>
      </li>
      <li>
        <t>Mandatory-to-Specify: Yes</t>
      </li>
    </ul>
  </section>
  <section anchor="h.32su0jbao1c8">
    <name>MI.LoggingSamplerMEL</name>
    <t>This sampler applies different sampling rates based on the values of fields within the log record.</t>
    <t>Property: rules</t>
    <ul>
      <li>
        <t>Description: An ordered list of MEL sampling rules. Each rule is applied in turn. If the rule matches but the record is not selected for sampling based on the `rate` parameter, the next rule is then tested.</t>
      </li>
      <li>
        <t>Type: Array of objects. Each object has the following properties:</t>
        <ul>
          <li>
            <t>filter: A MEL filter expression string. A special value of `"default"` matches all records and SHOULD be placed last in the list.</t>
          </li>
          <li>
            <t>rate: The sampling rate to apply for matching records.</t>
          </li>
        </ul>
      </li>
      <li>
        <t>Mandatory-to-Specify: Yes</t>
      </li>
    </ul>
    <t>The following is an example of an MI.LoggingSamplerConditional object:</t>
    <figure>
      <sourcecode><![CDATA["value": {
    "rules": [
        { "filter": "record.sc-status >= 500 && record.default", "rate": 1.0 },
        { "filter": "record.s-cached == false", "rate": 0.5 },
        { "filter": "record.default", "rate": 0.01 }
    ]
}]]></sourcecode>
    </figure>
  </section>
  <section anchor="h.fo55htg7r8k7">
    <name>MI.LoggingSamplerSession</name>
    <t>This sampler performs consistent sampling based on a session identifier. When a session is selected for logging, all records sharing that session identifier are logged.</t>
    <t>Property: rate</t>
    <ul>
      <li>
        <t>Description: The fraction of unique session identifiers to sample, expressed as a value between 0.0 and 1.0.</t>
      </li>
      <li>
        <t>Type: Number</t>
      </li>
      <li>
        <t>Mandatory-to-Specify: Yes</t>
      </li>
    </ul>
    <t>Property: session-id-field</t>
    <ul>
      <li>
        <t>Description: The identifier of the request field to use for session grouping.</t>
      </li>
      <li>
        <t>Type: String</t>
      </li>
      <li>
        <t>Mandatory-to-Specify: Yes</t>
      </li>
    </ul>
    <t>Property: session-id-source</t>
    <ul>
      <li>
        <t>Description: Specifies where the dCDN should look for the identifier in the original request.</t>
      </li>
      <li>
        <t>Type: String. Valid values are "header", "query-param", or "both".</t>
      </li>
      <li>
        <t>Mandatory-to-Specify: Yes</t>
      </li>
    </ul>
  </section>
  <section anchor="h.azv14vcqk6z3">
    <name>MI.LoggingSamplerDebug</name>
    <t>This sampler forces logging for any request that contains a specific debug identifier. This is intended for diagnostic purposes. If the identifier is not present, the request is not logged by this sampler.</t>
    <t>Property: header-name</t>
    <ul>
      <li>
        <t>Description: The name of an HTTP header that, if present in a request, will trigger logging for that request.</t>
      </li>
      <li>
        <t>Type: String.</t>
      </li>
      <li>
        <t>Mandatory-to-Specify: No</t>
      </li>
    </ul>
    <t>Property: query-param-name</t>
    <ul>
      <li>
        <t>Description: The name of a URL query parameter that, if present in a request, will trigger logging for that request.</t>
      </li>
      <li>
        <t>Type: String.</t>
      </li>
      <li>
        <t>Mandatory-to-Specify: No</t>
      </li>
    </ul>
    <t>At least one of header-name or query-param-name MUST be specified.</t>
  </section>
</section><section anchor="h.blwf5sh4m6wg">
  <name>Logging Capabilities</name>
  <section anchor="h.jmqpjn51en3o">
    <name>FCI.LoggingPush</name>
    <t>The FCI.Logging object described in <xref target="RFC8008"/> is restricted to announcing available record types and OPTIONAL fields supported by the dCDN from those listed in <xref target="RFC7937"/>.</t>
    <t>This specification includes more record types that can be used by the uCDN in the log configuration as well as other possible metadata that can be used as container formats and possible pull transport methods.</t>
    <t>To permit the dCDN to announce multiple record types, container formats, and transport methods for the same footprint, this capability extends that described in <xref target="RFC8008"/> with a new capability dedicated to the push mechanisms.</t>
    <t>The following is an FCI.LoggingPush example:</t>
    <figure>
      <sourcecode><![CDATA[{
    "capability-type": "FCI.LoggingPush",
    "capability-value": {
            "record-types": [
                    "opencaching_minimal_json_v1",
                    "opencaching_extended_whitespace_v1"
            ],
            "allow-include-headers": true,
            "container-formats": ["json_v1", "carriage_return_v1"],
            "applied-transforms": [{
                    "record-fields": [
                            "c-ip"
                    ],
                    "operations": [{
                            "type": "MI.LoggingTransformMaskIp",
                            "value": {
                                    "mask-lsb-v4": 4,
                                    "mask-lsb-v6": 16
                            }
                    }]
            }]
    }
}]]></sourcecode>
    </figure>
    <t>Property: record-types</t>
    <ul>
      <li>
        <t>Description: The supported list of CDNI logging record-types.</t>
      </li>
      <li>
        <t>Type: Array of strings corresponding to an entry from the "CDNI logging record-types" registry <xref target="RFC7937"/>, or this specification</t>
      </li>
      <li>
        <t>Mandatory-to-Specify: Yes</t>
      </li>
    </ul>
    <t>Property: container-formats</t>
    <ul>
      <li>
        <t>Description: The supported list of container formats according to this specification.</t>
      </li>
      <li>
        <t>Type: Array of strings corresponding to available container formats</t>
      </li>
      <li>
        <t>Mandatory-to-Specify: No. If only streaming transports are used, container formats are not used. If not included, no non-streaming containers would be available.</t>
      </li>
    </ul>
    <t>Property: allow-include-headers</t>
    <ul>
      <li>
        <t>Description: A flag indicating support for custom headers configured by the uCDN for inclusion in the log records.</t>
      </li>
      <li>
        <t>Type: Boolean</t>
      </li>
      <li>
        <t>Mandatory-to-Specify: No</t>
      </li>
    </ul>
    <t> Property: applied-transforms</t>
    <ul>
      <li>
        <t>Description: Transforms applied by the dCDN on all logs to the upstream. The sets of record-fields field names MUST NOT overlap for the transport.</t>
      </li>
      <li>
        <t>Type: Array of MI.LoggingTransforms objects</t>
      </li>
      <li>
        <t>Mandatory-to-Specify: No</t>
      </li>
    </ul>
    <t>Property: sftp-public-keys</t>
    <ul>
      <li>
        <t>Description: An array of SSH public keys that the dCDN SHOULD authorize on SFTP logging endpoints.</t>
      </li>
      <li>
        <t>Type: Array of strings</t>
      </li>
      <li>
        <t>Mandatory-to-Specify: No</t>
      </li>
    </ul>
    <t>Property: allow-include-fields</t>
    <ul>
      <li>
        <t>Description: A flag indicating support for appended additional logging fields configured by the uCDN.</t>
      </li>
      <li>
        <t>Type: Boolean</t>
      </li>
      <li>
        <t>Mandatory-to-Specify: No</t>
      </li>
    </ul>
    <t>Property: custom-fields</t>
    <ul>
      <li>
        <t>Description: Custom advertised logging fields that may be appended to a logging record via include-fields and included-fields.</t>
      </li>
      <li>
        <t>Type: Array of FCI.LoggingField objects. Each object MUST have a unique value for field-name.</t>
      </li>
      <li>
        <t>Mandatory-to-Specify: No</t>
      </li>
    </ul>
    <t>Property: sampling-support</t>
    <ul>
      <li>
        <t>Description: An object that describes the dCDN's sampling capabilities.</t>
      </li>
      <li>
        <t>Type: Object</t>
      </li>
      <li>
        <t>Mandatory-to-Specify: No</t>
      </li>
      <li>
        <t>Object properties:</t>
      </li>
    </ul>
    <t>The following is an example of FCI.LoggingPush with sampling-support:</t>
    <figure>
      <sourcecode><![CDATA[{
   "capability-type": "FCI.LoggingPush",
   "capability-value": {
       "record-types": ["..."],
       "sampling-support": {
           "sampler-types": [
               "MI.LoggingSamplerPercentage",
               "MI.LoggingSamplerConditional",
               "MI.LoggingSamplerSession",
               "MI.LoggingSamplerDebug"
           ]
       }
   }
}]]></sourcecode>
    </figure>
  </section>
  <section anchor="h.tsu3kwem5jjd">
    <name>FCI.LoggingSource</name>
    <t>A dCDN can provide information on available logging endpoints to the uCDN that do not require metadata configuration. The FCI.LoggingSource capability object can be announced with the relevant information for the uCDN to get the log from the dCDN.</t>
    <t>The dCDN could provide different endpoints, depending on footprint and/or delegated hosts. It could be possible that the sources in FCI.LoggingSource in the dCDN are not available for all delegated hosts or all available footprints, permitting flexibility and control of its own infrastructure (e.g., a dCDN could limit logging endpoints to suitable hosts included in an agreement between a dCDN and uCDN).</t>
    <t>Property: hosts</t>
    <ul>
      <li>
        <t>Description: One or more uCDN hosts for which the logs are available for pull logging transports. A redirecting host SHOULD be a host that was published in an MI.HostMatch object by the uCDN as defined in section 4.1.2 of <xref target="RFC8006"/>.</t>
      </li>
      <li>
        <t>Type: A list of endpoint objects (see section 4.3.3 of <xref target="RFC8006"/>)</t>
      </li>
      <li>
        <t>Mandatory-to-Specify:No. If absent or empty, the logging endpoint is available for all hosts of the redirecting uCDN.</t>
      </li>
    </ul>
    <t>  Property: transports</t>
    <ul>
      <li>
        <t>Description: List of available sources for an uCDN to obtain the logs for its delegated services</t>
      </li>
      <li>
        <t>Type: Array of FCI.LoggingPullTransport objects</t>
      </li>
      <li>
        <t>Mandatory-to-Specify: No</t>
      </li>
    </ul>
    <t>  Property: included-fields</t>
    <ul>
      <li>
        <t>Description: Additional log record fields to be appended to the fields defined by the predefined type specified in record-type. Headers may also be specified using the sc-hdr-* and cs-hdr-* field name convention.</t>
      </li>
      <li>
        <t>Type: Ordered array of fields</t>
      </li>
      <li>
        <t>Mandatory-to-Specify: No</t>
      </li>
    </ul>
    <t>Property: custom-fields</t>
    <ul>
      <li>
        <t>Description: Custom advertised logging fields that may be appended to a logging record via included-fields.</t>
      </li>
      <li>
        <t>Type: Array of FCI.LoggingField objects. Each object MUST have a unique value for field-name.</t>
      </li>
      <li>
        <t>Mandatory-to-Specify: No</t>
      </li>
    </ul>
    <t>Property: sampling</t>
    <ul>
      <li>
        <t>Description: An MI.LoggingSampler object that describes the sampling configuration that has been applied to the log records available at this pull transport.</t>
      </li>
      <li>
        <t>Type: MI.LoggingSampler</t>
      </li>
      <li>
        <t>Mandatory-to-Specify: No</t>
      </li>
    </ul>
    <t>The following is an FCI.LoggingSource example that shows use of an S3 bucket for retrieving JSON formatted logs in a JSON container:</t>
    <figure>
      <sourcecode><![CDATA[{
    "capability-type": "FCI.LoggingSource",
    "capability-value": {
            "hosts": [
                    "a.service123.ucdn.example.com",
                    "b.service123.ucdn.example.com"
            ],
            "transports": [{
                    "type": "FCI.LoggingPullTransportS3",
                    "record-type": "opencaching_standard_json_v1",
                    "container-metadata": {
                            "container-type": "json_v1",
                            "filename-template": "%Y-%m-%d-access.%i.log",
                            "interval": 86400
                    },
                    "account-id": "dCDN1",
                    "endpoint": {
                            "host": "s3.amazonaws.com",
                            "access-key-id": {
                        "secret-store-id": "store-1-cms",
                        "secret-value": "MIIBiQYJKoZIhvcNAQcDoIIBejCCAXYCAQAxggEhMIIBHQIBAD
AFMAACAQEwDQYJKoZIhvcNAQEBBQAEggEApJeXzsUS1jbAyNtQiJ9um9IMIHW5B2g+gHn
XdNSTyd33OEfTR6yLSZihBlFbHpY3qSzK1CX7RF5Oz3SqLDW+r3i1D/aHbVXwQbviWHEv
Hterql8l9VDm2FCNaDx5vihdbtvng3+/vdJNNMMhmovwZL5uhPsK81DkKwZCvznMMWt8Y
dNSFGT62f73ash7Eg/mS54IUyYOJHYrXEkRLSjvl0j+JqcIR8hCOCA78+5bS4MgfdsS9x
xSwQTrPru6EdTivMDKE/jlKg7li8lWdirWqtv0za5gLmH5T+zslXIoklwERAE50Jj8FxZ
D98EikKH8DAa+JeFsBm6Z1+yVFsWucTBMBgkqhkiG9w0BBwEwHQYJYIZIAWUDBAEqBBBw
s1riXA6m336zRbsiKtrVgCA267133v2zD/wjFQHXrKSJfd/2YJaxPskgdmQaVlgWCw=="
                      },
                            "bucket-name": "dcdnlogs"
                    }
            }]
    }
}]]></sourcecode>
    </figure>
    <t> </t>
  </section>
  <section anchor="h.2t1wmmgu4hpb">
    <name>FCI.LoggingPullTransport</name>
    <t>Property: transport-type</t>
    <ul>
      <li>
        <t>Description: The type discriminator for the endpoint property. The following values are supported:</t>
        <ul>
          <li>
            <t>FCI.LoggingPullTransportAtomFeed</t>
          </li>
          <li>
            <t>FCI.LoggingPullTransportSFTP</t>
          </li>
          <li>
            <t>FCI.LoggingPullTransportS3API</t>
          </li>
        </ul>
      </li>
      <li>
        <t>Type: String</t>
      </li>
      <li>
        <t>Mandatory-to-Specify: Yes</t>
      </li>
    </ul>
    <t>Property: record-type</t>
    <ul>
      <li>
        <t>Description: The format of the individual log records. This must be a valid value as defined in section <xref target="h.coa696tkzovl">5.</xref></t>
      </li>
      <li>
        <t>Type: String</t>
      </li>
      <li>
        <t>Mandatory-to-Specify: Yes</t>
      </li>
    </ul>
    <t>Property: container-metadata</t>
    <ul>
      <li>
        <t>Description: The configuration for the generation of log files when operating in file-based modes.</t>
      </li>
      <li>
        <t>Type: MI.LoggingContainerMetadata</t>
      </li>
      <li>
        <t>Mandatory-to-Specify:</t>
        <ul>
          <li>
            <t>File or object-based transports: Yes</t>
          </li>
          <li>
            <t>Streaming transports: No</t>
          </li>
        </ul>
      </li>
    </ul>
    <t>Property: transforms</t>
    <ul>
      <li>
        <t>Description: A list of transformations to be applied to sets of record fields before transport. A record field name SHOULD NOT appear more than once.</t>
      </li>
      <li>
        <t>Type: Array of MI.LoggingTransforms objects</t>
      </li>
      <li>
        <t>Mandatory-to-Specify: No</t>
      </li>
    </ul>
    <t>Property: endpoint</t>
    <ul>
      <li>
        <t>Description: An object with any configuration necessary for the specific logging transport, discriminated on the transport-type property. Objects for each transport type are described below.</t>
      </li>
      <li>
        <t>Type: Object</t>
      </li>
      <li>
        <t>Mandatory-to-Specify: Yes</t>
      </li>
    </ul>
  </section>
  <section anchor="h.ke6kenlrlcav">
    <name>FCI.LoggingPullTransportAtomFeed</name>
    <t>Property: feed-href</t>
    <ul>
      <li>
        <t>Description: The URL of the CDNI logging feed in Atom format per <xref target="RFC7937"/>.</t>
      </li>
      <li>
        <t>Type: String</t>
      </li>
      <li>
        <t>Mandatory-to-Specify: Yes</t>
      </li>
    </ul>
    <t>The following is an FCI.LoggingSourceAtomFeed example:</t>
    <figure>
      <sourcecode><![CDATA[{
    "capability-type": "FCI.LoggingSource",
    "capability-value": {
            "hosts": [
                    "a.service123.ucdn.example.com",
                    "b.service123.ucdn.example.com"
            ],
            "transports": [{
                    "transport-type": "FCI.LoggingPullTransportAtomFeed",
                    "record-type": "opencaching_standard_json_v1",
                    "container-metadata": {
                            "container-type": "cdni_v1",
                            "filename-template": "%Y-%m-%d-access.%i.log",
                            "interval": 86400
                    },
                    "endpoint": {
                            "feed-href": "https://occ.example.com/logs/index.atom"
                    }
            }]
    }
}]]></sourcecode>
    </figure>
  </section>
  <section anchor="h.wm2h1xmo436t">
    <name>FCI.LoggingPullTransportSFTP</name>
    <t>Property: host</t>
    <ul>
      <li>
        <t>Description: The hostname of the SFTP server, which MUST be specified as either hostname or hostname:port.</t>
      </li>
      <li>
        <t>Type: String</t>
      </li>
      <li>
        <t>Mandatory-to-Specify: Yes</t>
      </li>
    </ul>
    <t>Property: username</t>
    <ul>
      <li>
        <t>Description: The remote username to use for connecting to the SFTP server.</t>
      </li>
      <li>
        <t>Type: String</t>
      </li>
      <li>
        <t>Mandatory-to-Specify: Yes</t>
      </li>
    </ul>
    <t>Property: password</t>
    <ul>
      <li>
        <t>Description: The remote password to use for connecting to the SFTP server when using password-based authentication. This field is not used when SSH keys are provided by the uCDN via MI.LoggingPullTransportMetadata.</t>
      </li>
      <li>
        <t>Type: MI.SecretValue</t>
      </li>
      <li>
        <t>Mandatory-to-Specify: No</t>
      </li>
    </ul>
    <t>The following is an FCI.LoggingPullTransportSFTP example:</t>
    <figure>
      <sourcecode><![CDATA[{
    "capability-type": "FCI.LoggingSource",
    "capability-value": {
            "hosts": [
                    "a.service123.ucdn.example.com",
                    "b.service123.ucdn.example.com"
            ],
            "transports": [{
                    "type": "FCI.LoggingPullTransportSFTP",
                    "record-type": "opencaching_standard_json_v1",
                    "container-metadata": {
                            "container-type": "json_v1",
                            "filename-template": "%Y-%m-%d-access.%i.log",
                            "interval": 86400
                    },
                    "endpoint": {
                            "host": "logs.dcdn.example.com",
                            "username": "logsucdn"
                    }
            }]
    }

}]]></sourcecode>
    </figure>
  </section>
  <section anchor="h.bnlhkd5lqo8g">
    <name>FCI.LoggingPullTransportS3API</name>
    <t>Property: host</t>
    <ul>
      <li>
        <t>Description: The hostname to connect to the S3-compatible API.</t>
      </li>
      <li>
        <t>Type: String</t>
      </li>
      <li>
        <t>Mandatory-to-Specify: Yes</t>
      </li>
    </ul>
    <t>Property: access-key-id</t>
    <ul>
      <li>
        <t>Description: The access key ID to use when connecting to the API.</t>
      </li>
      <li>
        <t>Type: String</t>
      </li>
      <li>
        <t>Mandatory-to-Specify: Yes</t>
      </li>
    </ul>
    <t>Property: access-key-secret</t>
    <ul>
      <li>
        <t>Description: The access key secret to use when connecting to the API.</t>
      </li>
      <li>
        <t>Type: MI.SecretValue</t>
      </li>
      <li>
        <t>Mandatory-to-Specify: Yes</t>
      </li>
    </ul>
    <t>Property: bucket-name</t>
    <ul>
      <li>
        <t>Description: The bucket name to use to pull log file objects.</t>
      </li>
      <li>
        <t>Type: String</t>
      </li>
      <li>
        <t>Mandatory-to-Specify: Yes</t>
      </li>
    </ul>
    <t>The following is an FCI.LoggingPullTransportS3API example:</t>
    <figure>
      <sourcecode><![CDATA[{
    "capability-type": "FCI.LoggingSource",
    "capability-value": {
            "hosts": [
                    "a.service123.ucdn.example.com",
                    "b.service123.ucdn.example.com"
            ],
            "transports": [{
                    "type": "FCI.LoggingPullTransportS3API",
                    "record-type": "opencaching_standard_json_v1",
                    "container-metadata": {
                            "container-type": "json_v1",
                            "name-template": "%Y-%m-%d-access.log",
                            "interval": 86400
                    },
                    "endpoint": {
                            "host": "xxxxxxxxxxxxx",
                            "access-key-id": "xxxxxxxxx",
                            "access-key-secret": {
                                    "secret-store-id": "store-1",
                                    "secret-store-path": "/logging/dCDN1/dcdnlogs/s3"
                            },
                            "bucket-name": "ucdnlogs"
                    }
            }]
    }
}]]></sourcecode>
    </figure>
  </section>
  <section anchor="h.kkmak3iryf0u">
    <name>FCI.LoggingField</name>
    <t>Property: field-name</t>
    <ul>
      <li>
        <t>Description: A unique identifier for this custom logging field.</t>
      </li>
      <li>
        <t>Type: String</t>
      </li>
      <li>
        <t>Mandatory-to-Specify: Yes</t>
      </li>
    </ul>
    <t>Property: field-type</t>
    <ul>
      <li>
        <t>Description: The data type of the field value.</t>
      </li>
      <li>
        <t>Type: One of "String", "Numeric"</t>
      </li>
      <li>
        <t>Mandatory-to-Specify: Yes</t>
      </li>
    </ul>
    <t>Property: description</t>
    <ul>
      <li>
        <t>Description: A human readable description of the field.</t>
      </li>
      <li>
        <t>Type: String</t>
      </li>
      <li>
        <t>Mandatory-to-Specify: No</t>
      </li>
    </ul>
    <t>Property: documentation-href</t>
    <ul>
      <li>
        <t>Description: A link to external documentation for this field.</t>
      </li>
      <li>
        <t>Type: URL</t>
      </li>
      <li>
        <t>Mandatory-to-Specify: No</t>
      </li>
    </ul>
  </section>
</section><section anchor="h.2ig4356urxei">
  <name>Appendix</name>
  <section anchor="h.pv2ceok296tc">
    <name>Protobuf Record Type Definitions</name>
    <t>This subsection provides the protobuf definitions in the proto file format for the protobuf record types defined in <xref target="h.846yu5ttx32k">Section 4.4</xref></t>
    <figure>
      <sourcecode><![CDATA[syntax = "proto3";
message OCLogRequestV1 {
  google.protobuf.Timestamp timestamp = 1;                            
  uint64 s_time_total_ms = 3;                  
  string c_groupid = 4;
  Method cs_method = 5;                        
  float cs_version = 6;                        
  string cs_uri = 7;                          
  string cs_uri_transformed = 8;              
  uint32 sc_status = 9;                        
  uint64 sc_total_bytes = 10;
  string ccid = 11;          
  string s_sid = 12;                
  CacheStatus s_cached = 13;                
  uint64 sc_header_size_bytes = 14;          
  string c_ip = 15;                          
  string s_shortname = 16;                    
  string s_id = 17;                            
  uint64 s_time_first_ms = 18;                
  uint64 s_sdur_ms = 19;                        
  uint64 s_time_upstream_ms = 20;            
  uint64 s_upstream_header_size_bytes = 21;            
  uint64 s_upstream_content_size_bytes = 22;            
  repeated HttpHeader cs_hdr = 23;              
  repeated HttpHeader sc_hdr = 24;              
}
// Available at github.com/streaming-video-technology-alliance/logging]]></sourcecode>
    </figure>
  </section>
</section><section anchor="h.4nuv7r1b97o">
  <name>Security Considerations</name>
  <section anchor="h.xph57r44lkh0">
    <name>Field Privacy </name>
    <t>Logging records may contain a user's personally identifiable information (PII), which may be inappropriate for the dCDN to share, and/or for the uCDN to store. This is particularly important when the dCDN is supporting custom headers. This specification provides MI.LoggingTransforms defined in section <xref target="h.993807dxae2p">7.4.</xref> as a mechanism for truncating, replacing, encrypting, and/or hashing record field values.</t>
    <t>The uCDN can request transformations be applied to record fields with the transforms property of the MI.LoggingTransport object defined in section <xref target="h.3gcqfmy6uk0z">7.3.</xref> The dCDN advertises transformations applied to record fields with the applied-transforms property of FCI.LoggingPush defined in section <xref target="h.jmqpjn51en3o">8.1</xref></t>
  </section>
  <section anchor="h.fwl7k1wp105o">
    <name>Field Encryption </name>
    <t>In using the MI.LoggingTransformEncrypt defined in section <xref target="h.bpqj1aputagz">7.4.9</xref>, the nonce SHOULD NOT be reused across OCN nodes using a counter permutation. When the uniqueness of nonces cannot be ensured, you can use the XChaCha20 algorithm with a nonce from a random source with sufficient entropy, or you can take the hash of a weaker random source, concatenate it with the message and hash it with a cryptographic hash function that is safe against length-extension attacks, such as BLAKE2 or the HMAC construction.</t>
    <t>Rivest Cipher 4 (RC4) is listed to support annotating values originating from a packet cores' header enrichment network function for sharing subscriber or network identifiers with web applications. It SHOULD NOT be used as a security construct for concealing data in logs. An implementation would effectively be a no-op passing the value with bound credentials. It is RECOMMENDED to chain another MI.LoggingTransformEncrypt operation with a modern algorithm for actual confidentiality.</t>
  </section>
  <section anchor="h.ete3w7ljtfj7">
    <name>Field Length </name>
    <t>It is important to consider using an MI.LoggingTransformTruncate in implementations that MAY have constraints on individual field record lengths. The dCDN can advertise these field length constraints in the applied-transforms property of FCI.LoggingPush and the transforms property of FCI.LoggingPullTransport*.</t>
    <t>It is strongly RECOMMENDED to include an MI.LoggingTransformTruncate when including any cs-hdr-* OR sc-hdr-* fields in the logging format to protect against any denial of service (DoS) risks that arise from inadvertently malformed or actively malicious values in the headers.</t>
  </section>
  <section anchor="h.6ki5fvwfivmd">
    <name>Atom Endpoints</name>
    <t>Authentication and authorization mechanisms for the Atom feed are outside the scope of this document.</t>
  </section>
</section><section anchor="IANA" title="IANA Considerations">
</section><section anchor="Acknowledgements" title="Acknowledgements">
            <t>
                The authors would like to express their gratitude to the members of the Streaming Video Technology Alliance <xref target="SVTA"/> Open Caching Working Group for their guidance / contribution / reviews ...)
            </t>
            <t>Particulary the following people contributed to the content of this draft:</t>
                <ul>
                    <li>Glenn Goldstein (Lumen)</li>
                    <li>Christoph Neumann (Broadpeak)</li>
                    <li>Rajeev RK (PicoNETS)</li>
                    <li>Dave Seddon (Siden)</li>
                    <li>Alfonso Silóniz (Telefónica)</li>
                    <li>Matt Stock (Viasat)</li>
                </ul>
        </section>
   </middle>
   <back>
      <references title="Normative References">
         <xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.2119.xml"/>
         <xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.4180.xml"/>
         <xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.7937.xml"/>
         <xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.8006.xml"/>
         <xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.8008.xml"/>
         <xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.9110.xml"/>
      </references>
      <references title="Informative References">
        <reference anchor="SVTA" target="https://www.svta.org">
            <front>
               <title>Streaming Video Technology Alliance Home Page</title>
               <author><organization>SVTA</organization></author>
               <date />
            </front>
         </reference>
      </references>
   </back>
</rfc>
