Copilot commented on code in PR #13231:
URL: https://github.com/apache/trafficserver/pull/13231#discussion_r3379224175


##########
src/proxy/logging/Log.cc:
##########
@@ -740,58 +758,59 @@ Log::init_fields()
                            SQUID_MISS_ERROR, "MISS_ERROR", 
SQUID_MISS_CACHE_BYPASS, "MISS_CACHE_BYPASS",
                            SQUID_HIT_MISS_INVALID_ASSIGNED_CODE, 
"INVALID_CODE");
 
-  field = new LogField("cache_result_code", "crc", LogField::sINT, 
&LogAccess::marshal_cache_result_code,
+  field = new LogField("cache_result_code", "crc", LogField::Type::sINT, 
&LogAccess::marshal_cache_result_code,
                        &LogAccess::unmarshal_cache_code, 
make_alias_map(cache_code_map));
   global_field_list.add(field, false);
   field_symbol_hash.emplace("crc", field);
 
   // Reuse the unmarshalling code from crc
-  field = new LogField("cache_result_subcode", "crsc", LogField::sINT, 
&LogAccess::marshal_cache_result_subcode,
+  field = new LogField("cache_result_subcode", "crsc", LogField::Type::sINT, 
&LogAccess::marshal_cache_result_subcode,
                        &LogAccess::unmarshal_cache_code, 
make_alias_map(cache_subcode_map));
   global_field_list.add(field, false);
   field_symbol_hash.emplace("crsc", field);
 
-  field = new LogField("cache_hit_miss", "chm", LogField::sINT, 
&LogAccess::marshal_cache_hit_miss,
+  field = new LogField("cache_hit_miss", "chm", LogField::Type::sINT, 
&LogAccess::marshal_cache_hit_miss,
                        &LogAccess::unmarshal_cache_hit_miss, 
make_alias_map(cache_hit_miss_map));
   global_field_list.add(field, false);
   field_symbol_hash.emplace("chm", field);
 
-  field = new LogField("proxy_response_all_header_fields", "psah", 
LogField::STRING,
+  field = new LogField("proxy_response_all_header_fields", "psah", 
LogField::Type::STRING,
                        &LogAccess::marshal_proxy_resp_all_header_fields, 
&LogUtils::unmarshalMimeHdr);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("psah", field);
 
   // proxy -> server fields
-  field = new LogField("proxy_req_header_len", "pqhl", LogField::sINT, 
&LogAccess::marshal_proxy_req_header_len,
+  field = new LogField("proxy_req_header_len", "pqhl", LogField::Type::sINT, 
&LogAccess::marshal_proxy_req_header_len,
                        &LogAccess::unmarshal_int_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("pqhl", field);
 
-  field = new LogField("proxy_req_squid_len", "pqql", LogField::sINT, 
&LogAccess::marshal_proxy_req_squid_len,
+  field = new LogField("proxy_req_squid_len", "pqql", LogField::Type::sINT, 
&LogAccess::marshal_proxy_req_squid_len,
                        &LogAccess::unmarshal_int_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("pqql", field);
 
-  field = new LogField("proxy_req_content_len", "pqcl", LogField::sINT, 
&LogAccess::marshal_proxy_req_content_len,
+  field = new LogField("proxy_req_content_len", "pqcl", LogField::Type::sINT, 
&LogAccess::marshal_proxy_req_content_len,
                        &LogAccess::unmarshal_int_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("pqcl", field);
 
-  field = new LogField("proxy_req_server_ip", "pqsi", LogField::IP, 
&LogAccess::marshal_proxy_req_server_ip,
+  field = new LogField("proxy_req_server_ip", "pqsi", LogField::Type::IP, 
&LogAccess::marshal_proxy_req_server_ip,
                        &LogAccess::unmarshal_ip_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("pqsi", field);
 
-  field = new LogField("proxy_req_server_port", "pqsp", LogField::sINT, 
&LogAccess::marshal_proxy_req_server_port,
+  field = new LogField("proxy_req_server_port", "pqsp", LogField::Type::sINT, 
&LogAccess::marshal_proxy_req_server_port,
                        &LogAccess::unmarshal_int_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("pqsp", field);
 
-  field = new LogField("next_hop_ip", "nhi", LogField::IP, 
&LogAccess::marshal_next_hop_ip, &LogAccess::unmarshal_ip_to_str);
+  field = new LogField("next_hop_ip", "nhi", LogField::Type::IP, 
&LogAccess::marshal_next_hop_ip, &LogAccess::unmarshal_ip_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("nhi", field);
 
-  field = new LogField("next_hop_port", "nhp", LogField::IP, 
&LogAccess::marshal_next_hop_port, &LogAccess::unmarshal_int_to_str);
+  field =
+    new LogField("next_hop_port", "nhp", LogField::Type::IP, 
&LogAccess::marshal_next_hop_port, &LogAccess::unmarshal_int_to_str);

Review Comment:
   "nhp" (next_hop_port) marshals an integer port 
(LogAccess::marshal_next_hop_port returns INK_MIN_ALIGN after marshal_int), so 
its LogField type must be sINT. Declaring it as IP will cause v3 schemas and 
type-driven consumers to mis-decode the field.



##########
src/proxy/logging/Log.cc:
##########
@@ -328,371 +328,389 @@ Log::init_fields()
   LogField::init_milestone_container();
 
   // client -> proxy fields
-  field = new LogField("client_host_ip", "chi", LogField::IP, 
&LogAccess::marshal_client_host_ip, &LogAccess::unmarshal_ip_to_str);
+  field =
+    new LogField("client_host_ip", "chi", LogField::Type::IP, 
&LogAccess::marshal_client_host_ip, &LogAccess::unmarshal_ip_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("chi", field);
 
-  field =
-    new LogField("client_host_port", "chp", LogField::sINT, 
&LogAccess::marshal_client_host_port, &LogAccess::unmarshal_int_to_str);
+  field = new LogField("client_host_port", "chp", LogField::Type::sINT, 
&LogAccess::marshal_client_host_port,
+                       &LogAccess::unmarshal_int_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("chp", field);
 
-  field =
-    new LogField("client_host_ip_hex", "chih", LogField::IP, 
&LogAccess::marshal_client_host_ip, &LogAccess::unmarshal_ip_to_hex);
+  field = new LogField("client_host_ip_hex", "chih", LogField::Type::IP, 
&LogAccess::marshal_client_host_ip,
+                       &LogAccess::unmarshal_ip_to_hex);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("chih", field);
 
-  field = new LogField("client_host_ip_verified", "chiv", LogField::IP, 
&LogAccess::marshal_client_host_ip_verified,
+  field = new LogField("client_host_ip_verified", "chiv", LogField::Type::IP, 
&LogAccess::marshal_client_host_ip_verified,
                        &LogAccess::unmarshal_ip_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("chiv", field);
 
   // remote client (Not necessarily the requesting client IP - See proxy 
protocol)
-  field = new LogField("remote_host_ip", "rchi", LogField::IP, 
&LogAccess::marshal_remote_host_ip, &LogAccess::unmarshal_ip_to_str);
+  field =
+    new LogField("remote_host_ip", "rchi", LogField::Type::IP, 
&LogAccess::marshal_remote_host_ip, &LogAccess::unmarshal_ip_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("rchi", field);
 
-  field = new LogField("remote_host_port", "rchp", LogField::sINT, 
&LogAccess::marshal_remote_host_port,
+  field = new LogField("remote_host_port", "rchp", LogField::Type::sINT, 
&LogAccess::marshal_remote_host_port,
                        &LogAccess::unmarshal_int_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("rchp", field);
 
-  field =
-    new LogField("remote_host_ip_hex", "rchh", LogField::IP, 
&LogAccess::marshal_remote_host_ip, &LogAccess::unmarshal_ip_to_hex);
+  field = new LogField("remote_host_ip_hex", "rchh", LogField::Type::IP, 
&LogAccess::marshal_remote_host_ip,
+                       &LogAccess::unmarshal_ip_to_hex);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("rchh", field);
 
   // interface ip
 
-  field =
-    new LogField("host_interface_ip", "hii", LogField::IP, 
&LogAccess::marshal_host_interface_ip, &LogAccess::unmarshal_ip_to_str);
+  field = new LogField("host_interface_ip", "hii", LogField::Type::IP, 
&LogAccess::marshal_host_interface_ip,
+                       &LogAccess::unmarshal_ip_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("hii", field);
 
-  field = new LogField("host_interface_ip_hex", "hiih", LogField::IP, 
&LogAccess::marshal_host_interface_ip,
+  field = new LogField("host_interface_ip_hex", "hiih", LogField::Type::IP, 
&LogAccess::marshal_host_interface_ip,
                        &LogAccess::unmarshal_ip_to_hex);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("hiih", field);
   // interface ip end
-  field = new LogField("client_auth_user_name", "caun", LogField::STRING, 
&LogAccess::marshal_client_auth_user_name,
+  field = new LogField("client_auth_user_name", "caun", 
LogField::Type::STRING, &LogAccess::marshal_client_auth_user_name,
                        &LogAccess::unmarshal_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("caun", field);
 
-  field = new LogField("plugin_identity_id", "piid", LogField::sINT, 
&LogAccess::marshal_plugin_identity_id,
+  field = new LogField("plugin_identity_id", "piid", LogField::Type::sINT, 
&LogAccess::marshal_plugin_identity_id,
                        &LogAccess::unmarshal_int_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("piid", field);
 
-  field = new LogField("plugin_identity_tag", "pitag", LogField::STRING, 
&LogAccess::marshal_plugin_identity_tag,
+  field = new LogField("plugin_identity_tag", "pitag", LogField::Type::STRING, 
&LogAccess::marshal_plugin_identity_tag,
                        &LogAccess::unmarshal_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("pitag", field);
 
-  field = new LogField("client_req_timestamp_sec", "cqts", LogField::sINT, 
&LogAccess::marshal_client_req_timestamp_sec,
+  field = new LogField("client_req_timestamp_sec", "cqts", 
LogField::Type::sINT, &LogAccess::marshal_client_req_timestamp_sec,
                        &LogAccess::unmarshal_int_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cqts", field);
 
-  field = new LogField("client_req_timestamp_hex_sec", "cqth", LogField::sINT, 
&LogAccess::marshal_client_req_timestamp_sec,
+  field = new LogField("client_req_timestamp_hex_sec", "cqth", 
LogField::Type::sINT, &LogAccess::marshal_client_req_timestamp_sec,
                        &LogAccess::unmarshal_int_to_str_hex);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cqth", field);
 
-  field = new LogField("client_req_timestamp_squid", "cqtq", LogField::sINT, 
&LogAccess::marshal_client_req_timestamp_ms,
+  field = new LogField("client_req_timestamp_squid", "cqtq", 
LogField::Type::sINT, &LogAccess::marshal_client_req_timestamp_ms,
                        &LogAccess::unmarshal_ttmsf);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cqtq", field);
 
-  field = new LogField("client_req_timestamp_netscape", "cqtn", 
LogField::sINT, &LogAccess::marshal_client_req_timestamp_sec,
+  field = new LogField("client_req_timestamp_netscape", "cqtn", 
LogField::Type::sINT, &LogAccess::marshal_client_req_timestamp_sec,
                        &LogAccess::unmarshal_int_to_netscape_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cqtn", field);
 
-  field = new LogField("client_req_timestamp_date", "cqtd", LogField::sINT, 
&LogAccess::marshal_client_req_timestamp_sec,
+  field = new LogField("client_req_timestamp_date", "cqtd", 
LogField::Type::sINT, &LogAccess::marshal_client_req_timestamp_sec,
                        &LogAccess::unmarshal_int_to_date_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cqtd", field);
 
-  field = new LogField("client_req_timestamp_time", "cqtt", LogField::sINT, 
&LogAccess::marshal_client_req_timestamp_sec,
+  field = new LogField("client_req_timestamp_time", "cqtt", 
LogField::Type::sINT, &LogAccess::marshal_client_req_timestamp_sec,
                        &LogAccess::unmarshal_int_to_time_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cqtt", field);
 
-  field = new LogField("client_req_http_method", "cqhm", LogField::STRING, 
&LogAccess::marshal_client_req_http_method,
+  field = new LogField("client_req_http_method", "cqhm", 
LogField::Type::STRING, &LogAccess::marshal_client_req_http_method,
                        &LogAccess::unmarshal_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cqhm", field);
 
-  field = new LogField("client_req_url", "cqu", LogField::STRING, 
&LogAccess::marshal_client_req_url, &LogAccess::unmarshal_str,
-                       &LogAccess::set_client_req_url);
+  field = new LogField("client_req_url", "cqu", LogField::Type::STRING, 
&LogAccess::marshal_client_req_url,
+                       &LogAccess::unmarshal_str, 
&LogAccess::set_client_req_url);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cqu", field);
 
-  field = new LogField("client_req_url", "pqu", LogField::STRING, 
&LogAccess::marshal_client_req_url, &LogAccess::unmarshal_str,
-                       &LogAccess::set_client_req_url);
+  field = new LogField("client_req_url", "pqu", LogField::Type::STRING, 
&LogAccess::marshal_client_req_url,
+                       &LogAccess::unmarshal_str, 
&LogAccess::set_client_req_url);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("pqu", field);
 
-  field = new LogField("client_req_url_canonical", "cquc", LogField::STRING, 
&LogAccess::marshal_client_req_url_canon,
+  field = new LogField("client_req_url_canonical", "cquc", 
LogField::Type::STRING, &LogAccess::marshal_client_req_url_canon,
                        &LogAccess::unmarshal_str, 
&LogAccess::set_client_req_url_canon);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cquc", field);
 
-  field = new LogField("client_req_url_canonical", "pquc", LogField::STRING, 
&LogAccess::marshal_client_req_url_canon,
+  field = new LogField("client_req_url_canonical", "pquc", 
LogField::Type::STRING, &LogAccess::marshal_client_req_url_canon,
                        &LogAccess::unmarshal_str, 
&LogAccess::set_client_req_url_canon);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("pquc", field);
 
-  field =
-    new LogField("client_req_unmapped_url_canonical", "cquuc", 
LogField::STRING, &LogAccess::marshal_client_req_unmapped_url_canon,
-                 &LogAccess::unmarshal_str, 
&LogAccess::set_client_req_unmapped_url_canon);
+  field = new LogField("client_req_unmapped_url_canonical", "cquuc", 
LogField::Type::STRING,
+                       &LogAccess::marshal_client_req_unmapped_url_canon, 
&LogAccess::unmarshal_str,
+                       &LogAccess::set_client_req_unmapped_url_canon);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cquuc", field);
 
-  field = new LogField("client_req_unmapped_url_path", "cquup", 
LogField::STRING, &LogAccess::marshal_client_req_unmapped_url_path,
-                       &LogAccess::unmarshal_str, 
&LogAccess::set_client_req_unmapped_url_path);
+  field =
+    new LogField("client_req_unmapped_url_path", "cquup", 
LogField::Type::STRING, &LogAccess::marshal_client_req_unmapped_url_path,
+                 &LogAccess::unmarshal_str, 
&LogAccess::set_client_req_unmapped_url_path);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cquup", field);
 
-  field = new LogField("client_req_unmapped_url_host", "cquuh", 
LogField::STRING, &LogAccess::marshal_client_req_unmapped_url_host,
-                       &LogAccess::unmarshal_str, 
&LogAccess::set_client_req_unmapped_url_host);
+  field =
+    new LogField("client_req_unmapped_url_host", "cquuh", 
LogField::Type::STRING, &LogAccess::marshal_client_req_unmapped_url_host,
+                 &LogAccess::unmarshal_str, 
&LogAccess::set_client_req_unmapped_url_host);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cquuh", field);
 
-  field = new LogField("client_req_url_scheme", "cqus", LogField::STRING, 
&LogAccess::marshal_client_req_url_scheme,
+  field = new LogField("client_req_url_scheme", "cqus", 
LogField::Type::STRING, &LogAccess::marshal_client_req_url_scheme,
                        &LogAccess::unmarshal_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cqus", field);
 
-  field = new LogField("client_req_url_scheme", "pqus", LogField::STRING, 
&LogAccess::marshal_client_req_url_scheme,
+  field = new LogField("client_req_url_scheme", "pqus", 
LogField::Type::STRING, &LogAccess::marshal_client_req_url_scheme,
                        &LogAccess::unmarshal_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("pqus", field);
 
-  field = new LogField("client_req_url_path", "cqup", LogField::STRING, 
&LogAccess::marshal_client_req_url_path,
+  field = new LogField("client_req_url_path", "cqup", LogField::Type::STRING, 
&LogAccess::marshal_client_req_url_path,
                        &LogAccess::unmarshal_str, 
&LogAccess::set_client_req_url_path);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cqup", field);
 
-  field = new LogField("client_req_url_path", "pqup", LogField::STRING, 
&LogAccess::marshal_client_req_url_path,
+  field = new LogField("client_req_url_path", "pqup", LogField::Type::STRING, 
&LogAccess::marshal_client_req_url_path,
                        &LogAccess::unmarshal_str, 
&LogAccess::set_client_req_url_path);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("pqup", field);
 
-  field = new LogField("client_req_protocol_version", "cqpv", 
LogField::STRING, &LogAccess::marshal_client_req_protocol_version,
-                       &LogAccess::unmarshal_str);
+  field = new LogField("client_req_protocol_version", "cqpv", 
LogField::Type::STRING,
+                       &LogAccess::marshal_client_req_protocol_version, 
&LogAccess::unmarshal_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cqpv", field);
 
-  field = new LogField("server_req_protocol_version", "sqpv", 
LogField::STRING, &LogAccess::marshal_server_req_protocol_version,
-                       &LogAccess::unmarshal_str);
+  field = new LogField("server_req_protocol_version", "sqpv", 
LogField::Type::STRING,
+                       &LogAccess::marshal_server_req_protocol_version, 
&LogAccess::unmarshal_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("sqpv", field);
 
-  field = new LogField("client_req_header_len", "cqhl", LogField::sINT, 
&LogAccess::marshal_client_req_header_len,
+  field = new LogField("client_req_header_len", "cqhl", LogField::Type::sINT, 
&LogAccess::marshal_client_req_header_len,
                        &LogAccess::unmarshal_int_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cqhl", field);
 
-  field = new LogField("client_req_squid_len", "cqql", LogField::sINT, 
&LogAccess::marshal_client_req_squid_len,
+  field = new LogField("client_req_squid_len", "cqql", LogField::Type::sINT, 
&LogAccess::marshal_client_req_squid_len,
                        &LogAccess::unmarshal_int_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cqql", field);
 
   // Client request squid length plus TLS handshake bytes received for TLS 
connections
-  field = new LogField("client_req_squid_len_tls", "cqqtl", LogField::sINT, 
&LogAccess::marshal_client_req_squid_len_tls,
+  field = new LogField("client_req_squid_len_tls", "cqqtl", 
LogField::Type::sINT, &LogAccess::marshal_client_req_squid_len_tls,
                        &LogAccess::unmarshal_int_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cqqtl", field);

Review Comment:
   Log field "cqqtl" is registered twice (duplicate LogField allocation, 
duplicate global_field_list entry, and duplicate field_symbol_hash key). The 
second registration leaks memory and can lead to inconsistent field 
iteration/lookup behavior.



##########
src/proxy/logging/Log.cc:
##########
@@ -977,118 +1000,119 @@ Log::init_fields()
   Ptr<LogFieldAliasTable> cache_write_code_map = make_ptr(new 
LogFieldAliasTable);
   cache_write_code_map->init(N_LOG_CACHE_WRITE_TYPES, LOG_CACHE_WRITE_NONE, 
"-", LOG_CACHE_WRITE_LOCK_MISSED, "WL_MISS",
                              LOG_CACHE_WRITE_LOCK_ABORTED, "INTR", 
LOG_CACHE_WRITE_ERROR, "ERR", LOG_CACHE_WRITE_COMPLETE, "FIN");
-  field = new LogField("cache_write_result", "cwr", LogField::sINT, 
&LogAccess::marshal_cache_write_code,
+  field = new LogField("cache_write_result", "cwr", LogField::Type::sINT, 
&LogAccess::marshal_cache_write_code,
                        &LogAccess::unmarshal_cache_write_code, 
make_alias_map(cache_write_code_map));
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cwr", field);
 
-  field = new LogField("cache_write_transform_result", "cwtr", LogField::sINT, 
&LogAccess::marshal_cache_write_transform_code,
+  field = new LogField("cache_write_transform_result", "cwtr", 
LogField::Type::sINT, &LogAccess::marshal_cache_write_transform_code,
                        &LogAccess::unmarshal_cache_write_code, 
make_alias_map(cache_write_code_map));
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cwtr", field);
 
   // other fields
 
-  field = new LogField("transfer_time_ms", "ttms", LogField::sINT, 
&LogAccess::marshal_transfer_time_ms,
+  field = new LogField("transfer_time_ms", "ttms", LogField::Type::sINT, 
&LogAccess::marshal_transfer_time_ms,
                        &LogAccess::unmarshal_int_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("ttms", field);
 
-  field = new LogField("transfer_time_ms_hex", "ttmsh", LogField::sINT, 
&LogAccess::marshal_transfer_time_ms,
+  field = new LogField("transfer_time_ms_hex", "ttmsh", LogField::Type::sINT, 
&LogAccess::marshal_transfer_time_ms,
                        &LogAccess::unmarshal_int_to_str_hex);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("ttmsh", field);
 
-  field = new LogField("transfer_time_ms_fractional", "ttmsf", LogField::sINT, 
&LogAccess::marshal_transfer_time_ms,
+  field = new LogField("transfer_time_ms_fractional", "ttmsf", 
LogField::Type::sINT, &LogAccess::marshal_transfer_time_ms,
                        &LogAccess::unmarshal_ttmsf);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("ttmsf", field);
 
-  field =
-    new LogField("transfer_time_sec", "tts", LogField::sINT, 
&LogAccess::marshal_transfer_time_s, &LogAccess::unmarshal_int_to_str);
+  field = new LogField("transfer_time_sec", "tts", LogField::Type::sINT, 
&LogAccess::marshal_transfer_time_s,
+                       &LogAccess::unmarshal_int_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("tts", field);
 
-  field = new LogField("file_size", "fsiz", LogField::sINT, 
&LogAccess::marshal_file_size, &LogAccess::unmarshal_int_to_str);
+  field = new LogField("file_size", "fsiz", LogField::Type::sINT, 
&LogAccess::marshal_file_size, &LogAccess::unmarshal_int_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("fsiz", field);
 
-  field = new LogField("client_connection_id", "ccid", LogField::sINT, 
&LogAccess::marshal_client_http_connection_id,
+  field = new LogField("client_connection_id", "ccid", LogField::Type::sINT, 
&LogAccess::marshal_client_http_connection_id,
                        &LogAccess::unmarshal_int_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("ccid", field);
 
-  field = new LogField("client_transaction_id", "ctid", LogField::sINT, 
&LogAccess::marshal_client_http_transaction_id,
+  field = new LogField("client_transaction_id", "ctid", LogField::Type::sINT, 
&LogAccess::marshal_client_http_transaction_id,
                        &LogAccess::unmarshal_int_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("ctid", field);
 
-  field = new LogField("cache_read_retry_attempts", "crra", LogField::sINT, 
&LogAccess::marshal_cache_read_retries,
+  field = new LogField("cache_read_retry_attempts", "crra", 
LogField::Type::sINT, &LogAccess::marshal_cache_read_retries,
                        &LogAccess::unmarshal_int_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("crra", field);
 
-  field = new LogField("cache_write_retry_attempts", "cwra", LogField::sINT, 
&LogAccess::marshal_cache_write_retries,
+  field = new LogField("cache_write_retry_attempts", "cwra", 
LogField::Type::sINT, &LogAccess::marshal_cache_write_retries,
                        &LogAccess::unmarshal_int_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cwra", field);
 
-  field = new LogField("cache_collapsed_connection_success", "cccs", 
LogField::sINT,
+  field = new LogField("cache_collapsed_connection_success", "cccs", 
LogField::Type::sINT,
                        &LogAccess::marshal_cache_collapsed_connection_success, 
&LogAccess::unmarshal_int_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cccs", field);
 
-  field = new LogField("client_transaction_priority_weight", "ctpw", 
LogField::sINT,
+  field = new LogField("client_transaction_priority_weight", "ctpw", 
LogField::Type::sINT,
                        
&LogAccess::marshal_client_http_transaction_priority_weight, 
&LogAccess::unmarshal_int_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("ctpw", field);
 
-  field = new LogField("client_transaction_priority_dependence", "ctpd", 
LogField::sINT,
+  field = new LogField("client_transaction_priority_dependence", "ctpd", 
LogField::Type::sINT,
                        
&LogAccess::marshal_client_http_transaction_priority_dependence, 
&LogAccess::unmarshal_int_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("ctpd", field);
 
-  field = new LogField("proxy_protocol_version", "ppv", LogField::STRING, 
&LogAccess::marshal_proxy_protocol_version,
+  field = new LogField("proxy_protocol_version", "ppv", 
LogField::Type::STRING, &LogAccess::marshal_proxy_protocol_version,
                        &LogAccess::unmarshal_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("ppv", field);
 
-  field = new LogField("proxy_protocol_src_ip", "pps", LogField::IP, 
&LogAccess::marshal_proxy_protocol_src_ip,
+  field = new LogField("proxy_protocol_src_ip", "pps", LogField::Type::IP, 
&LogAccess::marshal_proxy_protocol_src_ip,
                        &LogAccess::unmarshal_ip_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("ppsip", field);

Review Comment:
   field_symbol_hash key should match the field symbol ("pps"), otherwise log 
formats using %<pps> won't resolve this field. The current key "ppsip" makes 
the field effectively unreachable by its documented symbol.



##########
doc/appendices/command-line/traffic_logcat.en.rst:
##########
@@ -25,14 +25,17 @@ traffic_logcat
 Synopsis
 ========
 
-:program:`traffic_logcat` [-o output-file | -a] [-CEhSVw2] [input-file ...]
+:program:`traffic_logcat` [-o output-file | -a] [-CEHhSVw2] [input-file ...]

Review Comment:
   Synopsis flag list omits -j/--json even though it's documented and 
implemented below. This makes the top-level usage string inaccurate.



##########
doc/developer-guide/logging-architecture/binary-log-v3-format.en.rst:
##########
@@ -0,0 +1,216 @@
+.. 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.
+
+.. include:: ../../common.defs
+
+.. _binary-log-v3-format:
+
+Self-Describing Binary Log Format (v3)
+**************************************
+
+This page specifies the on-disk format of a binary log segment, version 3, in
+enough detail to implement a decoder *without* the Traffic Server source tree.
+A version 3 segment is **self-describing**: every field's type is published in
+the segment header, so a generic reader can decode each entry by dispatching on
+a small, stable set of type codes — no embedded copy of the ATS symbol-to-type
+table is required.
+
+Motivation
+==========
+
+In version 2, a segment header carries the field *symbols* (``fmt_fieldlist``,
+e.g. ``"chi cqu pssc"``) and a printf-style *template* (``fmt_printf``) but
+**not** the field types. To decode an entry a reader had to already know the
+type of each symbol, because the value encodings are only self-delimiting once
+the type is known (``IP`` is variable length, for example). That coupled every
+out-of-tree parser to the exact ATS build that wrote the log.
+
+Version 3 adds one thing: a per-segment **field-type schema** that lists the
+wire type of every field, in field order. Decoding then needs only the symbols
+(as keys) and the schema (for types).
+
+Segment layout
+==============
+
+A ``.blog`` file is a stream of segments, each a serialized ``LogBuffer``:
+
+::
+
+    LogBufferHeader            (per segment)
+      cookie       = 0xaceface
+      version      = 3
+      format_type, byte_count, entry_count, timestamps, flags, signature
+      fmt_name_offset
+      fmt_fieldlist_offset      -> "chi cqu pssc ..."   (symbols, space 
separated)
+      fmt_printf_offset         -> "%<chi> %<cqu> ..."
+      src_hostname_offset, log_filename_offset
+      data_offset               -> first entry
+      fmt_fieldtypes_offset     -> field-type schema     (NEW in v3)
+    [ LogEntryHeader | field0 field1 field2 ... ]   x entry_count
+      LogEntryHeader: timestamp(8) timestamp_usec(4) entry_len(4)
+      fields: concatenated in fieldlist order, no per-field tags
+
+All ``*_offset`` members are byte offsets from the start of the segment (the
+address of the ``LogBufferHeader``). ``fmt_fieldtypes_offset`` is appended
+**after** ``data_offset`` so that the layout through ``data_offset`` is
+byte-identical to version 2; a value of ``0`` means the schema is absent (e.g.
+a text-format segment, or a version 2 segment).
+
+Field-type schema
+=================
+
+At ``fmt_fieldtypes_offset`` the segment stores:
+
+::
+
+    uint16_t field_count;             // == number of symbols in fmt_fieldlist
+    uint8_t  type_code[field_count];  // one type code per field, in order
+
+``type_code[i]`` is the type of the i-th field, which corresponds to the i-th
+symbol in ``fmt_fieldlist`` and the i-th value in each entry. The ``uint16_t``
+``field_count`` prefix is written in **host byte order**, like the rest of
+``LogBufferHeader``. The blob is padded along with the header to an 8-byte
+boundary.
+
+The schema carries no independent version of its own: the segment ``version``
+(``3`` here) governs this layout, so a future schema change rides the same
+``LOG_SEGMENT_VERSION`` bump rather than a second, separate counter.
+
+Stable type codes
+=================
+
+The type codes are the values of the in-tree ``LogField::Type`` enumeration,
+serialized directly. They are part of the published format and are
+**append-only**: codes are never renumbered or reused.
+
+==== ========= ===========================================================
+Code Name      Wire encoding
+==== ========= ===========================================================
+0    INVALID   Reserved. Not emitted by a correct writer; a reader that
+               meets it -- or any code it does not recognize -- cannot
+               determine the field length and must stop decoding the entry.
+1    sINT      A single ``int64_t``, fixed 8 bytes, **host byte order**.
+2    dINT      Two ``int64_t`` (16 bytes), host byte order. Used for
+               values stored as two integers, e.g. HTTP version
+               major/minor.
+3    STRING    NUL-terminated bytes, then padded to an 8-byte boundary.
+4    IP        ``uint16_t`` address family followed by a family-sized
+               address, then padded to an 8-byte boundary (see below).
+==== ========= ===========================================================
+
+The code reflects how the value is *framed* on disk, i.e. how a reader walks
+(or skips) it -- not what the value means. (The ``sINT``/``dINT`` names are an
+ATS-internal distinction; on the wire ``sINT`` is one 8-byte integer and
+``dINT`` is two consecutive ones.) How a consumer *renders* a value -- mapping
+a cache-result integer to ``TCP_HIT``, or a ``dINT`` to ``1.1`` -- is layered
+on top by the consumer and is not part of the wire format.
+
+Value encodings
+===============
+
+sINT
+    An ``int64_t`` occupying exactly 8 bytes, in **host byte order** (as in
+    version 2). Integer values are not endianness-normalized, so a ``.blog`` is
+    not portable across hosts of differing endianness; cross-architecture
+    portability is future work.
+
+dINT
+    Two consecutive ``sINT`` values: 16 bytes total, in host byte order. Used
+    where one log field is stored as two integers, such as an HTTP version
+    (major then minor). The reference decoder renders it as a JSON array, e.g.
+    ``[1,1]``; turning that into ``1.1`` is a consumer concern.
+
+STRING
+    The string bytes followed by a single NUL, then zero padding up to the next
+    8-byte boundary. The on-wire length is therefore
+    ``align_up(strlen + 1, 8)``. An empty/absent string is written as ``"-"``.
+
+IP
+    A ``uint16_t`` address family in host byte order, then:
+
+    .. list-table::
+       :header-rows: 1
+       :widths: 30 70
+
+       * - Family
+         - Following bytes
+       * - ``AF_INET`` (IPv4)
+         - 4-byte ``in_addr``
+       * - ``AF_INET6`` (IPv6)
+         - 16-byte ``in6_addr``
+       * - ``AF_UNIX``
+         - fixed-size path buffer
+       * - ``AF_UNSPEC`` / other
+         - no address bytes
+
+    The whole field is padded to the next 8-byte boundary. Because the length
+    depends on the family byte *inside* the value, only a reader that knows the
+    field is an ``IP`` (from the schema) can compute its size — which is 
exactly
+    why the schema is required to skip or decode unknown fields safely.
+
+Decoding an entry
+=================
+
+Given a segment, a generic decoder:
+
+#. Reads ``field_count`` and the ``type_code[]`` array from the schema at
+   ``fmt_fieldtypes_offset``.
+#. Splits ``fmt_fieldlist`` into ``field_count`` whitespace-separated symbols.
+#. For each entry (located via ``data_offset`` and walked using

Review Comment:
   fmt_fieldlist is parsed as a comma-separated symbol list (commas optionally 
followed by spaces). The decode algorithm should reflect that delimiter rather 
than "whitespace-separated".



##########
src/proxy/logging/Log.cc:
##########
@@ -977,118 +1000,119 @@ Log::init_fields()
   Ptr<LogFieldAliasTable> cache_write_code_map = make_ptr(new 
LogFieldAliasTable);
   cache_write_code_map->init(N_LOG_CACHE_WRITE_TYPES, LOG_CACHE_WRITE_NONE, 
"-", LOG_CACHE_WRITE_LOCK_MISSED, "WL_MISS",
                              LOG_CACHE_WRITE_LOCK_ABORTED, "INTR", 
LOG_CACHE_WRITE_ERROR, "ERR", LOG_CACHE_WRITE_COMPLETE, "FIN");
-  field = new LogField("cache_write_result", "cwr", LogField::sINT, 
&LogAccess::marshal_cache_write_code,
+  field = new LogField("cache_write_result", "cwr", LogField::Type::sINT, 
&LogAccess::marshal_cache_write_code,
                        &LogAccess::unmarshal_cache_write_code, 
make_alias_map(cache_write_code_map));
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cwr", field);
 
-  field = new LogField("cache_write_transform_result", "cwtr", LogField::sINT, 
&LogAccess::marshal_cache_write_transform_code,
+  field = new LogField("cache_write_transform_result", "cwtr", 
LogField::Type::sINT, &LogAccess::marshal_cache_write_transform_code,
                        &LogAccess::unmarshal_cache_write_code, 
make_alias_map(cache_write_code_map));
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cwtr", field);
 
   // other fields
 
-  field = new LogField("transfer_time_ms", "ttms", LogField::sINT, 
&LogAccess::marshal_transfer_time_ms,
+  field = new LogField("transfer_time_ms", "ttms", LogField::Type::sINT, 
&LogAccess::marshal_transfer_time_ms,
                        &LogAccess::unmarshal_int_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("ttms", field);
 
-  field = new LogField("transfer_time_ms_hex", "ttmsh", LogField::sINT, 
&LogAccess::marshal_transfer_time_ms,
+  field = new LogField("transfer_time_ms_hex", "ttmsh", LogField::Type::sINT, 
&LogAccess::marshal_transfer_time_ms,
                        &LogAccess::unmarshal_int_to_str_hex);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("ttmsh", field);
 
-  field = new LogField("transfer_time_ms_fractional", "ttmsf", LogField::sINT, 
&LogAccess::marshal_transfer_time_ms,
+  field = new LogField("transfer_time_ms_fractional", "ttmsf", 
LogField::Type::sINT, &LogAccess::marshal_transfer_time_ms,
                        &LogAccess::unmarshal_ttmsf);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("ttmsf", field);
 
-  field =
-    new LogField("transfer_time_sec", "tts", LogField::sINT, 
&LogAccess::marshal_transfer_time_s, &LogAccess::unmarshal_int_to_str);
+  field = new LogField("transfer_time_sec", "tts", LogField::Type::sINT, 
&LogAccess::marshal_transfer_time_s,
+                       &LogAccess::unmarshal_int_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("tts", field);
 
-  field = new LogField("file_size", "fsiz", LogField::sINT, 
&LogAccess::marshal_file_size, &LogAccess::unmarshal_int_to_str);
+  field = new LogField("file_size", "fsiz", LogField::Type::sINT, 
&LogAccess::marshal_file_size, &LogAccess::unmarshal_int_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("fsiz", field);
 
-  field = new LogField("client_connection_id", "ccid", LogField::sINT, 
&LogAccess::marshal_client_http_connection_id,
+  field = new LogField("client_connection_id", "ccid", LogField::Type::sINT, 
&LogAccess::marshal_client_http_connection_id,
                        &LogAccess::unmarshal_int_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("ccid", field);
 
-  field = new LogField("client_transaction_id", "ctid", LogField::sINT, 
&LogAccess::marshal_client_http_transaction_id,
+  field = new LogField("client_transaction_id", "ctid", LogField::Type::sINT, 
&LogAccess::marshal_client_http_transaction_id,
                        &LogAccess::unmarshal_int_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("ctid", field);
 
-  field = new LogField("cache_read_retry_attempts", "crra", LogField::sINT, 
&LogAccess::marshal_cache_read_retries,
+  field = new LogField("cache_read_retry_attempts", "crra", 
LogField::Type::sINT, &LogAccess::marshal_cache_read_retries,
                        &LogAccess::unmarshal_int_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("crra", field);
 
-  field = new LogField("cache_write_retry_attempts", "cwra", LogField::sINT, 
&LogAccess::marshal_cache_write_retries,
+  field = new LogField("cache_write_retry_attempts", "cwra", 
LogField::Type::sINT, &LogAccess::marshal_cache_write_retries,
                        &LogAccess::unmarshal_int_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cwra", field);
 
-  field = new LogField("cache_collapsed_connection_success", "cccs", 
LogField::sINT,
+  field = new LogField("cache_collapsed_connection_success", "cccs", 
LogField::Type::sINT,
                        &LogAccess::marshal_cache_collapsed_connection_success, 
&LogAccess::unmarshal_int_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cccs", field);
 
-  field = new LogField("client_transaction_priority_weight", "ctpw", 
LogField::sINT,
+  field = new LogField("client_transaction_priority_weight", "ctpw", 
LogField::Type::sINT,
                        
&LogAccess::marshal_client_http_transaction_priority_weight, 
&LogAccess::unmarshal_int_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("ctpw", field);
 
-  field = new LogField("client_transaction_priority_dependence", "ctpd", 
LogField::sINT,
+  field = new LogField("client_transaction_priority_dependence", "ctpd", 
LogField::Type::sINT,
                        
&LogAccess::marshal_client_http_transaction_priority_dependence, 
&LogAccess::unmarshal_int_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("ctpd", field);
 
-  field = new LogField("proxy_protocol_version", "ppv", LogField::STRING, 
&LogAccess::marshal_proxy_protocol_version,
+  field = new LogField("proxy_protocol_version", "ppv", 
LogField::Type::STRING, &LogAccess::marshal_proxy_protocol_version,
                        &LogAccess::unmarshal_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("ppv", field);
 
-  field = new LogField("proxy_protocol_src_ip", "pps", LogField::IP, 
&LogAccess::marshal_proxy_protocol_src_ip,
+  field = new LogField("proxy_protocol_src_ip", "pps", LogField::Type::IP, 
&LogAccess::marshal_proxy_protocol_src_ip,
                        &LogAccess::unmarshal_ip_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("ppsip", field);
 
-  field = new LogField("proxy_protocol_dst_ip", "ppd", LogField::IP, 
&LogAccess::marshal_proxy_protocol_dst_ip,
+  field = new LogField("proxy_protocol_dst_ip", "ppd", LogField::Type::IP, 
&LogAccess::marshal_proxy_protocol_dst_ip,
                        &LogAccess::unmarshal_ip_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("ppdip", field);
 
-  field = new LogField("proxy_protocol_authority", "ppa", LogField::IP, 
&LogAccess::marshal_proxy_protocol_authority,
+  field = new LogField("proxy_protocol_authority", "ppa", LogField::Type::IP, 
&LogAccess::marshal_proxy_protocol_authority,

Review Comment:
   "ppa" (proxy_protocol_authority) is marshalled as a padded NUL-terminated 
byte string (marshal_proxy_protocol_authority uses marshal_mem/padded_length), 
so its LogField type must be STRING. Declaring it as IP will cause v3 schemas 
and type-driven readers to misframe subsequent fields.



##########
src/traffic_logcat/LogEntryJson.cc:
##########
@@ -0,0 +1,291 @@
+/** @file
+
+  Reference decoder for the self-describing v3 binary log format.
+
+  @section license License
+
+  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.
+ */
+
+#include "LogEntryJson.h"
+
+#include "proxy/logging/LogBuffer.h"
+#include "proxy/logging/LogAccess.h"
+#include "proxy/logging/LogField.h"
+
+// Deliberately not including the global field table (proxy/logging/Log.h):
+// decoding must depend only on the segment's schema (see LogEntryJson.h).
+
+#include "tscore/ink_inet.h"
+#include "tscore/ink_align.h"
+
+#include <cinttypes>
+#include <cstdint>
+#include <cstdio>
+#include <cstring>
+
+namespace
+{
+// fmt_fieldlist symbols are comma-separated (e.g. "chi,cqu,pssc"); tolerate
+// spaces too.
+bool
+is_field_sep(char c)
+{
+  return c == ',' || c == ' ';
+}
+} // namespace
+
+int
+log_entry_to_json(LogEntryHeader *entry, LogBufferHeader *header, char *buf, 
int buf_len)
+{
+  if (entry == nullptr || header == nullptr || buf == nullptr || buf_len <= 0) 
{
+    return -1;
+  }
+
+  // [seg_start, seg_end) is the only readable region (byte_count == segment 
size).
+  char *seg_start = reinterpret_cast<char *>(header);
+  char *seg_end   = seg_start + header->byte_count;
+
+  // v3 decode needs the field-type schema and the symbols.
+  char *schema_blob = header->fmt_fieldtypes();
+  char *symbols     = header->fmt_fieldlist();
+  if (schema_blob == nullptr || symbols == nullptr) {
+    return -1;
+  }
+  // Both must lie within the segment, including the fixed schema prefix.
+  if (schema_blob < seg_start || schema_blob + sizeof(LogFieldTypeSchema) > 
seg_end || symbols < seg_start || symbols >= seg_end) {
+    return -1;
+  }
+
+  // field_count is a uint16 that may sit at an unaligned offset (the writer
+  // places the schema right after the NUL-terminated header strings), so read
+  // it byte-wise rather than through the struct.
+  uint16_t fc16 = 0;
+  memcpy(&fc16, schema_blob, sizeof(fc16));
+  const unsigned field_count = fc16;
+  const uint8_t *codes       = reinterpret_cast<const uint8_t *>(schema_blob) 
+ sizeof(LogFieldTypeSchema);
+  if (reinterpret_cast<const char *>(codes) + field_count > seg_end) {
+    return -1;
+  }
+
+  // field_count must match the symbol count; a mismatch is a corrupt segment.
+  unsigned symbol_count = 0;
+  bool     in_token     = false;
+  for (const char *p = symbols; p < seg_end && *p != '\0'; ++p) {
+    if (is_field_sep(*p)) {
+      in_token = false;
+    } else if (!in_token) {
+      in_token = true;
+      ++symbol_count;
+    }
+  }
+  if (symbol_count != field_count) {
+    return -1;
+  }
+
+  // Clamp value reads to this entry (or the segment, if entry_len is bogus).
+  char *entry_start = reinterpret_cast<char *>(entry);
+  if (entry_start < seg_start || entry_start + sizeof(LogEntryHeader) > 
seg_end) {
+    return -1;
+  }
+  char *read_from = entry_start + sizeof(LogEntryHeader);
+  char *read_end  = entry_start + entry->entry_len;
+  if (read_end < read_from || read_end > seg_end) {
+    read_end = seg_end;
+  }
+
+  int written = 0;
+
+  // Append n bytes, keeping one byte in reserve for the trailing NUL.
+  auto put = [&](const char *src, int n) -> bool {
+    if (n < 0 || written + n >= buf_len) {
+      return false;
+    }
+    memcpy(buf + written, src, n);
+    written += n;
+    return true;
+  };
+  auto put_ch = [&](char c) -> bool { return put(&c, 1); };
+
+  if (!put_ch('{')) {
+    return -1;
+  }
+
+  const char *sym = symbols;
+  for (unsigned i = 0; i < field_count; ++i) {
+    // Next symbol token (comma-separated in fmt_fieldlist), bounded by the 
segment.
+    while (sym < seg_end && is_field_sep(*sym)) {
+      ++sym;
+    }
+    const char *sym_start = sym;
+    while (sym < seg_end && *sym != '\0' && !is_field_sep(*sym)) {
+      ++sym;
+    }
+    int sym_len = static_cast<int>(sym - sym_start);
+
+    if (i > 0 && !put_ch(',')) {
+      return -1;
+    }
+    if (!put_ch('"') || !put(sym_start, sym_len) || !put_ch('"') || 
!put_ch(':')) {
+      return -1;
+    }

Review Comment:
   JSON keys are emitted directly from fmt_fieldlist without any escaping. 
Since fmt_fieldlist is parsed from config (and may also come from an 
untrusted/corrupt .blog), a symbol containing a quote, backslash, or control 
byte will produce invalid JSON output (and can break downstream parsers). Keys 
should be JSON-escaped the same way STRING field values are.



##########
src/proxy/logging/Log.cc:
##########
@@ -977,118 +1000,119 @@ Log::init_fields()
   Ptr<LogFieldAliasTable> cache_write_code_map = make_ptr(new 
LogFieldAliasTable);
   cache_write_code_map->init(N_LOG_CACHE_WRITE_TYPES, LOG_CACHE_WRITE_NONE, 
"-", LOG_CACHE_WRITE_LOCK_MISSED, "WL_MISS",
                              LOG_CACHE_WRITE_LOCK_ABORTED, "INTR", 
LOG_CACHE_WRITE_ERROR, "ERR", LOG_CACHE_WRITE_COMPLETE, "FIN");
-  field = new LogField("cache_write_result", "cwr", LogField::sINT, 
&LogAccess::marshal_cache_write_code,
+  field = new LogField("cache_write_result", "cwr", LogField::Type::sINT, 
&LogAccess::marshal_cache_write_code,
                        &LogAccess::unmarshal_cache_write_code, 
make_alias_map(cache_write_code_map));
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cwr", field);
 
-  field = new LogField("cache_write_transform_result", "cwtr", LogField::sINT, 
&LogAccess::marshal_cache_write_transform_code,
+  field = new LogField("cache_write_transform_result", "cwtr", 
LogField::Type::sINT, &LogAccess::marshal_cache_write_transform_code,
                        &LogAccess::unmarshal_cache_write_code, 
make_alias_map(cache_write_code_map));
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cwtr", field);
 
   // other fields
 
-  field = new LogField("transfer_time_ms", "ttms", LogField::sINT, 
&LogAccess::marshal_transfer_time_ms,
+  field = new LogField("transfer_time_ms", "ttms", LogField::Type::sINT, 
&LogAccess::marshal_transfer_time_ms,
                        &LogAccess::unmarshal_int_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("ttms", field);
 
-  field = new LogField("transfer_time_ms_hex", "ttmsh", LogField::sINT, 
&LogAccess::marshal_transfer_time_ms,
+  field = new LogField("transfer_time_ms_hex", "ttmsh", LogField::Type::sINT, 
&LogAccess::marshal_transfer_time_ms,
                        &LogAccess::unmarshal_int_to_str_hex);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("ttmsh", field);
 
-  field = new LogField("transfer_time_ms_fractional", "ttmsf", LogField::sINT, 
&LogAccess::marshal_transfer_time_ms,
+  field = new LogField("transfer_time_ms_fractional", "ttmsf", 
LogField::Type::sINT, &LogAccess::marshal_transfer_time_ms,
                        &LogAccess::unmarshal_ttmsf);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("ttmsf", field);
 
-  field =
-    new LogField("transfer_time_sec", "tts", LogField::sINT, 
&LogAccess::marshal_transfer_time_s, &LogAccess::unmarshal_int_to_str);
+  field = new LogField("transfer_time_sec", "tts", LogField::Type::sINT, 
&LogAccess::marshal_transfer_time_s,
+                       &LogAccess::unmarshal_int_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("tts", field);
 
-  field = new LogField("file_size", "fsiz", LogField::sINT, 
&LogAccess::marshal_file_size, &LogAccess::unmarshal_int_to_str);
+  field = new LogField("file_size", "fsiz", LogField::Type::sINT, 
&LogAccess::marshal_file_size, &LogAccess::unmarshal_int_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("fsiz", field);
 
-  field = new LogField("client_connection_id", "ccid", LogField::sINT, 
&LogAccess::marshal_client_http_connection_id,
+  field = new LogField("client_connection_id", "ccid", LogField::Type::sINT, 
&LogAccess::marshal_client_http_connection_id,
                        &LogAccess::unmarshal_int_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("ccid", field);
 
-  field = new LogField("client_transaction_id", "ctid", LogField::sINT, 
&LogAccess::marshal_client_http_transaction_id,
+  field = new LogField("client_transaction_id", "ctid", LogField::Type::sINT, 
&LogAccess::marshal_client_http_transaction_id,
                        &LogAccess::unmarshal_int_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("ctid", field);
 
-  field = new LogField("cache_read_retry_attempts", "crra", LogField::sINT, 
&LogAccess::marshal_cache_read_retries,
+  field = new LogField("cache_read_retry_attempts", "crra", 
LogField::Type::sINT, &LogAccess::marshal_cache_read_retries,
                        &LogAccess::unmarshal_int_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("crra", field);
 
-  field = new LogField("cache_write_retry_attempts", "cwra", LogField::sINT, 
&LogAccess::marshal_cache_write_retries,
+  field = new LogField("cache_write_retry_attempts", "cwra", 
LogField::Type::sINT, &LogAccess::marshal_cache_write_retries,
                        &LogAccess::unmarshal_int_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cwra", field);
 
-  field = new LogField("cache_collapsed_connection_success", "cccs", 
LogField::sINT,
+  field = new LogField("cache_collapsed_connection_success", "cccs", 
LogField::Type::sINT,
                        &LogAccess::marshal_cache_collapsed_connection_success, 
&LogAccess::unmarshal_int_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cccs", field);
 
-  field = new LogField("client_transaction_priority_weight", "ctpw", 
LogField::sINT,
+  field = new LogField("client_transaction_priority_weight", "ctpw", 
LogField::Type::sINT,
                        
&LogAccess::marshal_client_http_transaction_priority_weight, 
&LogAccess::unmarshal_int_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("ctpw", field);
 
-  field = new LogField("client_transaction_priority_dependence", "ctpd", 
LogField::sINT,
+  field = new LogField("client_transaction_priority_dependence", "ctpd", 
LogField::Type::sINT,
                        
&LogAccess::marshal_client_http_transaction_priority_dependence, 
&LogAccess::unmarshal_int_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("ctpd", field);
 
-  field = new LogField("proxy_protocol_version", "ppv", LogField::STRING, 
&LogAccess::marshal_proxy_protocol_version,
+  field = new LogField("proxy_protocol_version", "ppv", 
LogField::Type::STRING, &LogAccess::marshal_proxy_protocol_version,
                        &LogAccess::unmarshal_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("ppv", field);
 
-  field = new LogField("proxy_protocol_src_ip", "pps", LogField::IP, 
&LogAccess::marshal_proxy_protocol_src_ip,
+  field = new LogField("proxy_protocol_src_ip", "pps", LogField::Type::IP, 
&LogAccess::marshal_proxy_protocol_src_ip,
                        &LogAccess::unmarshal_ip_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("ppsip", field);
 
-  field = new LogField("proxy_protocol_dst_ip", "ppd", LogField::IP, 
&LogAccess::marshal_proxy_protocol_dst_ip,
+  field = new LogField("proxy_protocol_dst_ip", "ppd", LogField::Type::IP, 
&LogAccess::marshal_proxy_protocol_dst_ip,
                        &LogAccess::unmarshal_ip_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("ppdip", field);

Review Comment:
   field_symbol_hash key should match the field symbol ("ppd"), otherwise log 
formats using %<ppd> won't resolve this field. The current key "ppdip" makes 
the field effectively unreachable by its documented symbol.



##########
doc/developer-guide/logging-architecture/binary-log-v3-format.en.rst:
##########
@@ -0,0 +1,216 @@
+.. 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.
+
+.. include:: ../../common.defs
+
+.. _binary-log-v3-format:
+
+Self-Describing Binary Log Format (v3)
+**************************************
+
+This page specifies the on-disk format of a binary log segment, version 3, in
+enough detail to implement a decoder *without* the Traffic Server source tree.
+A version 3 segment is **self-describing**: every field's type is published in
+the segment header, so a generic reader can decode each entry by dispatching on
+a small, stable set of type codes — no embedded copy of the ATS symbol-to-type
+table is required.
+
+Motivation
+==========
+
+In version 2, a segment header carries the field *symbols* (``fmt_fieldlist``,
+e.g. ``"chi cqu pssc"``) and a printf-style *template* (``fmt_printf``) but
+**not** the field types. To decode an entry a reader had to already know the
+type of each symbol, because the value encodings are only self-delimiting once
+the type is known (``IP`` is variable length, for example). That coupled every
+out-of-tree parser to the exact ATS build that wrote the log.
+
+Version 3 adds one thing: a per-segment **field-type schema** that lists the
+wire type of every field, in field order. Decoding then needs only the symbols
+(as keys) and the schema (for types).
+
+Segment layout
+==============
+
+A ``.blog`` file is a stream of segments, each a serialized ``LogBuffer``:
+
+::
+
+    LogBufferHeader            (per segment)
+      cookie       = 0xaceface
+      version      = 3
+      format_type, byte_count, entry_count, timestamps, flags, signature
+      fmt_name_offset
+      fmt_fieldlist_offset      -> "chi cqu pssc ..."   (symbols, space 
separated)
+      fmt_printf_offset         -> "%<chi> %<cqu> ..."

Review Comment:
   The on-disk fmt_fieldlist produced by LogFormat::parse_format_string is 
comma-separated (e.g. "chi,cqu,pssc"), not space-separated. Describing it as 
space-separated is misleading for out-of-tree readers that follow this spec 
verbatim.



##########
src/proxy/logging/Log.cc:
##########
@@ -328,371 +328,389 @@ Log::init_fields()
   LogField::init_milestone_container();
 
   // client -> proxy fields
-  field = new LogField("client_host_ip", "chi", LogField::IP, 
&LogAccess::marshal_client_host_ip, &LogAccess::unmarshal_ip_to_str);
+  field =
+    new LogField("client_host_ip", "chi", LogField::Type::IP, 
&LogAccess::marshal_client_host_ip, &LogAccess::unmarshal_ip_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("chi", field);
 
-  field =
-    new LogField("client_host_port", "chp", LogField::sINT, 
&LogAccess::marshal_client_host_port, &LogAccess::unmarshal_int_to_str);
+  field = new LogField("client_host_port", "chp", LogField::Type::sINT, 
&LogAccess::marshal_client_host_port,
+                       &LogAccess::unmarshal_int_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("chp", field);
 
-  field =
-    new LogField("client_host_ip_hex", "chih", LogField::IP, 
&LogAccess::marshal_client_host_ip, &LogAccess::unmarshal_ip_to_hex);
+  field = new LogField("client_host_ip_hex", "chih", LogField::Type::IP, 
&LogAccess::marshal_client_host_ip,
+                       &LogAccess::unmarshal_ip_to_hex);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("chih", field);
 
-  field = new LogField("client_host_ip_verified", "chiv", LogField::IP, 
&LogAccess::marshal_client_host_ip_verified,
+  field = new LogField("client_host_ip_verified", "chiv", LogField::Type::IP, 
&LogAccess::marshal_client_host_ip_verified,
                        &LogAccess::unmarshal_ip_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("chiv", field);
 
   // remote client (Not necessarily the requesting client IP - See proxy 
protocol)
-  field = new LogField("remote_host_ip", "rchi", LogField::IP, 
&LogAccess::marshal_remote_host_ip, &LogAccess::unmarshal_ip_to_str);
+  field =
+    new LogField("remote_host_ip", "rchi", LogField::Type::IP, 
&LogAccess::marshal_remote_host_ip, &LogAccess::unmarshal_ip_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("rchi", field);
 
-  field = new LogField("remote_host_port", "rchp", LogField::sINT, 
&LogAccess::marshal_remote_host_port,
+  field = new LogField("remote_host_port", "rchp", LogField::Type::sINT, 
&LogAccess::marshal_remote_host_port,
                        &LogAccess::unmarshal_int_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("rchp", field);
 
-  field =
-    new LogField("remote_host_ip_hex", "rchh", LogField::IP, 
&LogAccess::marshal_remote_host_ip, &LogAccess::unmarshal_ip_to_hex);
+  field = new LogField("remote_host_ip_hex", "rchh", LogField::Type::IP, 
&LogAccess::marshal_remote_host_ip,
+                       &LogAccess::unmarshal_ip_to_hex);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("rchh", field);
 
   // interface ip
 
-  field =
-    new LogField("host_interface_ip", "hii", LogField::IP, 
&LogAccess::marshal_host_interface_ip, &LogAccess::unmarshal_ip_to_str);
+  field = new LogField("host_interface_ip", "hii", LogField::Type::IP, 
&LogAccess::marshal_host_interface_ip,
+                       &LogAccess::unmarshal_ip_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("hii", field);
 
-  field = new LogField("host_interface_ip_hex", "hiih", LogField::IP, 
&LogAccess::marshal_host_interface_ip,
+  field = new LogField("host_interface_ip_hex", "hiih", LogField::Type::IP, 
&LogAccess::marshal_host_interface_ip,
                        &LogAccess::unmarshal_ip_to_hex);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("hiih", field);
   // interface ip end
-  field = new LogField("client_auth_user_name", "caun", LogField::STRING, 
&LogAccess::marshal_client_auth_user_name,
+  field = new LogField("client_auth_user_name", "caun", 
LogField::Type::STRING, &LogAccess::marshal_client_auth_user_name,
                        &LogAccess::unmarshal_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("caun", field);
 
-  field = new LogField("plugin_identity_id", "piid", LogField::sINT, 
&LogAccess::marshal_plugin_identity_id,
+  field = new LogField("plugin_identity_id", "piid", LogField::Type::sINT, 
&LogAccess::marshal_plugin_identity_id,
                        &LogAccess::unmarshal_int_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("piid", field);
 
-  field = new LogField("plugin_identity_tag", "pitag", LogField::STRING, 
&LogAccess::marshal_plugin_identity_tag,
+  field = new LogField("plugin_identity_tag", "pitag", LogField::Type::STRING, 
&LogAccess::marshal_plugin_identity_tag,
                        &LogAccess::unmarshal_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("pitag", field);
 
-  field = new LogField("client_req_timestamp_sec", "cqts", LogField::sINT, 
&LogAccess::marshal_client_req_timestamp_sec,
+  field = new LogField("client_req_timestamp_sec", "cqts", 
LogField::Type::sINT, &LogAccess::marshal_client_req_timestamp_sec,
                        &LogAccess::unmarshal_int_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cqts", field);
 
-  field = new LogField("client_req_timestamp_hex_sec", "cqth", LogField::sINT, 
&LogAccess::marshal_client_req_timestamp_sec,
+  field = new LogField("client_req_timestamp_hex_sec", "cqth", 
LogField::Type::sINT, &LogAccess::marshal_client_req_timestamp_sec,
                        &LogAccess::unmarshal_int_to_str_hex);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cqth", field);
 
-  field = new LogField("client_req_timestamp_squid", "cqtq", LogField::sINT, 
&LogAccess::marshal_client_req_timestamp_ms,
+  field = new LogField("client_req_timestamp_squid", "cqtq", 
LogField::Type::sINT, &LogAccess::marshal_client_req_timestamp_ms,
                        &LogAccess::unmarshal_ttmsf);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cqtq", field);
 
-  field = new LogField("client_req_timestamp_netscape", "cqtn", 
LogField::sINT, &LogAccess::marshal_client_req_timestamp_sec,
+  field = new LogField("client_req_timestamp_netscape", "cqtn", 
LogField::Type::sINT, &LogAccess::marshal_client_req_timestamp_sec,
                        &LogAccess::unmarshal_int_to_netscape_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cqtn", field);
 
-  field = new LogField("client_req_timestamp_date", "cqtd", LogField::sINT, 
&LogAccess::marshal_client_req_timestamp_sec,
+  field = new LogField("client_req_timestamp_date", "cqtd", 
LogField::Type::sINT, &LogAccess::marshal_client_req_timestamp_sec,
                        &LogAccess::unmarshal_int_to_date_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cqtd", field);
 
-  field = new LogField("client_req_timestamp_time", "cqtt", LogField::sINT, 
&LogAccess::marshal_client_req_timestamp_sec,
+  field = new LogField("client_req_timestamp_time", "cqtt", 
LogField::Type::sINT, &LogAccess::marshal_client_req_timestamp_sec,
                        &LogAccess::unmarshal_int_to_time_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cqtt", field);
 
-  field = new LogField("client_req_http_method", "cqhm", LogField::STRING, 
&LogAccess::marshal_client_req_http_method,
+  field = new LogField("client_req_http_method", "cqhm", 
LogField::Type::STRING, &LogAccess::marshal_client_req_http_method,
                        &LogAccess::unmarshal_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cqhm", field);
 
-  field = new LogField("client_req_url", "cqu", LogField::STRING, 
&LogAccess::marshal_client_req_url, &LogAccess::unmarshal_str,
-                       &LogAccess::set_client_req_url);
+  field = new LogField("client_req_url", "cqu", LogField::Type::STRING, 
&LogAccess::marshal_client_req_url,
+                       &LogAccess::unmarshal_str, 
&LogAccess::set_client_req_url);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cqu", field);
 
-  field = new LogField("client_req_url", "pqu", LogField::STRING, 
&LogAccess::marshal_client_req_url, &LogAccess::unmarshal_str,
-                       &LogAccess::set_client_req_url);
+  field = new LogField("client_req_url", "pqu", LogField::Type::STRING, 
&LogAccess::marshal_client_req_url,
+                       &LogAccess::unmarshal_str, 
&LogAccess::set_client_req_url);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("pqu", field);
 
-  field = new LogField("client_req_url_canonical", "cquc", LogField::STRING, 
&LogAccess::marshal_client_req_url_canon,
+  field = new LogField("client_req_url_canonical", "cquc", 
LogField::Type::STRING, &LogAccess::marshal_client_req_url_canon,
                        &LogAccess::unmarshal_str, 
&LogAccess::set_client_req_url_canon);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cquc", field);
 
-  field = new LogField("client_req_url_canonical", "pquc", LogField::STRING, 
&LogAccess::marshal_client_req_url_canon,
+  field = new LogField("client_req_url_canonical", "pquc", 
LogField::Type::STRING, &LogAccess::marshal_client_req_url_canon,
                        &LogAccess::unmarshal_str, 
&LogAccess::set_client_req_url_canon);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("pquc", field);
 
-  field =
-    new LogField("client_req_unmapped_url_canonical", "cquuc", 
LogField::STRING, &LogAccess::marshal_client_req_unmapped_url_canon,
-                 &LogAccess::unmarshal_str, 
&LogAccess::set_client_req_unmapped_url_canon);
+  field = new LogField("client_req_unmapped_url_canonical", "cquuc", 
LogField::Type::STRING,
+                       &LogAccess::marshal_client_req_unmapped_url_canon, 
&LogAccess::unmarshal_str,
+                       &LogAccess::set_client_req_unmapped_url_canon);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cquuc", field);
 
-  field = new LogField("client_req_unmapped_url_path", "cquup", 
LogField::STRING, &LogAccess::marshal_client_req_unmapped_url_path,
-                       &LogAccess::unmarshal_str, 
&LogAccess::set_client_req_unmapped_url_path);
+  field =
+    new LogField("client_req_unmapped_url_path", "cquup", 
LogField::Type::STRING, &LogAccess::marshal_client_req_unmapped_url_path,
+                 &LogAccess::unmarshal_str, 
&LogAccess::set_client_req_unmapped_url_path);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cquup", field);
 
-  field = new LogField("client_req_unmapped_url_host", "cquuh", 
LogField::STRING, &LogAccess::marshal_client_req_unmapped_url_host,
-                       &LogAccess::unmarshal_str, 
&LogAccess::set_client_req_unmapped_url_host);
+  field =
+    new LogField("client_req_unmapped_url_host", "cquuh", 
LogField::Type::STRING, &LogAccess::marshal_client_req_unmapped_url_host,
+                 &LogAccess::unmarshal_str, 
&LogAccess::set_client_req_unmapped_url_host);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cquuh", field);
 
-  field = new LogField("client_req_url_scheme", "cqus", LogField::STRING, 
&LogAccess::marshal_client_req_url_scheme,
+  field = new LogField("client_req_url_scheme", "cqus", 
LogField::Type::STRING, &LogAccess::marshal_client_req_url_scheme,
                        &LogAccess::unmarshal_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cqus", field);
 
-  field = new LogField("client_req_url_scheme", "pqus", LogField::STRING, 
&LogAccess::marshal_client_req_url_scheme,
+  field = new LogField("client_req_url_scheme", "pqus", 
LogField::Type::STRING, &LogAccess::marshal_client_req_url_scheme,
                        &LogAccess::unmarshal_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("pqus", field);
 
-  field = new LogField("client_req_url_path", "cqup", LogField::STRING, 
&LogAccess::marshal_client_req_url_path,
+  field = new LogField("client_req_url_path", "cqup", LogField::Type::STRING, 
&LogAccess::marshal_client_req_url_path,
                        &LogAccess::unmarshal_str, 
&LogAccess::set_client_req_url_path);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cqup", field);
 
-  field = new LogField("client_req_url_path", "pqup", LogField::STRING, 
&LogAccess::marshal_client_req_url_path,
+  field = new LogField("client_req_url_path", "pqup", LogField::Type::STRING, 
&LogAccess::marshal_client_req_url_path,
                        &LogAccess::unmarshal_str, 
&LogAccess::set_client_req_url_path);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("pqup", field);
 
-  field = new LogField("client_req_protocol_version", "cqpv", 
LogField::STRING, &LogAccess::marshal_client_req_protocol_version,
-                       &LogAccess::unmarshal_str);
+  field = new LogField("client_req_protocol_version", "cqpv", 
LogField::Type::STRING,
+                       &LogAccess::marshal_client_req_protocol_version, 
&LogAccess::unmarshal_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cqpv", field);
 
-  field = new LogField("server_req_protocol_version", "sqpv", 
LogField::STRING, &LogAccess::marshal_server_req_protocol_version,
-                       &LogAccess::unmarshal_str);
+  field = new LogField("server_req_protocol_version", "sqpv", 
LogField::Type::STRING,
+                       &LogAccess::marshal_server_req_protocol_version, 
&LogAccess::unmarshal_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("sqpv", field);
 
-  field = new LogField("client_req_header_len", "cqhl", LogField::sINT, 
&LogAccess::marshal_client_req_header_len,
+  field = new LogField("client_req_header_len", "cqhl", LogField::Type::sINT, 
&LogAccess::marshal_client_req_header_len,
                        &LogAccess::unmarshal_int_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cqhl", field);
 
-  field = new LogField("client_req_squid_len", "cqql", LogField::sINT, 
&LogAccess::marshal_client_req_squid_len,
+  field = new LogField("client_req_squid_len", "cqql", LogField::Type::sINT, 
&LogAccess::marshal_client_req_squid_len,
                        &LogAccess::unmarshal_int_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cqql", field);
 
   // Client request squid length plus TLS handshake bytes received for TLS 
connections
-  field = new LogField("client_req_squid_len_tls", "cqqtl", LogField::sINT, 
&LogAccess::marshal_client_req_squid_len_tls,
+  field = new LogField("client_req_squid_len_tls", "cqqtl", 
LogField::Type::sINT, &LogAccess::marshal_client_req_squid_len_tls,
                        &LogAccess::unmarshal_int_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cqqtl", field);
 
-  field = new LogField("cache_lookup_url_canonical", "cluc", LogField::STRING, 
&LogAccess::marshal_cache_lookup_url_canon,
+  // Client request squid length plus TLS handshake bytes received for TLS 
connections
+  field = new LogField("client_req_squid_len_tls", "cqqtl", 
LogField::Type::sINT, &LogAccess::marshal_client_req_squid_len_tls,
+                       &LogAccess::unmarshal_int_to_str);
+  global_field_list.add(field, false);
+  field_symbol_hash.emplace("cqqtl", field);
+
+  field = new LogField("cache_lookup_url_canonical", "cluc", 
LogField::Type::STRING, &LogAccess::marshal_cache_lookup_url_canon,
                        &LogAccess::unmarshal_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cluc", field);
 
-  field = new LogField("cache_key_hash", "ckh", LogField::STRING, 
&LogAccess::marshal_cache_key_hash, &LogAccess::unmarshal_str);
+  field =
+    new LogField("cache_key_hash", "ckh", LogField::Type::STRING, 
&LogAccess::marshal_cache_key_hash, &LogAccess::unmarshal_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("ckh", field);
 
-  field = new LogField("client_sni_server_name", "cssn", LogField::STRING, 
&LogAccess::marshal_client_sni_server_name,
+  field = new LogField("client_sni_server_name", "cssn", 
LogField::Type::STRING, &LogAccess::marshal_client_sni_server_name,
                        &LogAccess::unmarshal_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cssn", field);
 
-  field = new LogField("client_ssl_cert_provided", "cscert", LogField::STRING, 
&LogAccess::marshal_client_provided_cert,
+  field = new LogField("client_ssl_cert_provided", "cscert", 
LogField::Type::STRING, &LogAccess::marshal_client_provided_cert,
                        &LogAccess::unmarshal_int_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cscert", field);
 
-  field = new LogField("proxy_ssl_cert_provided", "pscert", LogField::STRING, 
&LogAccess::marshal_proxy_provided_cert,
+  field = new LogField("proxy_ssl_cert_provided", "pscert", 
LogField::Type::STRING, &LogAccess::marshal_proxy_provided_cert,
                        &LogAccess::unmarshal_int_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("pscert", field);
 
-  field = new LogField("process_uuid", "puuid", LogField::STRING, 
&LogAccess::marshal_process_uuid, &LogAccess::unmarshal_str);
+  field =
+    new LogField("process_uuid", "puuid", LogField::Type::STRING, 
&LogAccess::marshal_process_uuid, &LogAccess::unmarshal_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("puuid", field);
 
-  field =
-    new LogField("process_snowflake_id", "psfid", LogField::STRING, 
&LogAccess::marshal_process_sfid, &LogAccess::unmarshal_str);
+  field = new LogField("process_snowflake_id", "psfid", 
LogField::Type::STRING, &LogAccess::marshal_process_sfid,
+                       &LogAccess::unmarshal_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("psfid", field);
 
-  field = new LogField("client_req_content_len", "cqcl", LogField::sINT, 
&LogAccess::marshal_client_req_content_len,
+  field = new LogField("client_req_content_len", "cqcl", LogField::Type::sINT, 
&LogAccess::marshal_client_req_content_len,
                        &LogAccess::unmarshal_int_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cqcl", field);
 
-  field = new LogField("client_req_tcp_reused", "cqtr", LogField::sINT, 
&LogAccess::marshal_client_req_tcp_reused,
+  field = new LogField("client_req_tcp_reused", "cqtr", LogField::Type::sINT, 
&LogAccess::marshal_client_req_tcp_reused,
                        &LogAccess::unmarshal_int_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cqtr", field);
 
-  field = new LogField("client_req_is_ssl", "cqssl", LogField::sINT, 
&LogAccess::marshal_client_req_is_ssl,
+  field = new LogField("client_req_is_ssl", "cqssl", LogField::Type::sINT, 
&LogAccess::marshal_client_req_is_ssl,
                        &LogAccess::unmarshal_int_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cqssl", field);
 
-  field = new LogField("client_req_ssl_reused", "cqssr", LogField::sINT, 
&LogAccess::marshal_client_req_ssl_reused,
+  field = new LogField("client_req_ssl_reused", "cqssr", LogField::Type::sINT, 
&LogAccess::marshal_client_req_ssl_reused,
                        &LogAccess::unmarshal_int_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cqssr", field);
 
-  field = new LogField("client_req_is_internal", "cqint", LogField::sINT, 
&LogAccess::marshal_client_req_is_internal,
+  field = new LogField("client_req_is_internal", "cqint", 
LogField::Type::sINT, &LogAccess::marshal_client_req_is_internal,
                        &LogAccess::unmarshal_int_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cqint", field);
 
-  field = new LogField("client_req_mptcp", "cqmpt", LogField::sINT, 
&LogAccess::marshal_client_req_mptcp_state,
+  field = new LogField("client_req_mptcp", "cqmpt", LogField::Type::sINT, 
&LogAccess::marshal_client_req_mptcp_state,
                        &LogAccess::unmarshal_int_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cqmpt", field);
 
-  field = new LogField("client_sec_protocol", "cqssv", LogField::STRING, 
&LogAccess::marshal_client_security_protocol,
+  field = new LogField("client_sec_protocol", "cqssv", LogField::Type::STRING, 
&LogAccess::marshal_client_security_protocol,
                        &LogAccess::unmarshal_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cqssv", field);
 
-  field = new LogField("client_cipher_suite", "cqssc", LogField::STRING, 
&LogAccess::marshal_client_security_cipher_suite,
+  field = new LogField("client_cipher_suite", "cqssc", LogField::Type::STRING, 
&LogAccess::marshal_client_security_cipher_suite,
                        &LogAccess::unmarshal_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cqssc", field);
 
-  field =
-    new LogField("client_curve", "cqssu", LogField::STRING, 
&LogAccess::marshal_client_security_curve, &LogAccess::unmarshal_str);
+  field = new LogField("client_curve", "cqssu", LogField::Type::STRING, 
&LogAccess::marshal_client_security_curve,
+                       &LogAccess::unmarshal_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cqssu", field);
 
-  field =
-    new LogField("client_group", "cqssg", LogField::STRING, 
&LogAccess::marshal_client_security_group, &LogAccess::unmarshal_str);
+  field = new LogField("client_group", "cqssg", LogField::Type::STRING, 
&LogAccess::marshal_client_security_group,
+                       &LogAccess::unmarshal_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cqssg", field);
 
-  field =
-    new LogField("client_sec_alpn", "cqssa", LogField::STRING, 
&LogAccess::marshal_client_security_alpn, &LogAccess::unmarshal_str);
+  field = new LogField("client_sec_alpn", "cqssa", LogField::Type::STRING, 
&LogAccess::marshal_client_security_alpn,
+                       &LogAccess::unmarshal_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cqssa", field);
 
   // TLS handshake bytes - bytes received from client during TLS handshake
-  field = new LogField("client_tls_handshake_bytes_rx", "cthbr", 
LogField::sINT, &LogAccess::marshal_client_tls_handshake_bytes_rx,
-                       &LogAccess::unmarshal_int_to_str);
+  field = new LogField("client_tls_handshake_bytes_rx", "cthbr", 
LogField::Type::sINT,
+                       &LogAccess::marshal_client_tls_handshake_bytes_rx, 
&LogAccess::unmarshal_int_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cthbr", field);
 
   // TLS handshake bytes - bytes sent to client during TLS handshake
-  field = new LogField("client_tls_handshake_bytes_tx", "cthbt", 
LogField::sINT, &LogAccess::marshal_client_tls_handshake_bytes_tx,
-                       &LogAccess::unmarshal_int_to_str);
+  field = new LogField("client_tls_handshake_bytes_tx", "cthbt", 
LogField::Type::sINT,
+                       &LogAccess::marshal_client_tls_handshake_bytes_tx, 
&LogAccess::unmarshal_int_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cthbt", field);
 
   // TLS handshake bytes - total (rx + tx) during TLS handshake
-  field = new LogField("client_tls_handshake_bytes", "cthb", LogField::sINT, 
&LogAccess::marshal_client_tls_handshake_bytes,
+  field = new LogField("client_tls_handshake_bytes", "cthb", 
LogField::Type::sINT, &LogAccess::marshal_client_tls_handshake_bytes,
                        &LogAccess::unmarshal_int_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cthb", field);
 
   Ptr<LogFieldAliasTable> finish_status_map = make_ptr(new LogFieldAliasTable);
   finish_status_map->init(N_LOG_FINISH_CODE_TYPES, LOG_FINISH_FIN, "FIN", 
LOG_FINISH_INTR, "INTR", LOG_FINISH_TIMEOUT, "TIMEOUT");
 
-  field = new LogField("client_finish_status_code", "cfsc", LogField::sINT, 
&LogAccess::marshal_client_finish_status_code,
+  field = new LogField("client_finish_status_code", "cfsc", 
LogField::Type::sINT, &LogAccess::marshal_client_finish_status_code,
                        &LogAccess::unmarshal_finish_status, 
make_alias_map(finish_status_map));
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cfsc", field);
 
-  field =
-    new LogField("client_req_id", "crid", LogField::sINT, 
&LogAccess::marshal_client_req_id, &LogAccess::unmarshal_int_to_str);
+  field = new LogField("client_req_id", "crid", LogField::Type::sINT, 
&LogAccess::marshal_client_req_id,
+                       &LogAccess::unmarshal_int_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("crid", field);
 
-  field =
-    new LogField("client_req_uuid", "cruuid", LogField::STRING, 
&LogAccess::marshal_client_req_uuid, &LogAccess::unmarshal_str);
+  field = new LogField("client_req_uuid", "cruuid", LogField::Type::STRING, 
&LogAccess::marshal_client_req_uuid,
+                       &LogAccess::unmarshal_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cruuid", field);
 
-  field = new LogField("client_rx_error_code", "crec", LogField::STRING, 
&LogAccess::marshal_client_rx_error_code,
+  field = new LogField("client_rx_error_code", "crec", LogField::Type::STRING, 
&LogAccess::marshal_client_rx_error_code,
                        &LogAccess::unmarshal_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("crec", field);
 
-  field = new LogField("client_tx_error_code", "ctec", LogField::STRING, 
&LogAccess::marshal_client_tx_error_code,
+  field = new LogField("client_tx_error_code", "ctec", LogField::Type::STRING, 
&LogAccess::marshal_client_tx_error_code,
                        &LogAccess::unmarshal_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("ctec", field);
 
-  field = new LogField("client_request_all_header_fields", "cqah", 
LogField::STRING,
+  field = new LogField("client_request_all_header_fields", "cqah", 
LogField::Type::STRING,
                        &LogAccess::marshal_client_req_all_header_fields, 
&LogUtils::unmarshalMimeHdr);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("cqah", field);
 
   // proxy -> client fields
-  field = new LogField("proxy_resp_content_type", "psct", LogField::STRING, 
&LogAccess::marshal_proxy_resp_content_type,
+  field = new LogField("proxy_resp_content_type", "psct", 
LogField::Type::STRING, &LogAccess::marshal_proxy_resp_content_type,
                        &LogAccess::unmarshal_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("psct", field);
 
-  field = new LogField("proxy_resp_reason_phrase", "prrp", LogField::STRING, 
&LogAccess::marshal_proxy_resp_reason_phrase,
+  field = new LogField("proxy_resp_reason_phrase", "prrp", 
LogField::Type::STRING, &LogAccess::marshal_proxy_resp_reason_phrase,
                        &LogAccess::unmarshal_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("prrp", field);
 
-  field = new LogField("proxy_resp_squid_len", "psql", LogField::sINT, 
&LogAccess::marshal_proxy_resp_squid_len,
+  field = new LogField("proxy_resp_squid_len", "psql", LogField::Type::sINT, 
&LogAccess::marshal_proxy_resp_squid_len,
                        &LogAccess::unmarshal_int_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("psql", field);
 
   // Squid length plus TLS handshake bytes sent for TLS connections
-  field = new LogField("proxy_resp_squid_len_tls", "psqtl", LogField::sINT, 
&LogAccess::marshal_proxy_resp_squid_len_tls,
+  field = new LogField("proxy_resp_squid_len_tls", "psqtl", 
LogField::Type::sINT, &LogAccess::marshal_proxy_resp_squid_len_tls,
                        &LogAccess::unmarshal_int_to_str);
   global_field_list.add(field, false);
   field_symbol_hash.emplace("psqtl", field);

Review Comment:
   Log field "psqtl" is registered twice (duplicate LogField allocation, 
duplicate global_field_list entry, and duplicate field_symbol_hash key). The 
second registration leaks memory and can lead to inconsistent field 
iteration/lookup behavior.



-- 
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]

Reply via email to