)]}'
{"/PATCHSET_LEVEL":[{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"ba44967570118fbaf5b318b181e1c8f159230ced","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":2,"id":"47c4c426_3805b9bf","updated":"2026-01-30 14:34:31.000000000","message":"Thanks for looking at this 🙏 It\u0027s obvs going to need some more thought/work, so setting WIP flag for now.","commit_id":"c3dc4554661355b38109e869445dccbb90110b32"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"6e0fbf9e0b8761689e213f7fc6636f3e58b65dbd","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":2,"id":"a1aafda1_3a0e312c","updated":"2026-01-29 17:43:37.000000000","message":"maybe if we documented \"prior to swift xyz long lines looked like xyz and the path was double-quoted, but after swift abc log lines look like abc and you should only unquote %20\" ... or ... something?  \n\nIt\u0027s possible my entire perspective here is skewed towards some kind of \"lawful neutral\" alignment that\u0027s not putting sufficient moral judgement on the documented behavior.  I 100% agree the inconsistencies between proxy/dataplane logging (and now labeled metrics!) is NOT ideal!!!  Perfectly reasonable to ask \"what can we do here to improve consistency and ease of use\" - I assume it\u0027s something something, upgrade path, something something, future is awesome.","commit_id":"c3dc4554661355b38109e869445dccbb90110b32"}],"doc/source/logs.rst":[{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"6e0fbf9e0b8761689e213f7fc6636f3e58b65dbd","unresolved":true,"context_lines":[{"line_number":121,"context_line":"    Some log fields (like the request path) are already url quoted, so the"},{"line_number":122,"context_line":"    logged value will be double-quoted. For example, if a client uploads an"},{"line_number":123,"context_line":"    object name with a ``:`` in it, it will be url-quoted as ``%3A``. The log"},{"line_number":124,"context_line":"    module will then quote this value as ``%253A``."},{"line_number":125,"context_line":""},{"line_number":126,"context_line":"Swift Source"},{"line_number":127,"context_line":"\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d"}],"source_content_type":"text/x-rst","patch_set":2,"id":"e2beb4a7_e4b464ac","side":"PARENT","line":124,"updated":"2026-01-29 17:43:37.000000000","message":"I don\u0027t think we can do this, consider this script:\n\n```\nvagrant@saio:~$ cat /vagrant/.scratch/my_out_of_tree_log_parser.py \nimport sys\nfrom urllib.parse import unquote, urlparse\nwith open(\u0027/var/log/syslog\u0027) as f:\n    for line in f:\n        parts \u003d line.split()\n        if len(parts) \u003e 21 and parts[18] \u003d\u003d sys.argv[1] and parts[21] \u003d\u003d \u0027-\u0027:\n            # dunno why swift double quotes, but it\u0027s the documented behavior so I\u0027m sure they\u0027ll maintain it\n            quoted_path \u003d unquote(parts[9])\n            quoted_container \u003d urlparse(quoted_path).path.split(\u0027/\u0027)[3]\n            print(\u0027the for realzy container name is: %s\u0027 % unquote(quoted_container))\n```\n\nBefore:\n\n```\nvagrant@saio:~$ swift stat \"100%beef\"\n               Account: AUTH_test\n             Container: 100%beef\n               Objects: 0\n                 Bytes: 0\n              Read ACL:\n             Write ACL:\n               Sync To:\n              Sync Key:\n          Content-Type: text/plain; charset\u003dutf-8\n           X-Timestamp: 1769706797.21618\n         Last-Modified: Thu, 29 Jan 2026 17:13:18 GMT\n         Accept-Ranges: bytes\n      X-Storage-Policy: default\n                  Vary: Accept\n            X-Trans-Id: tx4e58a4d372f84f2494d3d-00697b9931\nX-Openstack-Request-Id: tx4e58a4d372f84f2494d3d-00697b9931\nvagrant@saio:~$ python3 /vagrant/.scratch/my_out_of_tree_log_parser.py tx4e58a4d372f84f2494d3d-00697b9931\nthe for realzy container name is: 100%beef\n```\n\nAfter:\n\n```\nvagrant@saio:~$ swift stat \"100%beef\"\n               Account: AUTH_test\n             Container: 100%beef\n               Objects: 0\n                 Bytes: 0\n              Read ACL:\n             Write ACL:\n               Sync To:\n              Sync Key:\n          Content-Type: text/plain; charset\u003dutf-8\n           X-Timestamp: 1769706797.22474\n         Last-Modified: Thu, 29 Jan 2026 17:13:18 GMT\n         Accept-Ranges: bytes\n      X-Storage-Policy: default\n                  Vary: Accept\n            X-Trans-Id: txe0433acf991544e6a0817-00697b9944\nX-Openstack-Request-Id: txe0433acf991544e6a0817-00697b9944\nvagrant@saio:~$ python3 /vagrant/.scratch/my_out_of_tree_log_parser.py txe0433acf991544e6a0817-00697b9944\nthe for realzy container name is: 100�ef\n```","commit_id":"1b0c11b68fe264fad2ed2e5d8e4db083986c9e6a"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"ba44967570118fbaf5b318b181e1c8f159230ced","unresolved":true,"context_lines":[{"line_number":121,"context_line":"    Some log fields (like the request path) are already url quoted, so the"},{"line_number":122,"context_line":"    logged value will be double-quoted. For example, if a client uploads an"},{"line_number":123,"context_line":"    object name with a ``:`` in it, it will be url-quoted as ``%3A``. The log"},{"line_number":124,"context_line":"    module will then quote this value as ``%253A``."},{"line_number":125,"context_line":""},{"line_number":126,"context_line":"Swift Source"},{"line_number":127,"context_line":"\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d"}],"source_content_type":"text/x-rst","patch_set":2,"id":"50a3079c_629e822b","side":"PARENT","line":124,"in_reply_to":"e2beb4a7_e4b464ac","updated":"2026-01-30 14:34:31.000000000","message":"fair point, I guess I need to see this as an optional upgrade","commit_id":"1b0c11b68fe264fad2ed2e5d8e4db083986c9e6a"}],"swift/common/swob.py":[{"author":{"_account_id":15343,"name":"Tim Burke","email":"tburke@nvidia.com","username":"tburke"},"change_message_id":"ded325a1b81817016dff59be165e92fa5eff7b51","unresolved":true,"context_lines":[{"line_number":1080,"context_line":"        This can be useful when constructing a path to send to a backend"},{"line_number":1081,"context_line":"        server, as that path will need everything after the \"/v1\"."},{"line_number":1082,"context_line":""},{"line_number":1083,"context_line":"        :returns: an unquoted string."},{"line_number":1084,"context_line":"        \"\"\""},{"line_number":1085,"context_line":"        _ver, entity_path \u003d self.split_path(1, 2, rest_with_last\u003dTrue)"},{"line_number":1086,"context_line":"        if entity_path is not None:"}],"source_content_type":"text/x-python","patch_set":2,"id":"c70f0bc6_80ee74a8","line":1083,"range":{"start_line":1083,"start_character":18,"end_line":1083,"end_character":36},"updated":"2026-01-30 18:55:39.000000000","message":"Maybe better as\n\n\u003e an unquoted native string\n\nto differentiate from what I\u0027ve taken to calling WSGI strings. Though I guess we have that up in the first line...","commit_id":"c3dc4554661355b38109e869445dccbb90110b32"},{"author":{"_account_id":15343,"name":"Tim Burke","email":"tburke@nvidia.com","username":"tburke"},"change_message_id":"ded325a1b81817016dff59be165e92fa5eff7b51","unresolved":true,"context_lines":[{"line_number":1183,"context_line":"                               trailing data, raises ValueError."},{"line_number":1184,"context_line":"        :returns: list of segments with a length of maxsegs (non-existent"},{"line_number":1185,"context_line":"                  segments will return as None). Each segment is an unquoted"},{"line_number":1186,"context_line":"                  string."},{"line_number":1187,"context_line":"        :raises ValueError: if given an invalid path"},{"line_number":1188,"context_line":"        \"\"\""},{"line_number":1189,"context_line":"        return split_path("}],"source_content_type":"text/x-python","patch_set":2,"id":"43a6a741_22733504","line":1186,"updated":"2026-01-30 18:55:39.000000000","message":"*WSGI* string, yeah? There\u0027s no `wsgi_to_str` call hiding somewhere?","commit_id":"c3dc4554661355b38109e869445dccbb90110b32"}],"swift/common/utils/logs.py":[{"author":{"_account_id":15343,"name":"Tim Burke","email":"tburke@nvidia.com","username":"tburke"},"change_message_id":"893e4f0cdeb8dd4d312548df4666b61fca37c4b4","unresolved":true,"context_lines":[{"line_number":856,"context_line":""},{"line_number":857,"context_line":"    :param default: a string to use as a replacement for missing elements; the"},{"line_number":858,"context_line":"        default value is the empty string."},{"line_number":859,"context_line":"    :param quote: if True then elements containing whitespace will be"},{"line_number":860,"context_line":"        percent-encoded; default is False."},{"line_number":861,"context_line":"    \"\"\""},{"line_number":862,"context_line":"    def __init__(self, default\u003d\u0027\u0027, quote\u003dFalse):"}],"source_content_type":"text/x-python","patch_set":2,"id":"750c527a_04cd800e","line":859,"range":{"start_line":859,"start_character":51,"end_line":859,"end_character":61},"updated":"2026-01-29 20:53:51.000000000","message":"Like, tabs, too? Newlines? Non-breaking spaces?","commit_id":"c3dc4554661355b38109e869445dccbb90110b32"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"a3b9b3da6699f5a8a263ab1b02cfc90b30703ace","unresolved":true,"context_lines":[{"line_number":856,"context_line":""},{"line_number":857,"context_line":"    :param default: a string to use as a replacement for missing elements; the"},{"line_number":858,"context_line":"        default value is the empty string."},{"line_number":859,"context_line":"    :param quote: if True then elements containing whitespace will be"},{"line_number":860,"context_line":"        percent-encoded; default is False."},{"line_number":861,"context_line":"    \"\"\""},{"line_number":862,"context_line":"    def __init__(self, default\u003d\u0027\u0027, quote\u003dFalse):"}],"source_content_type":"text/x-python","patch_set":2,"id":"2f4a72fe_d5e7bff1","line":859,"range":{"start_line":859,"start_character":51,"end_line":859,"end_character":61},"in_reply_to":"1686b13c_6ea8f30b","updated":"2026-02-02 16:45:46.000000000","message":"\u003e if self.quote and quote(unquote(log), \u0027:/{}\u0027) !\u003d log:\n\nThis crossed my mind, but it doesn\u0027t work because of the path being quoted but query string still having \u0027unsafe\u0027 characters:\n\n```\nimport swift.common.swob as swob\nimport urllib.parse as up\nreq\u003dswob.Request({\u0027PATH_INFO\u0027: \u0027a\u003db\u0027, \u0027QUERY_STRING\u0027: \u0027c\u003dd\u0027})\nreq.path\n\u0027a%3Db\u0027\nreq.path_qs\n\u0027a%3Db?c\u003dd\u0027\nup.quote(up.unquote(req.path_qs))\n\u0027a%3Db%3Fc%3Dd\u0027\n```\n\nI\u0027m not sure there is a way to continue to override string.Formatter and selectively quote some fields but not ``path``. By the time we reach ``format_field`` we have lost sight of the key for the format values.\n\nQuoting every field (or even just some fields) except ``path`` before it is passed to the formatter doesn\u0027t work because the fields aren\u0027t necessarily strings until they have passed through the formatter :/ (e.g. anonymized fields)","commit_id":"c3dc4554661355b38109e869445dccbb90110b32"},{"author":{"_account_id":15343,"name":"Tim Burke","email":"tburke@nvidia.com","username":"tburke"},"change_message_id":"ded325a1b81817016dff59be165e92fa5eff7b51","unresolved":true,"context_lines":[{"line_number":856,"context_line":""},{"line_number":857,"context_line":"    :param default: a string to use as a replacement for missing elements; the"},{"line_number":858,"context_line":"        default value is the empty string."},{"line_number":859,"context_line":"    :param quote: if True then elements containing whitespace will be"},{"line_number":860,"context_line":"        percent-encoded; default is False."},{"line_number":861,"context_line":"    \"\"\""},{"line_number":862,"context_line":"    def __init__(self, default\u003d\u0027\u0027, quote\u003dFalse):"}],"source_content_type":"text/x-python","patch_set":2,"id":"1686b13c_6ea8f30b","line":859,"range":{"start_line":859,"start_character":51,"end_line":859,"end_character":61},"in_reply_to":"6bedbff5_89553de6","updated":"2026-01-30 18:55:39.000000000","message":"I guess I\u0027m not sure why we want to use some ill-defined heuristic to decide whether to respect `self.quote` or not. It feels off, which is usually a sign we\u0027re trying to put the fix in the wrong place.\n\nEven with a condition more like\n```\nif self.quote and quote(unquote(log), \u0027:/{}\u0027) !\u003d log:\n```\nI\u0027m not sure I like it, though -- it\u0027s so LBYL...","commit_id":"c3dc4554661355b38109e869445dccbb90110b32"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"ba44967570118fbaf5b318b181e1c8f159230ced","unresolved":true,"context_lines":[{"line_number":856,"context_line":""},{"line_number":857,"context_line":"    :param default: a string to use as a replacement for missing elements; the"},{"line_number":858,"context_line":"        default value is the empty string."},{"line_number":859,"context_line":"    :param quote: if True then elements containing whitespace will be"},{"line_number":860,"context_line":"        percent-encoded; default is False."},{"line_number":861,"context_line":"    \"\"\""},{"line_number":862,"context_line":"    def __init__(self, default\u003d\u0027\u0027, quote\u003dFalse):"}],"source_content_type":"text/x-python","patch_set":2,"id":"6bedbff5_89553de6","line":859,"range":{"start_line":859,"start_character":51,"end_line":859,"end_character":61},"in_reply_to":"750c527a_04cd800e","updated":"2026-01-30 14:34:31.000000000","message":"yeah this is clearly not sufficient :/","commit_id":"c3dc4554661355b38109e869445dccbb90110b32"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"6e0fbf9e0b8761689e213f7fc6636f3e58b65dbd","unresolved":true,"context_lines":[{"line_number":869,"context_line":"            return self.default"},{"line_number":870,"context_line":"        else:"},{"line_number":871,"context_line":"            log \u003d super(LogStringFormatter, self).format_field(value, spec)"},{"line_number":872,"context_line":"            if self.quote and \u0027 \u0027 in log:"},{"line_number":873,"context_line":"                return quote(log, \u0027:/{}\u0027)"},{"line_number":874,"context_line":"            else:"},{"line_number":875,"context_line":"                return log"}],"source_content_type":"text/x-python","patch_set":2,"id":"a96945ca_c82e6da1","line":872,"updated":"2026-01-29 17:43:37.000000000","message":"this was subtle - i had trouble spotting the behavior change among the doc string updates.","commit_id":"c3dc4554661355b38109e869445dccbb90110b32"}],"test/unit/common/utils/test_logs.py":[{"author":{"_account_id":15343,"name":"Tim Burke","email":"tburke@nvidia.com","username":"tburke"},"change_message_id":"893e4f0cdeb8dd4d312548df4666b61fca37c4b4","unresolved":true,"context_lines":[{"line_number":1042,"context_line":"        lf \u003d utils.LogStringFormatter(quote\u003dTrue)"},{"line_number":1043,"context_line":"        self.assertEqual(lf.format(\u0027{a} {b}\u0027, a\u003d\u0027Swift est\u0027,"},{"line_number":1044,"context_line":"                                   b\u003d\u0027g%C3%A9nial\u0027),"},{"line_number":1045,"context_line":"                         \u0027Swift%20est g%C3%A9nial\u0027)"},{"line_number":1046,"context_line":""},{"line_number":1047,"context_line":"    def test_str_anonymizer(self):"},{"line_number":1048,"context_line":"        anon \u003d utils.StrAnonymizer(\u0027Swift is great!\u0027, \u0027md5\u0027, \u0027\u0027)"}],"source_content_type":"text/x-python","patch_set":2,"id":"42e3461d_21752d16","line":1045,"updated":"2026-01-29 20:53:51.000000000","message":"IDK that I like this -- there\u0027s no way, given a log line like `Swift%20est g%C3%A9nial`, to know whether the value of `a` passed to logging was `Swift est` or `Swift%20est`. Reversibility seems like a good/useful property for logging transformations.\n\nRelatedly: do we think there\u0027s value in knowing whether your clients are sending **un**quoted URL paths? With this, I think we lose that signal...","commit_id":"c3dc4554661355b38109e869445dccbb90110b32"},{"author":{"_account_id":15343,"name":"Tim Burke","email":"tburke@nvidia.com","username":"tburke"},"change_message_id":"ded325a1b81817016dff59be165e92fa5eff7b51","unresolved":true,"context_lines":[{"line_number":1042,"context_line":"        lf \u003d utils.LogStringFormatter(quote\u003dTrue)"},{"line_number":1043,"context_line":"        self.assertEqual(lf.format(\u0027{a} {b}\u0027, a\u003d\u0027Swift est\u0027,"},{"line_number":1044,"context_line":"                                   b\u003d\u0027g%C3%A9nial\u0027),"},{"line_number":1045,"context_line":"                         \u0027Swift%20est g%C3%A9nial\u0027)"},{"line_number":1046,"context_line":""},{"line_number":1047,"context_line":"    def test_str_anonymizer(self):"},{"line_number":1048,"context_line":"        anon \u003d utils.StrAnonymizer(\u0027Swift is great!\u0027, \u0027md5\u0027, \u0027\u0027)"}],"source_content_type":"text/x-python","patch_set":2,"id":"a8aa00bb_3bee4404","line":1045,"in_reply_to":"3ba9b4c4_db584a9f","updated":"2026-01-30 18:55:39.000000000","message":"[Eventlet](https://github.com/eventlet/eventlet/blob/0.40.4/eventlet/wsgi.py#L755), you mean. But point taken; I think s3api is the only one that looks at `RAW_PATH_INFO`...\n\nThis all still rubs me the wrong way, though -- I was expecting a new config option for proxy-logging that\u0027d just toggle whether to pass `quote\u003dTrue` or `quote\u003dFalse`...\n\nOr rather, whether the *path components* should get quoted... hmm... Stupid user headers...","commit_id":"c3dc4554661355b38109e869445dccbb90110b32"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"ba44967570118fbaf5b318b181e1c8f159230ced","unresolved":true,"context_lines":[{"line_number":1042,"context_line":"        lf \u003d utils.LogStringFormatter(quote\u003dTrue)"},{"line_number":1043,"context_line":"        self.assertEqual(lf.format(\u0027{a} {b}\u0027, a\u003d\u0027Swift est\u0027,"},{"line_number":1044,"context_line":"                                   b\u003d\u0027g%C3%A9nial\u0027),"},{"line_number":1045,"context_line":"                         \u0027Swift%20est g%C3%A9nial\u0027)"},{"line_number":1046,"context_line":""},{"line_number":1047,"context_line":"    def test_str_anonymizer(self):"},{"line_number":1048,"context_line":"        anon \u003d utils.StrAnonymizer(\u0027Swift is great!\u0027, \u0027md5\u0027, \u0027\u0027)"}],"source_content_type":"text/x-python","patch_set":2,"id":"3ba9b4c4_db584a9f","line":1045,"in_reply_to":"42e3461d_21752d16","updated":"2026-01-30 14:34:31.000000000","message":"IIUC swob.Request normalises the path (see https://review.opendev.org/c/openstack/swift/+/975150/2/test/unit/common/test_swob.py#595) so I\u0027m not sure we get that signal (but I could be wrong).","commit_id":"c3dc4554661355b38109e869445dccbb90110b32"}]}
