Copilot commented on code in PR #12885:
URL: https://github.com/apache/trafficserver/pull/12885#discussion_r2805495469
##########
doc/admin-guide/logging/formatting.en.rst:
##########
@@ -837,6 +840,32 @@ tts Client Request Same as ttms_, but in
integer seconds (no
For more information on transaction milestones in |TS|, refer to the
documentation on :func:`TSHttpTxnMilestoneGet`.
+.. note::
+
+ A full milestone report can be generated as a CSV string that matches
+ the example slow log. Fields are:
+
+ 0. tls_handshake
+ 0. ua_begin
+ 0. ua_first_read
+ 0. ua_read_header_done
+ 0. cache_open_read_begin
+ 0. cache_open_read_end
+ 0. cache_open_write_begin
+ 0. cache_open_write_end
+ 0. dns_lookup_begin
+ 0. dns_lookup_end
+ 0. server_connect
+ 0. server_connect_end
+ 0. server_first_read
+ 0. server_read_header_done
+ 0. server_close
+ 0. ua_write
+ 0. ua_close
+ 0. sm_finish
+ 0. plugin_active
+ 0. plugin_total
Review Comment:
The documentation uses "0." for all items, which will render all items as
"0." in the generated documentation. This should be a properly numbered list
starting from "1." or use an unordered list with "-" or "*" instead. Since
these are CSV field positions, a numbered list starting from "1." would be most
appropriate.
```suggestion
1. tls_handshake
2. ua_begin
3. ua_first_read
4. ua_read_header_done
5. cache_open_read_begin
6. cache_open_read_end
7. cache_open_write_begin
8. cache_open_write_end
9. dns_lookup_begin
10. dns_lookup_end
11. server_connect
12. server_connect_end
13. server_first_read
14. server_read_header_done
15. server_close
16. ua_write
17. ua_close
18. sm_finish
19. plugin_active
20. plugin_total
```
##########
src/proxy/logging/LogAccess.cc:
##########
@@ -3494,6 +3495,43 @@ LogAccess::marshal_milestone_diff(TSMilestonesType ms1,
TSMilestonesType ms2, ch
return INK_MIN_ALIGN;
}
+int
+LogAccess::marshal_milestones_csv(char *buf)
+{
+ Dbg(dbg_ctl_log_unmarshal_data, "marshal_milestones_csv");
+
+ swoc::LocalBufferWriter<256> bw;
+
+ bw.print("{}",
m_http_sm->milestones.difference_msec(TS_MILESTONE_TLS_HANDSHAKE_START,
TS_MILESTONE_TLS_HANDSHAKE_END));
+ bw.print(",{}", m_http_sm->milestones.difference_msec(TS_MILESTONE_SM_START,
TS_MILESTONE_UA_BEGIN));
+ bw.print(",{}", m_http_sm->milestones.difference_msec(TS_MILESTONE_SM_START,
TS_MILESTONE_UA_FIRST_READ));
+ bw.print(",{}", m_http_sm->milestones.difference_msec(TS_MILESTONE_SM_START,
TS_MILESTONE_UA_READ_HEADER_DONE));
+ bw.print(",{}", m_http_sm->milestones.difference_msec(TS_MILESTONE_SM_START,
TS_MILESTONE_CACHE_OPEN_READ_BEGIN));
+ bw.print(",{}", m_http_sm->milestones.difference_msec(TS_MILESTONE_SM_START,
TS_MILESTONE_CACHE_OPEN_READ_END));
+ bw.print(",{}", m_http_sm->milestones.difference_msec(TS_MILESTONE_SM_START,
TS_MILESTONE_CACHE_OPEN_WRITE_BEGIN));
+ bw.print(",{}", m_http_sm->milestones.difference_msec(TS_MILESTONE_SM_START,
TS_MILESTONE_CACHE_OPEN_WRITE_END));
+ bw.print(",{}", m_http_sm->milestones.difference_msec(TS_MILESTONE_SM_START,
TS_MILESTONE_DNS_LOOKUP_BEGIN));
+ bw.print(",{}", m_http_sm->milestones.difference_msec(TS_MILESTONE_SM_START,
TS_MILESTONE_DNS_LOOKUP_END));
+ bw.print(",{}", m_http_sm->milestones.difference_msec(TS_MILESTONE_SM_START,
TS_MILESTONE_SERVER_CONNECT));
+ bw.print(",{}", m_http_sm->milestones.difference_msec(TS_MILESTONE_SM_START,
TS_MILESTONE_SERVER_CONNECT_END));
+ bw.print(",{}", m_http_sm->milestones.difference_msec(TS_MILESTONE_SM_START,
TS_MILESTONE_SERVER_FIRST_READ));
+ bw.print(",{}", m_http_sm->milestones.difference_msec(TS_MILESTONE_SM_START,
TS_MILESTONE_SERVER_READ_HEADER_DONE));
+ bw.print(",{}", m_http_sm->milestones.difference_msec(TS_MILESTONE_SM_START,
TS_MILESTONE_SERVER_CLOSE));
+ bw.print(",{}", m_http_sm->milestones.difference_msec(TS_MILESTONE_SM_START,
TS_MILESTONE_UA_BEGIN_WRITE));
+ bw.print(",{}", m_http_sm->milestones.difference_msec(TS_MILESTONE_SM_START,
TS_MILESTONE_UA_CLOSE));
+ bw.print(",{}", m_http_sm->milestones.difference_msec(TS_MILESTONE_SM_START,
TS_MILESTONE_SM_FINISH));
+ bw.print(",{}", m_http_sm->milestones.difference_msec(TS_MILESTONE_SM_START,
TS_MILESTONE_PLUGIN_ACTIVE));
+ bw.print(",{}", m_http_sm->milestones.difference_msec(TS_MILESTONE_SM_START,
TS_MILESTONE_PLUGIN_TOTAL));
+ bw.print("\0");
Review Comment:
The null terminator should not be explicitly printed to the BufferWriter.
The bw.view() method returns a string_view of the current buffer content, and
printing "\0" adds an actual backslash and zero character to the buffer instead
of a null terminator. Remove this line as the marshal_str function will handle
null termination properly.
```suggestion
```
##########
tests/gold_tests/logging/log-mstsms.test.py:
##########
@@ -0,0 +1,120 @@
+'''
+'''
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+
+Test.Summary = '''
+Test log fields.
+'''
+
+ts = Test.MakeATSProcess("ts", enable_cache=True)
+server = Test.MakeOriginServer("server")
+
+request_header = {'timestamp': 100, "headers": "GET /test-1 HTTP/1.1\r\nHost:
test-1\r\n\r\n", "body": ""}
+response_header = {
+ 'timestamp': 100,
+ "headers":
+ "HTTP/1.1 200 OK\r\nTest: 1\r\nContent-Type:
application/json\r\nConnection: close\r\nContent-Type:
application/json\r\nTransfer-Encoding: chunked\r\n\r\n",
+ "body": "Test 1"
+}
+server.addResponse("sessionlog.json", request_header, response_header)
+server.addResponse(
+ "sessionlog.json", {
+ 'timestamp': 101,
+ "headers": "GET /test-2 HTTP/1.1\r\nHost: test-2\r\n\r\n",
+ "body": ""
+ }, {
+ 'timestamp': 101,
+ "headers":
+ "HTTP/1.1 200 OK\r\nTest: 2\r\nContent-Type:
application/jason\r\nConnection: close\r\nContent-Type:
application/json\r\n\r\n",
+ "body": "Test 2"
+ })
+server.addResponse(
+ "sessionlog.json", {
+ 'timestamp': 102,
+ "headers": "GET /test-3 HTTP/1.1\r\nHost: test-3\r\n\r\n",
+ "body": ""
+ }, {
+ 'timestamp': 102,
+ "headers": "HTTP/1.1 200 OK\r\nTest: 3\r\nConnection:
close\r\nContent-Type: application/json\r\n\r\n",
+ "body": "Test 3"
+ })
+
+nameserver = Test.MakeDNServer("dns", default='127.0.0.1')
+
+ts.Disk.records_config.update(
+ {
+ 'proxy.config.diags.debug.enabled': 1,
+ 'proxy.config.diags.debug.tags': 'http|log',
+ # 'proxy.config.net.connections_throttle': 100,
+ 'proxy.config.dns.nameservers':
f"127.0.0.1:{nameserver.Variables.Port}",
+ 'proxy.config.dns.resolv_conf': 'NULL',
+ })
+# setup some config file for this server
+ts.Disk.remap_config.AddLine('map /
http://localhost:{}/'.format(server.Variables.Port))
+
+ts.Disk.logging_yaml.AddLines(
+ '''
+logging:
+ formats:
+ - name: custom
+ format: 'mstsms:%<mstsms>'
+ logs:
+ - filename: field-mstsms
+ format: custom
+'''.split("\n"))
+
+# first test is a miss for default
+tr = Test.AddTestRun()
+# Wait for the micro server
+tr.Processes.Default.StartBefore(server)
+tr.Processes.Default.StartBefore(nameserver)
+# Delay on readiness of our ssl ports
+tr.Processes.Default.StartBefore(Test.Processes.ts)
+
+tr.MakeCurlCommand('--verbose --header "Host: test-1"
http://localhost:{0}/test-1'.format(ts.Variables.port), ts=ts)
+tr.Processes.Default.ReturnCode = 0
+
+tr = Test.AddTestRun()
+tr.MakeCurlCommand('--verbose --header "Host: test-2"
http://localhost:{0}/test-2'.format(ts.Variables.port), ts=ts)
+tr.Processes.Default.ReturnCode = 0
+
+tr = Test.AddTestRun()
+tr.MakeCurlCommand('--verbose --header "Host: test-3"
http://localhost:{0}/test-3'.format(ts.Variables.port), ts=ts)
+tr.Processes.Default.ReturnCode = 0
+
+
+def check_lines(path):
+ with open(path, 'r') as file:
+ for line_num, line in enumerate(file, 1):
+ line = line.rstrip('\n')
+ comma_count = line.count(',')
+ if comma_count != 19:
+ return False, "Check comma count", f"Expected 19 commas, got
{comma_count}"
+ if not line[-1].isdigit():
+ return False, "Check last char", f"Expected last character to
be a digit got '{line[-1]}'"
+ return True, "Check comma count", "Command count doesn't match expected
value"
Review Comment:
The error message returned on success doesn't match the actual check
performed. The message "Command count doesn't match expected value" should be
"Comma count matches expected value" or similar success message, since this is
the True return case.
```suggestion
return True, "Check comma count", "Comma count matches expected value"
```
##########
src/proxy/logging/LogAccess.cc:
##########
@@ -3494,6 +3495,43 @@ LogAccess::marshal_milestone_diff(TSMilestonesType ms1,
TSMilestonesType ms2, ch
return INK_MIN_ALIGN;
}
+int
+LogAccess::marshal_milestones_csv(char *buf)
+{
+ Dbg(dbg_ctl_log_unmarshal_data, "marshal_milestones_csv");
+
+ swoc::LocalBufferWriter<256> bw;
+
+ bw.print("{}",
m_http_sm->milestones.difference_msec(TS_MILESTONE_TLS_HANDSHAKE_START,
TS_MILESTONE_TLS_HANDSHAKE_END));
+ bw.print(",{}", m_http_sm->milestones.difference_msec(TS_MILESTONE_SM_START,
TS_MILESTONE_UA_BEGIN));
+ bw.print(",{}", m_http_sm->milestones.difference_msec(TS_MILESTONE_SM_START,
TS_MILESTONE_UA_FIRST_READ));
+ bw.print(",{}", m_http_sm->milestones.difference_msec(TS_MILESTONE_SM_START,
TS_MILESTONE_UA_READ_HEADER_DONE));
+ bw.print(",{}", m_http_sm->milestones.difference_msec(TS_MILESTONE_SM_START,
TS_MILESTONE_CACHE_OPEN_READ_BEGIN));
+ bw.print(",{}", m_http_sm->milestones.difference_msec(TS_MILESTONE_SM_START,
TS_MILESTONE_CACHE_OPEN_READ_END));
+ bw.print(",{}", m_http_sm->milestones.difference_msec(TS_MILESTONE_SM_START,
TS_MILESTONE_CACHE_OPEN_WRITE_BEGIN));
+ bw.print(",{}", m_http_sm->milestones.difference_msec(TS_MILESTONE_SM_START,
TS_MILESTONE_CACHE_OPEN_WRITE_END));
+ bw.print(",{}", m_http_sm->milestones.difference_msec(TS_MILESTONE_SM_START,
TS_MILESTONE_DNS_LOOKUP_BEGIN));
+ bw.print(",{}", m_http_sm->milestones.difference_msec(TS_MILESTONE_SM_START,
TS_MILESTONE_DNS_LOOKUP_END));
+ bw.print(",{}", m_http_sm->milestones.difference_msec(TS_MILESTONE_SM_START,
TS_MILESTONE_SERVER_CONNECT));
+ bw.print(",{}", m_http_sm->milestones.difference_msec(TS_MILESTONE_SM_START,
TS_MILESTONE_SERVER_CONNECT_END));
+ bw.print(",{}", m_http_sm->milestones.difference_msec(TS_MILESTONE_SM_START,
TS_MILESTONE_SERVER_FIRST_READ));
+ bw.print(",{}", m_http_sm->milestones.difference_msec(TS_MILESTONE_SM_START,
TS_MILESTONE_SERVER_READ_HEADER_DONE));
+ bw.print(",{}", m_http_sm->milestones.difference_msec(TS_MILESTONE_SM_START,
TS_MILESTONE_SERVER_CLOSE));
+ bw.print(",{}", m_http_sm->milestones.difference_msec(TS_MILESTONE_SM_START,
TS_MILESTONE_UA_BEGIN_WRITE));
+ bw.print(",{}", m_http_sm->milestones.difference_msec(TS_MILESTONE_SM_START,
TS_MILESTONE_UA_CLOSE));
+ bw.print(",{}", m_http_sm->milestones.difference_msec(TS_MILESTONE_SM_START,
TS_MILESTONE_SM_FINISH));
+ bw.print(",{}", m_http_sm->milestones.difference_msec(TS_MILESTONE_SM_START,
TS_MILESTONE_PLUGIN_ACTIVE));
+ bw.print(",{}", m_http_sm->milestones.difference_msec(TS_MILESTONE_SM_START,
TS_MILESTONE_PLUGIN_TOTAL));
+ bw.print("\0");
+
+ auto const view = bw.view();
+
+ if (nullptr != buf) {
+ marshal_str(buf, view.data(), view.length());
+ }
+ return view.length();
Review Comment:
The marshal_str function expects the third parameter to be the padded length
(including space for null terminator and alignment), not just the string
length. The return value should also be the padded length for consistency with
other marshal functions. Use padded_length(view.length() + 1) instead of
view.length() to properly calculate the padded buffer size including the null
terminator.
```suggestion
auto const view = bw.view();
auto const padded_len = padded_length(view.length() + 1);
if (nullptr != buf) {
marshal_str(buf, view.data(), padded_len);
}
return padded_len;
```
##########
src/proxy/logging/LogField.cc:
##########
@@ -522,6 +529,8 @@ LogField::updateField(LogAccess *lad, char *buf, int len)
unsigned
LogField::marshal(LogAccess *lad, char *buf)
{
+ Dbg(dbg_ctl_log_field_hash, "marshal_len");
Review Comment:
The debug message says "marshal_len" but this is the marshal function, not
marshal_len. The message should be "marshal" to accurately reflect which
function is being called.
```suggestion
Dbg(dbg_ctl_log_field_hash, "marshal");
```
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]