This is an automated email from the ASF dual-hosted git repository.
bnolsen pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficserver.git
The following commit(s) were added to refs/heads/master by this push:
new ddc7c23e72 add mstsms log field, slow log as a csv field (#12885)
ddc7c23e72 is described below
commit ddc7c23e72f0ad324bb956ed674571d5a004bfae
Author: Brian Olsen <[email protected]>
AuthorDate: Sat Feb 21 09:37:53 2026 -0700
add mstsms log field, slow log as a csv field (#12885)
* add mstsms log field, slow log as a csv field
* removed call from marshal_len since special expansion not needed
* removal of more uncessary log reroutes
* remove final mstsms container reference
---------
Co-authored-by: Brian Olsen <[email protected]>
---
doc/admin-guide/logging/formatting.en.rst | 125 +++++++++++++++++-----------
include/proxy/logging/LogAccess.h | 18 ++--
src/proxy/logging/Log.cc | 4 +
src/proxy/logging/LogAccess.cc | 39 +++++++++
tests/gold_tests/logging/log-mstsms.test.py | 121 +++++++++++++++++++++++++++
5 files changed, 251 insertions(+), 56 deletions(-)
diff --git a/doc/admin-guide/logging/formatting.en.rst
b/doc/admin-guide/logging/formatting.en.rst
index b85e6407ad..ba74dcb8f3 100644
--- a/doc/admin-guide/logging/formatting.en.rst
+++ b/doc/admin-guide/logging/formatting.en.rst
@@ -756,6 +756,7 @@ Timestamps and Durations
.. _crat:
.. _ms:
.. _msdms:
+.. _mstsms:
.. _stms:
.. _stmsh:
.. _stmsf:
@@ -775,54 +776,56 @@ Other fields in this category provide variously formatted
timestamps of
particular events within the current transaction (e.g. the time at which a
client request was received by |TS|).
-===== ======================= =================================================
-Field Source Description
-===== ======================= =================================================
-cqtd Client Request Client request timestamp. Specifies the date of
- the client request in the format ``YYYY-MM-DD``
- (four digit year, two digit month, two digit day
- - with leading zeros as necessary for the latter
- two).
-cqtn Client Request Client request timestamp in the Netscape
- timestamp format.
-cqtq Client Request The time at which the client request was received
- expressed as fractional (floating point) seconds
- since midnight January 1, 1970 UTC (epoch), with
- millisecond resolution.
-cqts Client Request Same as cqtq_, but as an integer without
- sub-second resolution.
-cqth Client Request Same as cqts_, but represented in hexadecimal.
-cqtt Client Request Client request timestamp in the 24-hour format
- ``hh:mm:ss`` (two digit hour, minutes, and
- seconds - with leading zeros as necessary).
-crat Origin Response Retry-After time in seconds if specified in the
- origin server response.
-ms Proxy Timestamp in milliseconds of a specific milestone
- for this request. See note below about specifying
- which milestone to use.
-msdms Proxy Difference in milliseconds between the timestamps
- of two milestones. See note below about
- specifying which milestones to use.
-stms Proxy-Origin Connection Time (in milliseconds) spent accessing the origin
- server. Measured from the time the connection
- between proxy and origin is established to the
- time it was closed.
-stmsh Proxy-Origin Connection Same as stms_, but represented in hexadecimal.
-stmsf Proxy-Origin Connection Same as stms_, but in fractional (floating point)
- seconds.
-sts Proxy-Origin Connection Same as stms_, but in integer seconds (no
- sub-second precision).
-ttms Client-Proxy Connection Time in milliseconds spent by |TS| processing the
- entire client request. Measured from the time the
- connection between the client and |TS| proxy was
- established until the last byte of the proxy
- response was delivered to the client.
-ttmsh Client-Proxy Connection Same as ttms_, but represented in hexadecimal.
-ttmsf Client-Proxy Connection Same as ttms_, but in fraction (floating point)
- seconds.
-tts Client Request Same as ttms_, but in integer seconds (no
- sub-second precision).
-===== ======================= =================================================
+====== =======================
=================================================
+Field Source Description
+====== =======================
=================================================
+cqtd Client Request Client request timestamp. Specifies the date of
+ the client request in the format ``YYYY-MM-DD``
+ (four digit year, two digit month, two digit day
+ - with leading zeros as necessary for the latter
+ two).
+cqtn Client Request Client request timestamp in the Netscape
+ timestamp format.
+cqtq Client Request The time at which the client request was
received
+ expressed as fractional (floating point) seconds
+ since midnight January 1, 1970 UTC (epoch), with
+ millisecond resolution.
+cqts Client Request Same as cqtq_, but as an integer without
+ sub-second resolution.
+cqth Client Request Same as cqts_, but represented in hexadecimal.
+cqtt Client Request Client request timestamp in the 24-hour format
+ ``hh:mm:ss`` (two digit hour, minutes, and
+ seconds - with leading zeros as necessary).
+crat Origin Response Retry-After time in seconds if specified in the
+ origin server response.
+ms Proxy Timestamp in milliseconds of a specific
milestone
+ for this request. See note below about
specifying
+ which milestone to use.
+msdms Proxy Difference in milliseconds between the
timestamps
+ of two milestones. See note below about
+ specifying which milestones to use.
+mstsms Proxy Slow log report in milliseconds as CSV.
+ See note below about what timestamps are used.
+stms Proxy-Origin Connection Time (in milliseconds) spent accessing the
origin
+ server. Measured from the time the connection
+ between proxy and origin is established to the
+ time it was closed.
+stmsh Proxy-Origin Connection Same as stms_, but represented in hexadecimal.
+stmsf Proxy-Origin Connection Same as stms_, but in fractional (floating
point)
+ seconds.
+sts Proxy-Origin Connection Same as stms_, but in integer seconds (no
+ sub-second precision).
+ttms Client-Proxy Connection Time in milliseconds spent by |TS| processing
the
+ entire client request. Measured from the time
the
+ connection between the client and |TS| proxy was
+ established until the last byte of the proxy
+ response was delivered to the client.
+ttmsh Client-Proxy Connection Same as ttms_, but represented in hexadecimal.
+ttmsf Client-Proxy Connection Same as ttms_, but in fraction (floating point)
+ seconds.
+tts Client Request Same as ttms_, but in integer seconds (no
+ sub-second precision).
+====== =======================
=================================================
.. note::
@@ -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:
+
+ 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
+
.. _admin-logging-fields-urls:
URLs, Schemes, and Paths
diff --git a/include/proxy/logging/LogAccess.h
b/include/proxy/logging/LogAccess.h
index 7cdf8ed48b..cfe3bf557f 100644
--- a/include/proxy/logging/LogAccess.h
+++ b/include/proxy/logging/LogAccess.h
@@ -296,14 +296,16 @@ public:
//
// milestones access
//
- int marshal_milestone(TSMilestonesType ms, char *buf);
- int marshal_milestone_fmt_sec(TSMilestonesType ms, char *buf);
- int marshal_milestone_fmt_squid(TSMilestonesType ms, char *buf);
- int marshal_milestone_fmt_netscape(TSMilestonesType ms, char *buf);
- int marshal_milestone_fmt_date(TSMilestonesType ms, char *buf);
- int marshal_milestone_fmt_time(TSMilestonesType ms, char *buf);
- int marshal_milestone_fmt_ms(TSMilestonesType ms, char *buf);
- int marshal_milestone_diff(TSMilestonesType ms1, TSMilestonesType ms2, char
*buf);
+ int marshal_milestone(TSMilestonesType ms, char *buf);
+ int marshal_milestone_fmt_sec(TSMilestonesType ms, char *buf);
+ int marshal_milestone_fmt_squid(TSMilestonesType ms, char *buf);
+ int marshal_milestone_fmt_netscape(TSMilestonesType ms, char *buf);
+ int marshal_milestone_fmt_date(TSMilestonesType ms, char *buf);
+ int marshal_milestone_fmt_time(TSMilestonesType ms, char *buf);
+ int marshal_milestone_fmt_ms(TSMilestonesType ms, char *buf);
+ int marshal_milestone_diff(TSMilestonesType ms1, TSMilestonesType ms2, char
*buf);
+ int marshal_milestones_csv(char *buf);
+
void set_http_header_field(LogField::Container container, char *field, char
*buf, int len);
//
// unmarshalling routines
diff --git a/src/proxy/logging/Log.cc b/src/proxy/logging/Log.cc
index 4ac59bf679..3bca7c5333 100644
--- a/src/proxy/logging/Log.cc
+++ b/src/proxy/logging/Log.cc
@@ -858,6 +858,10 @@ Log::init_fields()
global_field_list.add(field, false);
field_symbol_hash.emplace("sshv", field);
+ field = new LogField("milestones_csv", "mstsms", LogField::STRING,
&LogAccess::marshal_milestones_csv, &LogAccess::unmarshal_str);
+ global_field_list.add(field, false);
+ field_symbol_hash.emplace("mstsms", field);
+
field = new LogField("server_resp_time", "stms", LogField::sINT,
&LogAccess::marshal_server_resp_time_ms,
&LogAccess::unmarshal_int_to_str);
global_field_list.add(field, false);
diff --git a/src/proxy/logging/LogAccess.cc b/src/proxy/logging/LogAccess.cc
index 908ec8c22f..d04082fdce 100644
--- a/src/proxy/logging/LogAccess.cc
+++ b/src/proxy/logging/LogAccess.cc
@@ -30,6 +30,7 @@
#include "iocore/utils/Machine.h"
#include "proxy/logging/LogFormat.h"
#include "proxy/logging/LogBuffer.h"
+#include "swoc/BufferWriter.h"
#include "tscore/Encoding.h"
#include "../private/SSLProxySession.h"
#include "tscore/ink_inet.h"
@@ -3494,6 +3495,44 @@ 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();
+ int const len = LogAccess::padded_strlen(view.data());
+
+ if (nullptr != buf) {
+ marshal_str(buf, view.data(), len);
+ }
+ return len;
+}
+
void
LogAccess::set_http_header_field(LogField::Container container, char *field,
char *buf, int len)
{
diff --git a/tests/gold_tests/logging/log-mstsms.test.py
b/tests/gold_tests/logging/log-mstsms.test.py
new file mode 100644
index 0000000000..a8831e02e7
--- /dev/null
+++ b/tests/gold_tests/logging/log-mstsms.test.py
@@ -0,0 +1,121 @@
+'''
+'''
+# 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
+
+
+# check comma count and ensure last character is a digit
+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, "", ""
+
+
+logpath = os.path.join(ts.Variables.LOGDIR, 'field-mstsms.log')
+
+# Wait for log file to appear, then wait one extra second to make sure TS is
done writing it.
+tr = Test.AddTestRun()
+tr.Processes.Default.Command = (os.path.join(Test.Variables.AtsTestToolsDir,
'condwait') + ' 60 1 -f ' + logpath)
+#tr.Processes.Default.ReturnCode = 0
+tr.Streams.All.Content = Testers.Lambda(lambda info, tester:
check_lines(logpath))