{
 "number": 34038,
 "repo": "bitcoin/bitcoin",
 "url": "https://github.com/bitcoin/bitcoin/pull/34038",
 "title": "logging: replace -loglevel with -trace, expose trace logging via RPC",
 "author": "ajtowns",
 "author_association": "MEMBER",
 "created_at": "2025-12-10T05:56:20Z",
 "updated_at": "2026-09-17T07:25:19Z",
 "age_days": 281,
 "draft": false,
 "labels": [],
 "milestone": null,
 "base": "master",
 "head_sha": "befe8183dcdbb7e5ee4502b1391b1be6cc20324f",
 "head_ref": "202512-log-niceties",
 "head_repo": "ajtowns/bitcoin",
 "head_history": [
  {
   "t": "2025-12-10T06:00:33Z",
   "sha": "90d6db5c2188144bad63a09cfa1330c099aed3b3"
  },
  {
   "t": "2025-12-10T06:08:38Z",
   "sha": "1df56912a208d5050c6a28632c67ff292634a292"
  },
  {
   "t": "2025-12-10T20:55:53Z",
   "sha": "77f272c094b2ef1ff4914e62a280bc88d6a16aad"
  },
  {
   "t": "2025-12-10T23:26:39Z",
   "sha": "bee6ec563a98be1c78529fabe07ac462c92a68e2"
  },
  {
   "t": "2025-12-11T03:47:10Z",
   "sha": "46a8129d08b40e76abd6467667d0fe555a7f9799"
  },
  {
   "t": "2025-12-11T03:48:17Z",
   "sha": "2f71e4138afd9ed343506c0f1f811804e5ee997f"
  },
  {
   "t": "2025-12-11T03:49:38Z",
   "sha": "1f465319e89833e16ac54e7b2c577f3803473946"
  },
  {
   "t": "2025-12-11T08:11:46Z",
   "sha": "e4d30ec19ec15f5195fd3410f57ed6673d82f46c"
  },
  {
   "t": "2025-12-11T14:10:16Z",
   "sha": "d18459500bd684ebba8eab859e2008f445821971"
  },
  {
   "t": "2025-12-15T03:57:00Z",
   "sha": "2bdaf024fc82b995d936bcb6ae001ff462091fd9"
  },
  {
   "t": "2025-12-20T05:19:08Z",
   "sha": "c6635583e433a22f541d531bfed94df19fb48d02"
  },
  {
   "t": "2026-01-23T10:27:31Z",
   "sha": "f5a80c45b76e9b4203f283a7bda8b373e1fd1668"
  },
  {
   "t": "2026-01-28T04:41:23Z",
   "sha": "9b38dc78ac74f438be25d04b787b2f47a3b76f40"
  },
  {
   "t": "2026-01-29T01:13:31Z",
   "sha": "103fa7597e6ce9653d0419126d262676afe413c5"
  },
  {
   "t": "2026-02-11T03:28:08Z",
   "sha": "019f69364aa707f4401864669ea39c86755f6263"
  },
  {
   "t": "2026-02-11T03:43:03Z",
   "sha": "0a7dc95942035f9ced35630196c21a3c9a1f3b32"
  },
  {
   "t": "2026-02-20T12:10:24Z",
   "sha": "a98dae5c1caa543b20720db850af09145d401890"
  },
  {
   "t": "2026-03-11T15:28:48Z",
   "sha": "408a8d070b40d029510595e5ea979127fb0e09a5"
  },
  {
   "t": "2026-03-11T16:18:07Z",
   "sha": "e54609aedc4fd2cb28aed0708f2a15c1bdc739cf"
  },
  {
   "t": "2026-03-20T05:38:28Z",
   "sha": "df51f8d13c26c7255caf3211bcb7bc258c80dd01"
  },
  {
   "t": "2026-03-24T13:03:09Z",
   "sha": "6c2dc24e08c936c0a8b465782543a1c97c9e5033"
  },
  {
   "t": "2026-04-02T05:38:33Z",
   "sha": "6eee953bb8ee6fe465838fa7ce6cb4ff64ade543"
  },
  {
   "t": "2026-04-02T18:21:10Z",
   "sha": "286ef780fd77f3f30d56e69a8a7f83c1736ea87f"
  },
  {
   "t": "2026-05-07T13:26:41Z",
   "sha": "5b114839c24f2298cd57c3d724f85f39400d6832"
  },
  {
   "t": "2026-05-22T03:12:32Z",
   "sha": "77ac0f81cd90234667ef7a2b7c05d53fb1239bd2"
  },
  {
   "t": "2026-06-25T04:48:06Z",
   "sha": "df78e3bf12ab77ed1d7770e5369d87f20fe62c61"
  },
  {
   "t": "2026-06-26T02:57:57Z",
   "sha": "a9f22f598be947a31cd04fba1fae8027d90326d6"
  },
  {
   "t": "2026-07-03T07:08:50Z",
   "sha": "befe8183dcdbb7e5ee4502b1391b1be6cc20324f"
  }
 ],
 "additions": 266,
 "deletions": 132,
 "changed_files": 16,
 "commit_count": 3,
 "size_bucket": "M",
 "mergeable_state": "clean",
 "bot": {
  "drahtbot": {
   "present": true,
   "reviews": {
    "ack": [
     {
      "login": "stickies-v",
      "url": "https://github.com/bitcoin/bitcoin/pull/34038#pullrequestreview-4626374227"
     },
     {
      "login": "sedited",
      "url": "https://github.com/bitcoin/bitcoin/pull/34038#pullrequestreview-4629218802"
     }
    ],
    "approach_nack": [
     {
      "login": "ryanofsky",
      "url": "https://github.com/bitcoin/bitcoin/pull/34038#issuecomment-4976442638"
     }
    ]
   },
   "conflicts": [
    {
     "number": 36257,
     "title": "qa: assert_equals -> assert_true/assert_false",
     "author": "hodlinator"
    },
    {
     "number": 36167,
     "title": "[RFC] Enable `-Wunused`",
     "author": "fanquake"
    },
    {
     "number": 35387,
     "title": "logging: make trace logging easily usable",
     "author": "ryanofsky"
    },
    {
     "number": 35322,
     "title": "logging: streamline Logger state and drop redundant methods",
     "author": "ryanofsky"
    },
    {
     "number": 17783,
     "title": "common: Disallow calling IsArgSet() on ALLOW_LIST options",
     "author": "ryanofsky"
    },
    {
     "number": 17581,
     "title": "refactor: Remove settings merge reverse precedence code",
     "author": "ryanofsky"
    },
    {
     "number": 17580,
     "title": "refactor: Add ALLOW_LIST flags and enforce usage in CheckArgFlags",
     "author": "ryanofsky"
    },
    {
     "number": 17493,
     "title": "util: Forbid ambiguous multiple assignments in config file",
     "author": "ryanofsky"
    }
   ]
  }
 },
 "acks_parsed": {
  "stickies-v": {
   "kind": "ack",
   "hash": "befe8183dcdbb7e5ee4502b1391b1be6cc20324f",
   "t": "2026-07-03T16:04:10Z",
   "stale": false
  },
  "ryanofsky": {
   "kind": "concept_ack",
   "hash": null,
   "t": "2026-07-15T03:02:23Z",
   "stale": false
  },
  "sedited": {
   "kind": "ack",
   "hash": "befe8183dcdbb7e5ee4502b1391b1be6cc20324f",
   "t": "2026-07-04T08:55:30Z",
   "stale": false
  }
 },
 "acks_tally": {
  "ack": 2,
  "stale_ack": 0,
  "concept_ack": 1,
  "approach_ack": 0,
  "nack": 0,
  "concept_nack": 0,
  "approach_nack": 0
 },
 "reviews": {
  "approved": 5,
  "changes_requested": 0,
  "distinct_reviewers": [
   "abo-L",
   "maflcko",
   "ryanofsky",
   "sedited",
   "stickies-v"
  ]
 },
 "signals": {
  "needs_rebase": false,
  "ci_failed": false,
  "mergeable_state": "clean",
  "last_author_activity": "2026-08-09T16:48:58Z",
  "last_reviewer_activity": "2026-08-21T11:18:46Z",
  "last_reviewer": "maflcko",
  "author_silent_days": 38,
  "waiting_on_author_days": 27,
  "days_since_update": 0
 },
 "refs": {
  "mentioned": [
   21283,
   30486,
   34267,
   34417,
   34465,
   34806,
   34809,
   35322,
   35387,
   35597
  ],
  "depends_on": [],
  "fixes": [],
  "linked_issues": [],
  "references": [
   {
    "number": 21283,
    "type": "pull",
    "state": "closed",
    "merged": true,
    "merged_at": "2026-05-05",
    "title": "Implement BIP 370 PSBTv2"
   },
   {
    "number": 30486,
    "type": "issue",
    "state": "open",
    "merged": false,
    "merged_at": null,
    "title": "Logging controls"
   },
   {
    "number": 34267,
    "type": "pull",
    "state": "closed",
    "merged": true,
    "merged_at": "2026-01-27",
    "title": "net: avoid unconditional `privatebroadcast` logging (+ warn for debug logs)"
   },
   {
    "number": 34417,
    "type": "pull",
    "state": "closed",
    "merged": true,
    "merged_at": "2026-01-28",
    "title": "log: Print warning about privacy-sensitive log info unconditionally"
   },
   {
    "number": 34465,
    "type": "pull",
    "state": "closed",
    "merged": true,
    "merged_at": "2026-02-07",
    "title": "refactor: separate log generation from log handling"
   },
   {
    "number": 34806,
    "type": "pull",
    "state": "closed",
    "merged": true,
    "merged_at": "2026-05-22",
    "title": "refactor: logging: Various API improvements"
   },
   {
    "number": 34809,
    "type": "pull",
    "state": "closed",
    "merged": true,
    "merged_at": "2026-03-24",
    "title": "threadsafety: Add STDLOCK() macro for StdMutex"
   },
   {
    "number": 35322,
    "type": "pull",
    "state": "open",
    "merged": false,
    "merged_at": null,
    "title": "logging: streamline Logger state and drop redundant methods"
   },
   {
    "number": 35387,
    "type": "pull",
    "state": "open",
    "merged": false,
    "merged_at": null,
    "title": "logging: make trace logging easily usable"
   },
   {
    "number": 35597,
    "type": "pull",
    "state": "closed",
    "merged": true,
    "merged_at": "2026-06-30",
    "title": "logging: More fully remove libevent log category"
   }
  ],
  "conflicts": [
   36257,
   36167,
   35387,
   35322,
   17783,
   17581,
   17580,
   17493
  ]
 },
 "stack": {
  "shares_commits_with": [],
  "based_on": [],
  "base_for": []
 },
 "review_paths": [
  "doc/release-notes-34038.md",
  "src/init/common.cpp",
  "src/logging.cpp",
  "src/logging.h",
  "src/rpc/node.cpp",
  "src/test/logging_tests.cpp",
  "src/threadsafety.h",
  "test/functional/feature_logging.py",
  "test/functional/rpc_misc.py",
  "test/functional/test_framework/test_node.py"
 ],
 "body": "* A new -trace option controls trace-level logging, using the same syntax as -debug. So `-debug=net` enables debug logs for net, `-trace=net` adds trace logs too. This replaces the `-loglevel` option, which had different syntax and had to be combined with `-debug` to take effect.\n * The logging RPC gains trace support: a new trace parameter enables trace-level logging per category, and the output now distinguishes excluded/debug/trace categories. (`-deprecatedrpc=logging` preserves the old format.)",
 "commits": [
  {
   "sha": "b25dacba1d145101ff2764d60edf96b95a225569",
   "date": "2026-07-03T06:06:16Z",
   "message": "logging: replace -loglevel arg with -trace\n\nPreviously, trace logging for a category was enabled by saying \"-debug=net\n-loglevel=net:trace\" and saying \"-loglevel=net:trace\" (without \"-debug\"\nor \"-debug=net\") would silently do nothing. Simplify this so that\n\"-debug=net\" enables debug logging for net and \"-trace=net\" enables\nboth debug and trace logging for net.\n\nNote that \"-nodebug\" and \"-notrace\" only disable previous \"-debug=xxx\" and\n\"-trace=yyy\" arguments, so \"-debug=mempool -trace=net -nodebug\" will cancel\n\"-debug=mempool\", but will still include both debug and trace logs for net."
  },
  {
   "sha": "5a14f8f5d269a5737b62b413c50481581490494e",
   "date": "2026-07-03T06:59:50Z",
   "message": "rpc: Update logging RPC to support trace level debugging\n\nAlso changes the output format, to distinguish debug/trace levels.\nProvides `-deprecatedrpc=logging` if old output format is needed.\n\nCo-Authored-By: stickies-v <stickies-v@protonmail.com>"
  },
  {
   "sha": "befe8183dcdbb7e5ee4502b1391b1be6cc20324f",
   "date": "2026-07-03T06:59:50Z",
   "message": "[doc] Release notes for user-visible logging changes"
  }
 ],
 "timeline": [
  {
   "t": "2025-12-10T06:00:33Z",
   "kind": "force_push",
   "who": "ajtowns",
   "commit": "90d6db5c2188144bad63a09cfa1330c099aed3b3"
  },
  {
   "t": "2025-12-10T06:04:15Z",
   "kind": "comment",
   "who": "ajtowns",
   "assoc": "MEMBER",
   "text": "The reason for the locking change for `ShrinkDebugFile` is that the following commit is enough for clang to figure out that the `LogWarning(\"couldn't shrink\")` message would take the lock, and thus that ShrinkDebugFile should be annotated with `!m_cs`. But thinking about it, it seemed like it would make more sense to hold the lock and pause any attempted logging until the shrinking was finished anyway, so that's what I did. Because we only shrink on startup, before calling StartLogging, I don't think it has much real impact."
  },
  {
   "t": "2025-12-10T06:08:38Z",
   "kind": "force_push",
   "who": "ajtowns",
   "commit": "1df56912a208d5050c6a28632c67ff292634a292"
  },
  {
   "t": "2025-12-10T11:19:23Z",
   "kind": "review_comment",
   "who": "stickies-v",
   "assoc": "MEMBER",
   "path": "src/rpc/node.cpp",
   "commit": "befe8183dcdbb7e5ee4502b1391b1be6cc20324f",
   "in_reply_to": null,
   "text": "Since we're doing a potentially breaking change (with `-deprecatedrpc`) here already, what do you think about renaming \"include\" to \"debug\"? Also, the RPC documentation wrt evaluation order needs to be updated a bit to reflect the debug -> trace -> exclude evaluation order."
  },
  {
   "t": "2025-12-10T11:37:51Z",
   "kind": "review",
   "who": "stickies-v",
   "assoc": "MEMBER",
   "state": "COMMENTED",
   "commit": "1df56912a208d5050c6a28632c67ff292634a292",
   "text": "Concept ACK. Both the new `BCLog::NO_RATE_LIMIT` and `-loglevel` changes make for a more natural and ergonomic interface for both developers and users, and the changes required are limited enough. The breaking RPC change is probably the biggest downside/cost of this PR."
  },
  {
   "t": "2025-12-10T20:55:53Z",
   "kind": "force_push",
   "who": "ajtowns",
   "commit": "77f272c094b2ef1ff4914e62a280bc88d6a16aad"
  },
  {
   "t": "2025-12-10T21:00:38Z",
   "kind": "review_comment",
   "who": "ajtowns",
   "assoc": "MEMBER",
   "path": "src/rpc/node.cpp",
   "commit": "befe8183dcdbb7e5ee4502b1391b1be6cc20324f",
   "in_reply_to": 2606253417,
   "text": "Seems like a fine idea, but not sure how you'd provide compatibility for `include=[\"net\"]` with deprecaterpc?"
  },
  {
   "t": "2025-12-10T22:26:25Z",
   "kind": "review_comment",
   "who": "stickies-v",
   "assoc": "MEMBER",
   "path": "src/rpc/node.cpp",
   "commit": "befe8183dcdbb7e5ee4502b1391b1be6cc20324f",
   "in_reply_to": 2606253417,
   "text": "I think this should work?\n\ngit diff on 77f272c094\n\n```diff\ndiff --git a/src/rpc/client.cpp b/src/rpc/client.cpp\nindex 0693b60557..25c313c552 100644\n--- a/src/rpc/client.cpp\n+++ b/src/rpc/client.cpp\n@@ -325,7 +325,7 @@ static const CRPCConvertParam vRPCConvertParams[] =\n     { \"psbtbumpfee\", 1, \"replaceable\"},\n     { \"psbtbumpfee\", 1, \"outputs\"},\n     { \"psbtbumpfee\", 1, \"original_change_index\"},\n-    { \"logging\", 0, \"include\" },\n+    { \"logging\", 0, \"debug\" },\n     { \"logging\", 1, \"exclude\" },\n     { \"logging\", 2, \"trace\" },\n     { \"disconnectnode\", 1, \"nodeid\" },\ndiff --git a/src/rpc/node.cpp b/src/rpc/node.cpp\nindex abca5e69bd..06714bde37 100644\n--- a/src/rpc/node.cpp\n+++ b/src/rpc/node.cpp\n@@ -225,20 +225,22 @@ static void EnableOrDisableLogCategories(UniValue cats, BCLog::Level new_level)\n\n static RPCHelpMan logging()\n {\n+    const bool use_deprecated = IsDeprecatedRPCEnabled(\"logging\");\n+    const std::string debug_category_name{use_deprecated ? \"include\" : \"debug\"};\n     return RPCHelpMan{\"logging\",\n             \"Gets and sets the logging configuration.\\n\"\n             \"When called without an argument, returns the the status of the supported logging categories.\\n\"\n             \"When called with arguments, adds or removes categories from debug or trace logging and return the lists above.\\n\"\n-            \"The arguments are evaluated in order \\\"include\\\", \\\"trace\\\", \\\"exclude\\\".\\n\"\n-            \"If an item is both included/traced and excluded, it will thus end up being excluded.\\n\"\n+            \"The arguments are evaluated in order \\\"\" + debug_category_name + \"\\\", \\\"trace\\\", \\\"exclude\\\".\\n\"\n+            \"If an item is debug and/or trace but also excluded, it will thus end up being excluded.\\n\"\n             \"The valid logging categories are: \" + LogInstance().LogCategoriesString() + \"\\n\"\n             \"In addition, the following are available as category names with special meanings:\\n\"\n             \"  - \\\"all\\\",  \\\"1\\\" : represent all logging categories.\\n\"\n             ,\n                 {\n-                    {\"include\", RPCArg::Type::ARR, RPCArg::Optional::OMITTED, \"The categories to add to debug logging\",\n+                    {debug_category_name, RPCArg::Type::ARR, RPCArg::Optional::OMITTED, \"The categories to add to debug logging\",\n                         {\n-                            {\"include_category\", RPCArg::Type::STR, RPCArg::Optional::OMITTED, \"the valid logging category\"},\n+                            {\"debug_category\", RPCArg::Type::STR, RPCArg::Optional::OMITTED, \"the debug logging category\"},\n                         }},\n                     {\"exclude\", RPCArg::Type::ARR, RPCArg::Optional::OMITTED, \"The categories to remove from debug logging\",\n                         {\n@@ -249,14 +251,14 @@ static RPCHelpMan logging()\n                             {\"trace_category\", RPCArg::Type::STR, RPCArg::Optional::OMITTED, \"the valid logging category\"},\n                         }},\n                 },\n-                {\n-                    RPCResult{\"If -deprecatedrpc=logging is set\",\n+                {   use_deprecated ?\n+                    RPCResult{\n                         RPCResult::Type::OBJ_DYN, \"\", \"keys are the logging categories, and values indicates its status\",\n                         {\n                             {RPCResult::Type::BOOL, \"category\", \"if being debug logged or not. false:inactive, true:active\"},\n                         }\n-                    },\n-                    RPCResult{\"Otherwise\",\n+                    } :\n+                    RPCResult{\n                         RPCResult::Type::OBJ, \"\", \"\",\n                         {{\n                             {RPCResult::Type::ARR, \"excluded\", \"excluded categories\", {{RPCResult::Type::STR, \"\", \"\"}}},\n@@ -289,7 +291,6 @@ static RPCHelpMan logging()\n         UpdateHTTPServerLogging(LogInstance().WillLogCategory(BCLog::LIBEVENT));\n     }\n\n-    bool use_deprecated = IsDeprecatedRPCEnabled(\"logging\");\n     UniValue result(UniValue::VOBJ);\n     if (use_deprecated) {\n         for (const auto& info : LogInstance().LogCategoriesInfo()) {\ndiff --git a/test/functional/rpc_misc.py b/test/functional/rpc_misc.py\nindex fa1de774b7..4dfe13ec99 100755\n--- a/test/functional/rpc_misc.py\n+++ b/test/functional/rpc_misc.py\n@@ -80,10 +80,12 @@ class RpcMiscTest(BitcoinTestFramework):\n         assert('qt' in node.logging()['trace'])\n         node.logging(exclude=['qt'])\n         assert('qt' in node.logging()['excluded'])\n-        node.logging(include=['qt'])\n+        node.logging(debug=['qt'])\n         assert('qt' in node.logging()['debug'])\n         node.logging(trace=['qt'])\n         assert('qt' in node.logging()['trace'])\n+        # \"include\" is now deprecated in favour of \"debug\"\n+        assert_raises_rpc_error(-8, \"Unknown named parameter include\", node.logging, include=\"net\")\n\n         # Test logging RPC returns the logging categories in alphabetical order.\n         logging = node.logging()\n@@ -125,6 +127,10 @@ class RpcMiscTest(BitcoinTestFramework):\n         # Specifying an unknown index name returns an empty result\n         assert_equal(node.getindexinfo(\"foo\"), {})\n\n+        # Test that deprecatedrpc=\"logging\" maintains behaviour\n+        self.restart_node(0, [\"-deprecatedrpc=logging\"])\n+        node.logging(include=[\"net\"])\n+        assert_raises_rpc_error(-8, \"Unknown named parameter debug\", node.logging, debug=\"net\")\n\n if __name__ == '__main__':\n     RpcMiscTest(__file__).main()\n\n```"
  },
  {
   "t": "2025-12-10T23:26:39Z",
   "kind": "force_push",
   "who": "ajtowns",
   "commit": "bee6ec563a98be1c78529fabe07ac462c92a68e2"
  },
  {
   "t": "2025-12-11T03:47:10Z",
   "kind": "force_push",
   "who": "ajtowns",
   "commit": "46a8129d08b40e76abd6467667d0fe555a7f9799"
  },
  {
   "t": "2025-12-11T03:47:13Z",
   "kind": "review_comment",
   "who": "ajtowns",
   "assoc": "MEMBER",
   "path": "src/rpc/node.cpp",
   "commit": "befe8183dcdbb7e5ee4502b1391b1be6cc20324f",
   "in_reply_to": 2606253417,
   "text": "Nice. Left the RPCResult text including both forms of output, as that makes it a little more self-documenting that people can use the deprecatedrpc option if desired. Also left `include` in the client side so that bitcoin-cli still works with old bitcoinds or bitcoinds running deprecatedrpc=logging."
  },
  {
   "t": "2025-12-11T03:48:17Z",
   "kind": "force_push",
   "who": "ajtowns",
   "commit": "2f71e4138afd9ed343506c0f1f811804e5ee997f"
  },
  {
   "t": "2025-12-11T03:49:38Z",
   "kind": "force_push",
   "who": "ajtowns",
   "commit": "1f465319e89833e16ac54e7b2c577f3803473946"
  },
  {
   "t": "2025-12-11T08:11:46Z",
   "kind": "force_push",
   "who": "ajtowns",
   "commit": "e4d30ec19ec15f5195fd3410f57ed6673d82f46c"
  },
  {
   "t": "2025-12-11T10:57:13Z",
   "kind": "review_comment",
   "who": "stickies-v",
   "assoc": "MEMBER",
   "path": "src/rpc/node.cpp",
   "commit": "befe8183dcdbb7e5ee4502b1391b1be6cc20324f",
   "in_reply_to": 2606253417,
   "text": "[quoted text omitted]\n\nI'm not sure that's feasible without also adding a `-deprecatedrpc` option to `cli` (unless that already exists), which I don't think is worth it? Afaik we don't support disjoint `bitcoind` and `bitcoin-cli` versions, and I think `bitcoin-cli` is more for manual usage anyway at which point just typing \"debug\" instead of \"include\" is trivial? The diff I added passes all tests (locally) and also adds functional tests to ensure both the current and the deprecated approach work fine over RPC (i.e. without cli)."
  },
  {
   "t": "2025-12-11T14:10:16Z",
   "kind": "force_push",
   "who": "ajtowns",
   "commit": "d18459500bd684ebba8eab859e2008f445821971"
  },
  {
   "t": "2025-12-12T23:56:44Z",
   "kind": "review_comment",
   "who": "ajtowns",
   "assoc": "MEMBER",
   "path": "src/rpc/node.cpp",
   "commit": "befe8183dcdbb7e5ee4502b1391b1be6cc20324f",
   "in_reply_to": 2606253417,
   "text": "Changed it to just support both include or debug as aliases indefinitely. (Can remove the `include=` alias when the deprecatedrpc option is removed)\n\nI don't think it makes sense to ship a bitcoind/bitcoin-cli pair that don't work together under some configuration param (ie `bitcoin-cli -named include=\"['net']\"` doesn't work because cli won't parse it as json, and `debug=[\"net\"]` won't work because you're running with `deprecatedrpc=logging`)"
  },
  {
   "t": "2025-12-15T03:57:00Z",
   "kind": "force_push",
   "who": "ajtowns",
   "commit": "2bdaf024fc82b995d936bcb6ae001ff462091fd9"
  },
  {
   "t": "2025-12-15T13:52:10Z",
   "kind": "review",
   "who": "abo-L",
   "assoc": "NONE",
   "state": "COMMENTED",
   "commit": "2bdaf024fc82b995d936bcb6ae001ff462091fd9",
   "text": "bc1qhhml75wgm3a3303tuxyvhr74jr4jmhhk6qy5um"
  },
  {
   "t": "2025-12-20T05:19:08Z",
   "kind": "force_push",
   "who": "ajtowns",
   "commit": "c6635583e433a22f541d531bfed94df19fb48d02"
  },
  {
   "t": "2025-12-21T03:06:43Z",
   "kind": "comment",
   "who": "ajtowns",
   "assoc": "MEMBER",
   "text": "Rebased on top of other logging changes, added a release note"
  },
  {
   "t": "2026-01-02T08:05:21Z",
   "kind": "review_comment",
   "who": "stickies-v",
   "assoc": "MEMBER",
   "path": "src/threadsafety.h",
   "commit": "a98dae5c1caa543b20720db850af09145d401890",
   "in_reply_to": null,
   "text": "[quoted text omitted]\n\nI think this part of the docstring is still applicable after this PR, should we keep it?"
  },
  {
   "t": "2026-01-02T08:36:40Z",
   "kind": "review_comment",
   "who": "stickies-v",
   "assoc": "MEMBER",
   "path": "src/logging.h",
   "commit": "103fa7597e6ce9653d0419126d262676afe413c5",
   "in_reply_to": null,
   "text": "nit: can we multi-line these long signatures?"
  },
  {
   "t": "2026-01-02T08:54:06Z",
   "kind": "review_comment",
   "who": "stickies-v",
   "assoc": "MEMBER",
   "path": "src/logging.h",
   "commit": "408a8d070b40d029510595e5ea979127fb0e09a5",
   "in_reply_to": null,
   "text": "in commit 08a624736a: would be nice to get rid of the out-parameter\n\ngit diff on 08a624736a\n\n```diff\ndiff --git a/src/logging.cpp b/src/logging.cpp\nindex 494f513865..5765d8f322 100644\n--- a/src/logging.cpp\n+++ b/src/logging.cpp\n@@ -127,10 +127,11 @@ void BCLog::Logger::EnableCategory(BCLog::LogFlags flag)\n\n bool BCLog::Logger::EnableCategory(std::string_view str)\n {\n-    BCLog::LogFlags flag;\n-    if (!GetLogCategory(flag, str)) return false;\n-    EnableCategory(flag);\n-    return true;\n+    if (auto flag{GetLogCategory(str)}) {\n+        EnableCategory(*flag);\n+        return true;\n+    }\n+    return false;\n }\n\n void BCLog::Logger::DisableCategory(BCLog::LogFlags flag)\n@@ -140,10 +141,11 @@ void BCLog::Logger::DisableCategory(BCLog::LogFlags flag)\n\n bool BCLog::Logger::DisableCategory(std::string_view str)\n {\n-    BCLog::LogFlags flag;\n-    if (!GetLogCategory(flag, str)) return false;\n-    DisableCategory(flag);\n-    return true;\n+    if (auto flag{GetLogCategory(str)}) {\n+        DisableCategory(*flag);\n+        return true;\n+    }\n+    return false;\n }\n\n bool BCLog::Logger::WillLogCategory(BCLog::LogFlags category) const\n@@ -216,18 +218,16 @@ static const std::unordered_map<BCLog::LogFlags, std::string> LOG_CATEGORIES_BY_\n     }(LOG_CATEGORIES_BY_STR)\n };\n\n-bool BCLog::Logger::GetLogCategory(BCLog::LogFlags& flag, std::string_view str)\n+std::optional<BCLog::LogFlags> BCLog::Logger::GetLogCategory(std::string_view str)\n {\n     if (str.empty() || str == \"1\" || str == \"all\") {\n-        flag = BCLog::ALL;\n-        return true;\n+        return BCLog::ALL;\n     }\n     auto it = LOG_CATEGORIES_BY_STR.find(str);\n     if (it != LOG_CATEGORIES_BY_STR.end()) {\n-        flag = it->second;\n-        return true;\n+        return it->second;\n     }\n-    return false;\n+    return std::nullopt;\n }\n\n std::string BCLog::Logger::LogLevelToStr(BCLog::Level level)\n@@ -592,13 +592,13 @@ bool BCLog::Logger::SetLogLevel(std::string_view level_str)\n\n bool BCLog::Logger::SetCategoryLogLevel(std::string_view category_str, std::string_view level_str)\n {\n-    BCLog::LogFlags flag;\n-    if (!GetLogCategory(flag, category_str)) return false;\n+    auto flag{GetLogCategory(category_str)};\n+    if (!flag) return false;\n\n     const auto level = GetLogLevel(level_str);\n     if (!level.has_value() || level.value() > MAX_USER_SETABLE_SEVERITY_LEVEL) return false;\n\n     STDLOCK(m_cs);\n-    m_category_log_levels[flag] = level.value();\n+    m_category_log_levels[flag.value()] = level.value();\n     return true;\n }\ndiff --git a/src/logging.h b/src/logging.h\nindex 18c759daa4..d9c02e9e69 100644\n--- a/src/logging.h\n+++ b/src/logging.h\n@@ -380,7 +380,7 @@ namespace BCLog {\n         }\n\n         /** Return true if str parses as a log category and set the flag */\n-        static bool GetLogCategory(BCLog::LogFlags& flag, std::string_view str);\n+        static std::optional<BCLog::LogFlags> GetLogCategory(std::string_view str);\n     };\n } // namespace BCLog\n\ndiff --git a/src/test/logging_tests.cpp b/src/test/logging_tests.cpp\nindex 0e29fa314d..4de66bcc7a 100644\n--- a/src/test/logging_tests.cpp\n+++ b/src/test/logging_tests.cpp\n@@ -164,10 +164,10 @@ BOOST_FIXTURE_TEST_CASE(logging_LogPrintMacros_CategoryName, LogSetup)\n     std::vector<std::pair<BCLog::LogFlags, std::string>> expected_category_names;\n     const auto category_names = SplitString(concatenated_category_names, ',');\n     for (const auto& category_name : category_names) {\n-        BCLog::LogFlags category;\n         const auto trimmed_category_name = TrimString(category_name);\n-        BOOST_REQUIRE(BCLog::Logger::GetLogCategory(category, trimmed_category_name));\n-        expected_category_names.emplace_back(category, trimmed_category_name);\n+        auto category{BCLog::Logger::GetLogCategory(trimmed_category_name)};\n+        BOOST_REQUIRE(category);\n+        expected_category_names.emplace_back(*category, trimmed_category_name);\n     }\n\n     std::vector<std::string> expected;\n\n```"
  },
  {
   "t": "2026-01-02T09:07:02Z",
   "kind": "review_comment",
   "who": "stickies-v",
   "assoc": "MEMBER",
   "path": "src/threadsafety.h",
   "commit": "a98dae5c1caa543b20720db850af09145d401890",
   "in_reply_to": null,
   "text": "nit: since this fn is public, the name could be a bit confusing since this doesn't do any runtime checks, and the compile-time checks only work under specific circumstances. Maybe good to move this in `detail_` and/or add a brief docstring to avoid misuse?"
  },
  {
   "t": "2026-01-02T10:16:12Z",
   "kind": "review_comment",
   "who": "stickies-v",
   "assoc": "MEMBER",
   "path": "src/logging.cpp",
   "commit": "befe8183dcdbb7e5ee4502b1391b1be6cc20324f",
   "in_reply_to": null,
   "text": "Should we rename this to `DebugCategory` now for consistency?"
  },
  {
   "t": "2026-01-02T10:19:28Z",
   "kind": "review_comment",
   "who": "stickies-v",
   "assoc": "MEMBER",
   "path": "doc/release-notes-34038.md",
   "commit": "f565bbce26e52ffdbe3f8ecacd015d603d4eb05e",
   "in_reply_to": null,
   "text": "```suggestion\n  be accessed via `-trace=walletdb`. The `-loglevel` configuration option\n```"
  },
  {
   "t": "2026-01-02T10:39:34Z",
   "kind": "review_comment",
   "who": "stickies-v",
   "assoc": "MEMBER",
   "path": "src/logging.cpp",
   "commit": "befe8183dcdbb7e5ee4502b1391b1be6cc20324f",
   "in_reply_to": null,
   "text": "Shouldn't `DisableCategory` unset this, too?"
  },
  {
   "t": "2026-01-02T12:40:40Z",
   "kind": "review_comment",
   "who": "stickies-v",
   "assoc": "MEMBER",
   "path": "src/logging.h",
   "commit": "6eee953bb8ee6fe465838fa7ce6cb4ff64ade543",
   "in_reply_to": null,
   "text": "I'm a bit unsure about the merit of these functions. I think just using `SetCategoryLogLevel` is a bit more straightforward, which can also be simplified now that we don't have `-loglevel` anymore. I think the string overloads could also be removed, and move input validation/parsing to the edge rather than in `logging`.\n\ngit diff on f565bbce26\n\n```diff\ndiff --git a/src/bench/logging.cpp b/src/bench/logging.cpp\nindex 85e33b035e..c68206fd9b 100644\n--- a/src/bench/logging.cpp\n+++ b/src/bench/logging.cpp\n@@ -18,7 +18,7 @@\n static void Logging(benchmark::Bench& bench, const std::vector<const char*>& extra_args, const std::function<void()>& log)\n {\n     // Reset any enabled logging categories from a previous benchmark run.\n-    LogInstance().DisableCategory(BCLog::LogFlags::ALL);\n+    LogInstance().SetCategoryLogLevel(BCLog::ALL, BCLog::Level::Info);\n\n     TestingSetup test_setup{\n         ChainType::REGTEST,\ndiff --git a/src/init/common.cpp b/src/init/common.cpp\nindex eb1b06adf5..99908581ef 100644\n--- a/src/init/common.cpp\n+++ b/src/init/common.cpp\n@@ -69,9 +69,11 @@ util::Result<void> SetLoggingCategories(const ArgsManager& args)\n     const auto categories_to_process = (last_negated == categories.rend()) ? categories : std::ranges::subrange(last_negated.base(), categories.end());\n\n     for (const auto& cat : categories_to_process) {\n-        if (!LogInstance().EnableCategory(cat)) {\n+        BCLog::LogFlags flags;\n+        if (!BCLog::Logger::GetLogCategory(flags, cat)) {\n             return util::Error{strprintf(_(\"Unsupported logging category %s=%s.\"), \"-debug\", cat)};\n         }\n+        LogInstance().SetCategoryLogLevel(flags, BCLog::Level::Debug);\n     }\n\n     // Special-case: Disregard any debugging categories appearing before -trace=0/none\n@@ -81,16 +83,20 @@ util::Result<void> SetLoggingCategories(const ArgsManager& args)\n\n     const auto trace_categories_to_process = (trace_negated == trace_categories.rend()) ? trace_categories : std::ranges::subrange(trace_negated.base(), trace_categories.end());\n     for (const auto& cat : trace_categories_to_process) {\n-        if (!LogInstance().TraceCategory(cat)) {\n+        BCLog::LogFlags flags;\n+        if (!BCLog::Logger::GetLogCategory(flags, cat)) {\n             return util::Error{strprintf(_(\"Unsupported logging category %s=%s.\"), \"-trace\", cat)};\n         }\n+        LogInstance().SetCategoryLogLevel(flags, BCLog::Level::Trace);\n     }\n\n     // Now remove the logging categories which were explicitly excluded\n     for (const std::string& cat : args.GetArgs(\"-debugexclude\")) {\n-        if (!LogInstance().DisableCategory(cat)) {\n+        BCLog::LogFlags flags;\n+        if (!BCLog::Logger::GetLogCategory(flags, cat)) {\n             return util::Error{strprintf(_(\"Unsupported logging category %s=%s.\"), \"-debugexclude\", cat)};\n         }\n+        LogInstance().SetCategoryLogLevel(flags, BCLog::Level::Info);\n     }\n     return {};\n }\ndiff --git a/src/kernel/bitcoinkernel.cpp b/src/kernel/bitcoinkernel.cpp\nindex 15812baf00..253f101c4e 100644\n--- a/src/kernel/bitcoinkernel.cpp\n+++ b/src/kernel/bitcoinkernel.cpp\n@@ -735,12 +735,12 @@ void btck_logging_set_level_category(btck_LogCategory category, btck_LogLevel le\n\n void btck_logging_enable_category(btck_LogCategory category)\n {\n-    LogInstance().EnableCategory(get_bclog_flag(category));\n+    LogInstance().SetCategoryLogLevel(get_bclog_flag(category), BCLog::Level::Debug);\n }\n\n void btck_logging_disable_category(btck_LogCategory category)\n {\n-    LogInstance().DisableCategory(get_bclog_flag(category));\n+    LogInstance().SetCategoryLogLevel(get_bclog_flag(category), BCLog::Level::Info);\n }\n\n void btck_logging_disable()\ndiff --git a/src/logging.cpp b/src/logging.cpp\nindex 870e666c69..6fa0e22db3 100644\n--- a/src/logging.cpp\n+++ b/src/logging.cpp\n@@ -120,47 +120,6 @@ void BCLog::Logger::DisableLogging()\n     StartLogging();\n }\n\n-void BCLog::Logger::EnableCategory(BCLog::LogFlags flag)\n-{\n-    m_categories |= flag;\n-    SetCategoryLogLevel(flag, Level::Debug);\n-}\n-\n-bool BCLog::Logger::EnableCategory(std::string_view str)\n-{\n-    BCLog::LogFlags flag;\n-    if (!GetLogCategory(flag, str)) return false;\n-    EnableCategory(flag);\n-    return true;\n-}\n-\n-void BCLog::Logger::TraceCategory(BCLog::LogFlags flag)\n-{\n-    m_categories |= flag;\n-    SetCategoryLogLevel(flag, Level::Trace);\n-}\n-\n-bool BCLog::Logger::TraceCategory(std::string_view str)\n-{\n-    BCLog::LogFlags flag;\n-    if (!GetLogCategory(flag, str)) return false;\n-    TraceCategory(flag);\n-    return true;\n-}\n-\n-void BCLog::Logger::DisableCategory(BCLog::LogFlags flag)\n-{\n-    m_categories &= ~flag;\n-}\n-\n-bool BCLog::Logger::DisableCategory(std::string_view str)\n-{\n-    BCLog::LogFlags flag;\n-    if (!GetLogCategory(flag, str)) return false;\n-    DisableCategory(flag);\n-    return true;\n-}\n-\n bool BCLog::Logger::WillLogCategory(BCLog::LogFlags category) const\n {\n     return (m_categories.load(std::memory_order_relaxed) & category) != 0;\n@@ -272,7 +231,7 @@ static std::string LogCategoryToStr(BCLog::LogFlags category)\n     return it->second;\n }\n\n-static std::optional<BCLog::Level> GetLogLevel(std::string_view level_str)\n+std::optional<BCLog::Level> BCLog::Logger::GetLogLevel(std::string_view level_str)\n {\n     if (level_str == \"trace\") {\n         return BCLog::Level::Trace;\n@@ -625,6 +584,14 @@ bool BCLog::Logger::SetLogLevel(std::string_view level_str)\n void BCLog::Logger::SetCategoryLogLevel(BCLog::LogFlags flag, BCLog::Level level)\n {\n     STDLOCK(m_cs);\n+    Assume(level <= BCLog::Level::Info);\n+    if (level >= BCLog::Level::Info) {\n+        m_categories &= ~flag;\n+        m_category_log_levels.erase(flag);\n+    } else {\n+        m_categories |= flag;\n+    }\n+\n     if (flag == LogFlags::ALL) {\n         SetLogLevel(level);\n         m_category_log_levels.clear();\n@@ -632,15 +599,3 @@ void BCLog::Logger::SetCategoryLogLevel(BCLog::LogFlags flag, BCLog::Level level\n         m_category_log_levels[flag] = level;\n     }\n }\n-\n-bool BCLog::Logger::SetCategoryLogLevel(std::string_view category_str, std::string_view level_str)\n-{\n-    BCLog::LogFlags flag;\n-    if (!GetLogCategory(flag, category_str)) return false;\n-\n-    const auto level = GetLogLevel(level_str);\n-    if (!level.has_value() || level.value() > MAX_USER_SETABLE_SEVERITY_LEVEL) return false;\n-\n-    SetCategoryLogLevel(flag, level.value());\n-    return true;\n-}\ndiff --git a/src/logging.h b/src/logging.h\nindex 46ac348ac4..c894288ab4 100644\n--- a/src/logging.h\n+++ b/src/logging.h\n@@ -325,7 +325,6 @@ namespace BCLog {\n             m_category_log_levels[category] = level;\n         }\n         void SetCategoryLogLevel(LogFlags flag, Level level) EXCLUSIVE_LOCKS_REQUIRED(!m_cs);\n-        bool SetCategoryLogLevel(std::string_view category_str, std::string_view level_str) EXCLUSIVE_LOCKS_REQUIRED(!m_cs);\n\n         Level LogLevel() const { return m_log_level.load(); }\n         void SetLogLevel(Level level) { m_log_level = level; }\n@@ -333,13 +332,6 @@ namespace BCLog {\n\n         CategoryMask GetCategoryMask() const { return m_categories.load(); }\n\n-        void EnableCategory(LogFlags flag) EXCLUSIVE_LOCKS_REQUIRED(!m_cs);\n-        bool EnableCategory(std::string_view str) EXCLUSIVE_LOCKS_REQUIRED(!m_cs);\n-        void TraceCategory(LogFlags flag) EXCLUSIVE_LOCKS_REQUIRED(!m_cs);\n-        bool TraceCategory(std::string_view str) EXCLUSIVE_LOCKS_REQUIRED(!m_cs);\n-        void DisableCategory(LogFlags flag) EXCLUSIVE_LOCKS_REQUIRED(!m_cs);\n-        bool DisableCategory(std::string_view str) EXCLUSIVE_LOCKS_REQUIRED(!m_cs);\n-\n         bool WillLogCategory(LogFlags category) const;\n         bool WillLogCategoryLevel(LogFlags category, Level level) const EXCLUSIVE_LOCKS_REQUIRED(!m_cs);\n\n@@ -378,6 +370,7 @@ namespace BCLog {\n             LogPrintFormatInternal<false>(std::move(source_loc), flag, level, std::move(fmt), args...);\n         }\n\n+        static std::optional<BCLog::Level> GetLogLevel(std::string_view level_str);\n         /** Return true if str parses as a log category and set the flag */\n         static bool GetLogCategory(BCLog::LogFlags& flag, std::string_view str);\n     };\ndiff --git a/src/rpc/node.cpp b/src/rpc/node.cpp\nindex 301911fa52..79e8df7bd3 100644\n--- a/src/rpc/node.cpp\n+++ b/src/rpc/node.cpp\n@@ -202,24 +202,11 @@ static void EnableOrDisableLogCategories(UniValue cats, BCLog::Level new_level)\n     for (unsigned int i = 0; i < cats.size(); ++i) {\n         std::string cat = cats[i].get_str();\n\n-        bool success{false};\n-        switch (new_level) {\n-        case BCLog::Level::Trace:\n-            success = LogInstance().TraceCategory(cat);\n-            break;\n-        case BCLog::Level::Debug:\n-            success = LogInstance().EnableCategory(cat);\n-            break;\n-        case BCLog::Level::Info:\n-            success = LogInstance().DisableCategory(cat);\n-            break;\n-        default:\n-            NONFATAL_UNREACHABLE();\n-            break;\n-        }\n-        if (!success) {\n+        BCLog::LogFlags flags;\n+        if (!BCLog::Logger::GetLogCategory(flags, cat)) {\n             throw JSONRPCError(RPC_INVALID_PARAMETER, \"unknown logging category \" + cat);\n         }\n+        LogInstance().SetCategoryLogLevel(flags, new_level);\n     }\n }\n\ndiff --git a/src/test/logging_tests.cpp b/src/test/logging_tests.cpp\nindex b06752c1e2..26fadcbdcc 100644\n--- a/src/test/logging_tests.cpp\n+++ b/src/test/logging_tests.cpp\n@@ -95,8 +95,8 @@ struct LogSetup : public BasicTestingSetup {\n         LogInstance().SetLogLevel(prev_log_level);\n         LogInstance().SetCategoryLogLevel(prev_category_levels);\n         LogInstance().SetRateLimiting(nullptr);\n-        LogInstance().DisableCategory(BCLog::LogFlags::ALL);\n-        LogInstance().EnableCategory(BCLog::LogFlags{prev_category_mask});\n+        LogInstance().SetCategoryLogLevel(BCLog::LogFlags::ALL, BCLog::Level::Info);\n+        LogInstance().SetCategoryLogLevel(BCLog::LogFlags{prev_category_mask}, BCLog::Level::Debug);\n     }\n };\n\n@@ -139,7 +139,7 @@ BOOST_FIXTURE_TEST_CASE(logging_LogPrintStr, LogSetup)\n\n BOOST_FIXTURE_TEST_CASE(logging_LogPrintMacros, LogSetup)\n {\n-    LogInstance().EnableCategory(BCLog::NET);\n+    LogInstance().SetCategoryLogLevel(BCLog::NET, BCLog::Level::Debug);\n     LogTrace(BCLog::NET, \"foo6: %s\", \"bar6\"); // not logged\n     LogDebug(BCLog::NET, \"foo7: %s\", \"bar7\");\n     LogInfo(\"foo8: %s\", \"bar8\");\n@@ -157,7 +157,7 @@ BOOST_FIXTURE_TEST_CASE(logging_LogPrintMacros, LogSetup)\n\n BOOST_FIXTURE_TEST_CASE(logging_LogPrintMacros_CategoryName, LogSetup)\n {\n-    LogInstance().EnableCategory(BCLog::LogFlags::ALL);\n+    LogInstance().SetCategoryLogLevel(BCLog::ALL, BCLog::Level::Debug);\n     const auto concatenated_category_names = LogInstance().LogCategoriesString();\n     std::vector<std::pair<BCLog::LogFlags, std::string>> expected_category_names;\n     const auto category_names = SplitString(concatenated_category_names, ',');\n@@ -184,8 +184,8 @@ BOOST_FIXTURE_TEST_CASE(logging_LogPrintMacros_CategoryName, LogSetup)\n BOOST_FIXTURE_TEST_CASE(logging_SeverityLevels, LogSetup)\n {\n     LogInstance().SetLogLevel(BCLog::Level::Debug);\n-    LogInstance().EnableCategory(BCLog::LogFlags::ALL);\n-    LogInstance().SetCategoryLogLevel(/*category_str=*/\"net\", /*level_str=*/\"info\");\n+    LogInstance().SetCategoryLogLevel(BCLog::LogFlags::ALL, BCLog::Level::Debug);\n+    LogInstance().SetCategoryLogLevel(BCLog::LogFlags::NET, BCLog::Level::Info);\n\n     // Global log level\n     LogInfo(\"info_%s\", 1);\n@@ -431,8 +431,8 @@ BOOST_FIXTURE_TEST_CASE(logging_filesize_rate_limit, LogSetup)\n     LogInstance().m_log_timestamps = false;\n     LogInstance().m_log_sourcelocations = false;\n     LogInstance().m_log_threadnames = false;\n-    LogInstance().DisableCategory(BCLog::LogFlags::ALL);\n-    LogInstance().EnableCategory(BCLog::LogFlags::HTTP);\n+    LogInstance().SetCategoryLogLevel(BCLog::ALL, BCLog::Level::Info);\n+    LogInstance().SetCategoryLogLevel(BCLog::HTTP, BCLog::Level::Debug);\n\n     constexpr int64_t line_length{1024};\n     constexpr int64_t num_lines{10};\n\n```"
  },
  {
   "t": "2026-01-03T08:07:42Z",
   "kind": "review_comment",
   "who": "stickies-v",
   "assoc": "MEMBER",
   "path": "test/functional/feature_logging.py",
   "commit": "f565bbce26e52ffdbe3f8ecacd015d603d4eb05e",
   "in_reply_to": null,
   "text": "```suggestion\n            assert 'net' in logging['debug'] and 'net' not in (logging['excluded'] + logging['trace'])\n```"
  },
  {
   "t": "2026-01-03T08:08:07Z",
   "kind": "review_comment",
   "who": "stickies-v",
   "assoc": "MEMBER",
   "path": "test/functional/feature_logging.py",
   "commit": "f565bbce26e52ffdbe3f8ecacd015d603d4eb05e",
   "in_reply_to": null,
   "text": "```suggestion\n            assert 'net' in logging['trace'] and 'net' not in (logging['excluded'] + logging['debug'])\n```"
  },
  {
   "t": "2026-01-03T08:15:01Z",
   "kind": "review_comment",
   "who": "stickies-v",
   "assoc": "MEMBER",
   "path": "src/init/common.cpp",
   "commit": "f565bbce26e52ffdbe3f8ecacd015d603d4eb05e",
   "in_reply_to": null,
   "text": "nit\n```suggestion\n    argsman.AddArg(\"-debugexclude=<category>\", \"Exclude debug and trace logging for a category. Can be used in conjunction with -debug=1 or -trace=1 to output debug and trace logging for all categories except the specified category. This option can be specified multiple times to exclude multiple categories. This takes priority over \\\"-debug\\\" and \\\"-trace\\\".\", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);\n```"
  },
  {
   "t": "2026-01-03T08:27:24Z",
   "kind": "review_comment",
   "who": "stickies-v",
   "assoc": "MEMBER",
   "path": "src/init/common.cpp",
   "commit": "9b38dc78ac74f438be25d04b787b2f47a3b76f40",
   "in_reply_to": null,
   "text": "```suggestion\n    // Special-case: Disregard any trace categories appearing before -trace=0/none\n```"
  },
  {
   "t": "2026-01-03T08:34:12Z",
   "kind": "review_comment",
   "who": "stickies-v",
   "assoc": "MEMBER",
   "path": "src/logging.h",
   "commit": "6eee953bb8ee6fe465838fa7ce6cb4ff64ade543",
   "in_reply_to": 2657653389,
   "text": "Not implemented in my diff, but I think just using `SetCategoryLevel` would also make it quite straightforward to deduplicate the debug/trace parsing logic in `SetLoggingCategories`."
  },
  {
   "t": "2026-01-03T08:39:20Z",
   "kind": "review_comment",
   "who": "stickies-v",
   "assoc": "MEMBER",
   "path": "src/test/logging_tests.cpp",
   "commit": "befe8183dcdbb7e5ee4502b1391b1be6cc20324f",
   "in_reply_to": null,
   "text": "nit: not moving `LogInstance().DisableCategory(BCLog::LogFlags::ALL);` means we're no longer resetting the `m_categories` field, not a big issue but probably better to keep this? _(also probably a good indication that the current interface is still a bit wonky)_"
  },
  {
   "t": "2026-01-03T08:46:00Z",
   "kind": "review_comment",
   "who": "stickies-v",
   "assoc": "MEMBER",
   "path": "src/test/logging_tests.cpp",
   "commit": "f565bbce26e52ffdbe3f8ecacd015d603d4eb05e",
   "in_reply_to": null,
   "text": "nit: this line can just be removed, we're not passing any `trace` args"
  },
  {
   "t": "2026-01-03T08:53:44Z",
   "kind": "review_comment",
   "who": "stickies-v",
   "assoc": "MEMBER",
   "path": "src/test/logging_tests.cpp",
   "commit": "befe8183dcdbb7e5ee4502b1391b1be6cc20324f",
   "in_reply_to": null,
   "text": "I think it's also useful to test here that non-set categories don't show up? e.g.\n\n```cpp\nBOOST_CHECK(!category_levels.contains(BCLog::LogFlags::REINDEX));\n```"
  },
  {
   "t": "2026-01-03T09:05:29Z",
   "kind": "review_comment",
   "who": "stickies-v",
   "assoc": "MEMBER",
   "path": "src/rpc/node.cpp",
   "commit": "f565bbce26e52ffdbe3f8ecacd015d603d4eb05e",
   "in_reply_to": null,
   "text": "nit: \"the lists above\" is not really clear, would just duplicate from the line above to highlight that the result is the same:\n\n```suggestion\n            \"When called with arguments, adds or removes categories from debug or trace logging and returns the status of the supported logging categories.\\n\"\n```"
  },
  {
   "t": "2026-01-03T09:11:03Z",
   "kind": "review_comment",
   "who": "stickies-v",
   "assoc": "MEMBER",
   "path": "test/functional/feature_logging.py",
   "commit": "6eee953bb8ee6fe465838fa7ce6cb4ff64ade543",
   "in_reply_to": null,
   "text": "Could be useful to test that previous values aren't deleted?\n\n```py\n\n            # Check that previous settings without conflict are preserved\n            assert 'rpc' in logging['trace'] and 'rpc' not in (logging['excluded'] + logging['debug'])\n```"
  },
  {
   "t": "2026-01-03T09:18:21Z",
   "kind": "review_comment",
   "who": "stickies-v",
   "assoc": "MEMBER",
   "path": "src/rpc/node.cpp",
   "commit": "befe8183dcdbb7e5ee4502b1391b1be6cc20324f",
   "in_reply_to": null,
   "text": "\"exclude\" makes sense when configuring categories, but wouldn't \"disabled\" or \"inactive\" be a more appropriate name for the result?"
  },
  {
   "t": "2026-01-03T09:27:27Z",
   "kind": "review",
   "who": "stickies-v",
   "assoc": "MEMBER",
   "state": "COMMENTED",
   "commit": "f565bbce26e52ffdbe3f8ecacd015d603d4eb05e",
   "text": "Approach ACK, reviewed code f565bbce26e52ffdbe3f8ecacd015d603d4eb05e"
  },
  {
   "t": "2026-01-23T05:52:03Z",
   "kind": "review_comment",
   "who": "ajtowns",
   "assoc": "MEMBER",
   "path": "src/threadsafety.h",
   "commit": "a98dae5c1caa543b20720db850af09145d401890",
   "in_reply_to": 2657032120,
   "text": "That comment is still there for the `StdMutex` class, and `StdMutex::Guard` isn't usable without `StdMutex`"
  },
  {
   "t": "2026-01-23T05:56:39Z",
   "kind": "review_comment",
   "who": "ajtowns",
   "assoc": "MEMBER",
   "path": "src/threadsafety.h",
   "commit": "a98dae5c1caa543b20720db850af09145d401890",
   "in_reply_to": 2657113122,
   "text": "It matches the `MaybeCheckNotHeld` functions in sync.h (the `Maybe` there reflects that the overload for is weakened for Global/Recursive locks), and as an inline function in the class seems pretty self-documenting"
  },
  {
   "t": "2026-01-23T05:57:32Z",
   "kind": "review_comment",
   "who": "ajtowns",
   "assoc": "MEMBER",
   "path": "src/logging.cpp",
   "commit": "befe8183dcdbb7e5ee4502b1391b1be6cc20324f",
   "in_reply_to": 2657451024,
   "text": "Seems like a reasonable idea, but doesn't seem necessary to do in this PR"
  },
  {
   "t": "2026-01-23T06:03:47Z",
   "kind": "review_comment",
   "who": "ajtowns",
   "assoc": "MEMBER",
   "path": "src/logging.cpp",
   "commit": "befe8183dcdbb7e5ee4502b1391b1be6cc20324f",
   "in_reply_to": 2657490337,
   "text": "`EnableCategory` does it to allow downgrading from trace to debug; but `DisableCategory` can just set the flag to disable both trace and debug logging for the category immediately. If it's later re-enabled, `EnableCategory` or `TraceCategory` will replace any old value at that time."
  },
  {
   "t": "2026-01-23T06:06:54Z",
   "kind": "review_comment",
   "who": "ajtowns",
   "assoc": "MEMBER",
   "path": "src/logging.h",
   "commit": "6eee953bb8ee6fe465838fa7ce6cb4ff64ade543",
   "in_reply_to": 2657653389,
   "text": "I don't disagree, but I wanted to keep the API changes small in this PR, and tidy up the internal representation (dropping `m_log_level` and replacing the `m_category_log_levels` map with a bitfield) in the next one first."
  },
  {
   "t": "2026-01-23T09:05:59Z",
   "kind": "review_comment",
   "who": "ajtowns",
   "assoc": "MEMBER",
   "path": "src/rpc/node.cpp",
   "commit": "befe8183dcdbb7e5ee4502b1391b1be6cc20324f",
   "in_reply_to": 2658818723,
   "text": "I think if you're going to \"exclude\" something, it's not surprising if its resulting state is \"excluded\". I could maybe see an argument to making it disable/disabled everywhere, but even that doesn't seem like much of a win? Maybe \"silenced\" would be better though?"
  },
  {
   "t": "2026-01-23T10:27:31Z",
   "kind": "force_push",
   "who": "ajtowns",
   "commit": "f5a80c45b76e9b4203f283a7bda8b373e1fd1668"
  },
  {
   "t": "2026-01-28T04:41:23Z",
   "kind": "force_push",
   "who": "ajtowns",
   "commit": "9b38dc78ac74f438be25d04b787b2f47a3b76f40"
  },
  {
   "t": "2026-01-28T06:07:09Z",
   "kind": "review_comment",
   "who": "ajtowns",
   "assoc": "MEMBER",
   "path": "src/logging.h",
   "commit": "408a8d070b40d029510595e5ea979127fb0e09a5",
   "in_reply_to": 2657087747,
   "text": "It's only used internally in logging.cpp and in tests, and works fine, I don't think this needs changing."
  },
  {
   "t": "2026-01-28T06:10:52Z",
   "kind": "review_comment",
   "who": "ajtowns",
   "assoc": "MEMBER",
   "path": "src/logging.h",
   "commit": "6eee953bb8ee6fe465838fa7ce6cb4ff64ade543",
   "in_reply_to": 2657653389,
   "text": "I've added some cleanup commits in this vein to the end of the followup branch at https://github.com/ajtowns/bitcoin/commits/202512-log-atomic/"
  },
  {
   "t": "2026-01-29T01:13:31Z",
   "kind": "force_push",
   "who": "ajtowns",
   "commit": "103fa7597e6ce9653d0419126d262676afe413c5"
  },
  {
   "t": "2026-01-29T05:54:22Z",
   "kind": "comment",
   "who": "ajtowns",
   "assoc": "MEMBER",
   "text": "(Rebased past #34417 and #34267)"
  },
  {
   "t": "2026-02-11T03:28:08Z",
   "kind": "force_push",
   "who": "ajtowns",
   "commit": "019f69364aa707f4401864669ea39c86755f6263"
  },
  {
   "t": "2026-02-11T03:43:03Z",
   "kind": "force_push",
   "who": "ajtowns",
   "commit": "0a7dc95942035f9ced35630196c21a3c9a1f3b32"
  },
  {
   "t": "2026-02-11T06:58:14Z",
   "kind": "comment",
   "who": "ajtowns",
   "assoc": "MEMBER",
   "text": "Rebased past #34465"
  },
  {
   "t": "2026-02-20T12:10:24Z",
   "kind": "force_push",
   "who": "ajtowns",
   "commit": "a98dae5c1caa543b20720db850af09145d401890"
  },
  {
   "t": "2026-03-10T14:05:36Z",
   "kind": "comment",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "text": "Concept ACK a98dae5c1caa543b20720db850af09145d401890. Have had a look at the changes here and they seem nice and I plan to review.\n\nOne tentative suggestion I'd have is to try to make the PR smaller. The STDLOCK thread annotation change might make sense as a separate PR so it could be easily reviewed by people familiar with thread safety annotations (hebasto, vasild, marco) and merged quickly, without reviewers needing to look into more complicated rpc & logging backend changes.\n\nI also don't see the connection here between the logging backend changes and the developer facing ratelimiting and macro changes, so maybe those changes also don't need to be part of this PR. (I'm hoping over time we have a clearer separation between logging frontend & backend, so for example libbitcoinkernel doesn't need to include a logging backend. If this separation also has benefits for code review that would seem like another win.)"
  },
  {
   "t": "2026-03-11T08:39:32Z",
   "kind": "comment",
   "who": "stickies-v",
   "assoc": "MEMBER",
   "text": "[quoted text omitted]\n\nI agree. I was pretty close to signing off on the loglevel changes, but then got stuck on the `STDLOCK` changes - which seem sensible, but not trivial to review, and blocked my ack."
  },
  {
   "t": "2026-03-11T15:28:48Z",
   "kind": "force_push",
   "who": "ajtowns",
   "commit": "408a8d070b40d029510595e5ea979127fb0e09a5"
  },
  {
   "t": "2026-03-11T16:18:07Z",
   "kind": "force_push",
   "who": "ajtowns",
   "commit": "e54609aedc4fd2cb28aed0708f2a15c1bdc739cf"
  },
  {
   "t": "2026-03-11T16:18:14Z",
   "kind": "comment",
   "who": "ajtowns",
   "assoc": "MEMBER",
   "text": "[quoted text omitted]\n\nHmm, I thought those were much more tied together than they apparently are. Consider it split!\n\nEDIT: Split into #34806 (internal logging api refactors) and #34809 (STDLOCK)."
  },
  {
   "t": "2026-03-13T02:51:44Z",
   "kind": "review_comment",
   "who": "stickies-v",
   "assoc": "MEMBER",
   "path": "doc/release-notes-34038.md",
   "commit": "befe8183dcdbb7e5ee4502b1391b1be6cc20324f",
   "in_reply_to": null,
   "text": "I think IPC also has trace logs?"
  },
  {
   "t": "2026-03-13T02:53:00Z",
   "kind": "review_comment",
   "who": "stickies-v",
   "assoc": "MEMBER",
   "path": "test/functional/test_framework/test_node.py",
   "commit": "e54609aedc4fd2cb28aed0708f2a15c1bdc739cf",
   "in_reply_to": null,
   "text": "since this is part of the 32 milestone, I think this can be bumped already\n```suggestion\n        if self.version_is_at_least(319000):\n```"
  },
  {
   "t": "2026-03-13T02:57:04Z",
   "kind": "review_comment",
   "who": "stickies-v",
   "assoc": "MEMBER",
   "path": "src/rpc/node.cpp",
   "commit": "befe8183dcdbb7e5ee4502b1391b1be6cc20324f",
   "in_reply_to": 2658818723,
   "text": "[quoted text omitted]\n\nI would prefer that. My natural understanding of exclude is something the user actively did (e.g. debug all categories, but exclude net), whereas \"disabled\" more neutrally represents the state, regardless of how it got there. However, not a native speaker, so my intuition may be wrong here. Regardless, this is probably going to be subjective, so you can ignore if you disagree."
  },
  {
   "t": "2026-03-13T02:59:09Z",
   "kind": "review_comment",
   "who": "stickies-v",
   "assoc": "MEMBER",
   "path": "src/logging.cpp",
   "commit": "befe8183dcdbb7e5ee4502b1391b1be6cc20324f",
   "in_reply_to": 2657490337,
   "text": "Yeah, I agree it's not currently necessary, just seems nice to keep things consistent when there's no meaningful cost. But I don't have a strong rationale for it, so you can mark as resolved."
  },
  {
   "t": "2026-03-13T03:21:26Z",
   "kind": "review_comment",
   "who": "stickies-v",
   "assoc": "MEMBER",
   "path": "src/rpc/node.cpp",
   "commit": "befe8183dcdbb7e5ee4502b1391b1be6cc20324f",
   "in_reply_to": null,
   "text": "We have quite a bit of complexity wrt interface (documentation), implementation, and tests to deal with the interactions and ordering of these `debug`, `trace`, `exclude` options.\n\nWhat if we do away with all of that, and just let the user deal with ordering through multiple calls? For example, with an interface like `logging ( \"level\" [\"category\",...] )` the `cli logging [\"all\"] [\"net\"]` call is equivalent to `cli logging \"debug\" && cli logging \"disable\" [\"net\"]`. The latter is slightly more verbose, but is much more simple and doesn't require the user to keep remembering the (awkward, for backward compatibility purposes) ordering of arguments etc and their evaluation. I don't think atomicity is meaningful here?\n\nOrthogonally, I think it also wouldn't be unreasonable to just use \"info\" instead of \"disable{d}\"/\"excluded\" language. The fact that we internally choose to not assign categories to info+ levels shouldn't really concern the user. That way `cli logging \"info\"` becomes a natural way to just reset all logging to info."
  },
  {
   "t": "2026-03-13T03:37:50Z",
   "kind": "review",
   "who": "stickies-v",
   "assoc": "MEMBER",
   "state": "COMMENTED",
   "commit": "e54609aedc4fd2cb28aed0708f2a15c1bdc739cf",
   "text": "Thanks for splitting it up!"
  },
  {
   "t": "2026-03-20T05:12:32Z",
   "kind": "review_comment",
   "who": "ajtowns",
   "assoc": "MEMBER",
   "path": "doc/release-notes-34038.md",
   "commit": "befe8183dcdbb7e5ee4502b1391b1be6cc20324f",
   "in_reply_to": 2928708072,
   "text": "Maybe? Doesn't seem documented anywhere, and the implementation doesn't seem to turn into a no-op if tracing is disabled, so not sure I'd call it \"supported\"."
  },
  {
   "t": "2026-03-20T05:20:06Z",
   "kind": "review_comment",
   "who": "ajtowns",
   "assoc": "MEMBER",
   "path": "src/rpc/node.cpp",
   "commit": "befe8183dcdbb7e5ee4502b1391b1be6cc20324f",
   "in_reply_to": 2928796887,
   "text": "[quoted text omitted]\n\nIf we were creating the RPC from scratch, maybe; but doesn't seem worth the additional backwards compat break to me (currently this PR changes the output, but the old invocations still work unchanged).\n\n[quoted text omitted]\nThere is no \"net info\" category of logs. The only \"net\" logs are debug and trace, so the only logs you can expect to have are \"both trace and debug\", \"just debug\" or \"none\" of them. Disabled or excluded is a better description of the latter than \"info\"."
  },
  {
   "t": "2026-03-20T05:38:28Z",
   "kind": "force_push",
   "who": "ajtowns",
   "commit": "df51f8d13c26c7255caf3211bcb7bc258c80dd01"
  },
  {
   "t": "2026-03-20T10:07:41Z",
   "kind": "review_comment",
   "who": "stickies-v",
   "assoc": "MEMBER",
   "path": "src/rpc/node.cpp",
   "commit": "befe8183dcdbb7e5ee4502b1391b1be6cc20324f",
   "in_reply_to": 2928796887,
   "text": "[quoted text omitted]\n\nI personally think if we already introduce a `deprecatedrpc` (potentially) breaking change that is most likely going to require some engineering attention for users, meaningfully simplifying the interface and cleaning up what feels like tech debt is the right thing to do.\n\n[quoted text omitted]\nI'm aware, but our users shouldn't have to care about that. If there are no \"net info\" logs, then setting \"net\" to \"info\" will produce 0 logs. I'm not advocating for this because I later on want to start adding categories to info logs (I have no interest in that conversation), I'm suggesting this because i think for a logging user who's not intimately familiar with our codebase, that's much more intuitive."
  },
  {
   "t": "2026-03-20T13:28:59Z",
   "kind": "review_comment",
   "who": "ajtowns",
   "assoc": "MEMBER",
   "path": "src/rpc/node.cpp",
   "commit": "befe8183dcdbb7e5ee4502b1391b1be6cc20324f",
   "in_reply_to": 2928796887,
   "text": "[quoted text omitted]\n\nI don't expect the deprecatedrpc option this PR introduces to see much if any use; evaluating the result of the `logging` call is likely something that people only do visually, rather than automate. OTOH, making the call to turn logging off or on seems somewhat more likely to be automated.\n\nIn any event if you really want to do that, there's plenty of time for you to do so in a followup before 32.x is out if you want to expand the scope of API breakage. I don't think that's a very good use of anyone's time personally (my opinion in general on breaking existing uses in order to clean things up is at an all time low and still sinking fast), but either way I don't think it needs to be a blocker for this PR.\n\nRe: \"<category> info\" logs: I strongly disagree. Someone who's not intimately familiar with our codebase likely doesn't even know the term \"info\" for logging, which only appears either in our code (`LogInfo`) or if you enable the `-loglevelalways` option, or if you use the experimental `-loglevel` option that this PR removes in a way that would hard disable debug/trace logging for all categories.\n\n[quoted text omitted]\nThe same argument would equally well support naming the setting \"unicorns\"."
  },
  {
   "t": "2026-03-24T13:03:09Z",
   "kind": "force_push",
   "who": "ajtowns",
   "commit": "6c2dc24e08c936c0a8b465782543a1c97c9e5033"
  },
  {
   "t": "2026-04-02T05:38:33Z",
   "kind": "force_push",
   "who": "ajtowns",
   "commit": "6eee953bb8ee6fe465838fa7ce6cb4ff64ade543"
  },
  {
   "t": "2026-04-02T14:41:17Z",
   "kind": "review_comment",
   "who": "stickies-v",
   "assoc": "MEMBER",
   "path": "src/logging.h",
   "commit": "6eee953bb8ee6fe465838fa7ce6cb4ff64ade543",
   "in_reply_to": null,
   "text": "`DisableCategory` doesn't access `m_cs`, I think these annotations can be removed?\n\ngit diff on 6eee953bb8\n\n```diff\ndiff --git a/src/logging.h b/src/logging.h\nindex d5c33d5096..ef9b1ecc5d 100644\n--- a/src/logging.h\n+++ b/src/logging.h\n@@ -265,8 +265,8 @@ namespace BCLog {\n         bool EnableCategory(std::string_view str) EXCLUSIVE_LOCKS_REQUIRED(!m_cs);\n         void TraceCategory(LogFlags flag) EXCLUSIVE_LOCKS_REQUIRED(!m_cs);\n         bool TraceCategory(std::string_view str) EXCLUSIVE_LOCKS_REQUIRED(!m_cs);\n-        void DisableCategory(LogFlags flag) EXCLUSIVE_LOCKS_REQUIRED(!m_cs);\n-        bool DisableCategory(std::string_view str) EXCLUSIVE_LOCKS_REQUIRED(!m_cs);\n+        void DisableCategory(LogFlags flag);\n+        bool DisableCategory(std::string_view str);\n\n         bool WillLogCategory(LogFlags category) const;\n         bool WillLogCategoryLevel(LogFlags category, Level level) const EXCLUSIVE_LOCKS_REQUIRED(!m_cs);\n\n```"
  },
  {
   "t": "2026-04-02T15:00:13Z",
   "kind": "review_comment",
   "who": "stickies-v",
   "assoc": "MEMBER",
   "path": "doc/release-notes-34038.md",
   "commit": "befe8183dcdbb7e5ee4502b1391b1be6cc20324f",
   "in_reply_to": 2928708072,
   "text": "I'm also not getting any logs when running ` ./build/bin/bitcoin -m node -signet -trace=ipc | grep -i ipc`, so yeah seems fair to leave as-is."
  },
  {
   "t": "2026-04-02T15:21:30Z",
   "kind": "review_comment",
   "who": "stickies-v",
   "assoc": "MEMBER",
   "path": "src/rpc/node.cpp",
   "commit": "befe8183dcdbb7e5ee4502b1391b1be6cc20324f",
   "in_reply_to": 2928796887,
   "text": "[quoted text omitted]\n\nFair, not all breaking changes are equally breaking, and I agree we should properly weigh the impact this has on users. I'll see if I want to open a follow-up, I suspect I probably won't.\n\n[quoted text omitted]\n\"Info\" is standard among logging libraries, so I don't think it's unreasonable or a bad idea to generally align with that language instead of our bespoke way of doing things when it's not meaningfully better. Anyway, I understand you don't want to make this change here."
  },
  {
   "t": "2026-04-02T15:28:44Z",
   "kind": "review_comment",
   "who": "stickies-v",
   "assoc": "MEMBER",
   "path": "src/rpc/node.cpp",
   "commit": "befe8183dcdbb7e5ee4502b1391b1be6cc20324f",
   "in_reply_to": null,
   "text": "I think this should also be `NONFATAL_UNREACHABLE`\n```suggestion\n                NONFATAL_UNREACHABLE();\n                break;\n```"
  },
  {
   "t": "2026-04-02T15:33:08Z",
   "kind": "review_comment",
   "who": "stickies-v",
   "assoc": "MEMBER",
   "path": "test/functional/rpc_misc.py",
   "commit": "6eee953bb8ee6fe465838fa7ce6cb4ff64ade543",
   "in_reply_to": null,
   "text": "nit: `assert` is a keyword in python, not a function\n\ngit diff on 6eee953bb8\n\n```diff\ndiff --git a/test/functional/rpc_misc.py b/test/functional/rpc_misc.py\nindex a867444f66..43cbcbf19c 100755\n--- a/test/functional/rpc_misc.py\n+++ b/test/functional/rpc_misc.py\n@@ -77,15 +77,15 @@ class RpcMiscTest(BitcoinTestFramework):\n         self.log.info(\"test logging rpc and help\")\n\n         # Test toggling a logging category on/off/on with the logging RPC.\n-        assert('qt' in node.logging()['trace'])\n+        assert 'qt' in node.logging()['trace']\n         node.logging(exclude=['qt'])\n-        assert('qt' in node.logging()['excluded'])\n+        assert 'qt' in node.logging()['excluded']\n         node.logging(debug=['qt'])\n-        assert('qt' in node.logging()['debug'])\n+        assert 'qt' in node.logging()['debug']\n         node.logging(trace=['qt'])\n-        assert('qt' in node.logging()['trace'])\n+        assert 'qt' in node.logging()['trace']\n         node.logging(include=['qt'])\n-        assert('qt' in node.logging()['debug'])\n+        assert 'qt' in node.logging()['debug']\n\n         # Test logging RPC returns the logging categories in alphabetical order.\n         logging = node.logging()\n\n```"
  },
  {
   "t": "2026-04-02T16:11:18Z",
   "kind": "review_comment",
   "who": "stickies-v",
   "assoc": "MEMBER",
   "path": "test/functional/feature_logging.py",
   "commit": "6eee953bb8ee6fe465838fa7ce6cb4ff64ade543",
   "in_reply_to": null,
   "text": "nit: helper functions could clean up this verbosity quite a bit:\n\ngit diff on 6eee953bb8\n\n```diff\ndiff --git a/test/functional/feature_logging.py b/test/functional/feature_logging.py\nindex 3fcfd71615..07a82debb1 100755\n--- a/test/functional/feature_logging.py\n+++ b/test/functional/feature_logging.py\n@@ -90,14 +90,21 @@ class LoggingTest(BitcoinTestFramework):\n             '-nodebug'\n         ]\n\n+        def is_excluded(logging, cat):\n+            return cat in logging['excluded'] and cat not in logging['debug'] and cat not in logging['trace']\n+        def is_debug(logging, cat):\n+            return cat in logging['debug'] and cat not in logging['excluded'] and cat not in logging['trace']\n+        def is_trace(logging, cat):\n+            return cat in logging['trace'] and cat not in logging['excluded'] and cat not in logging['debug']\n+\n         for disable_debug_opt in disable_debug_options:\n             # Every category before disable_debug_opt will be ignored, including the invalid 'abc'\n             self.restart_node(0, ['-trace=0', '-debug=http', '-debug=abc', disable_debug_opt, '-debug=rpc', '-debug=net'])\n             logging = self.nodes[0].logging()\n-            assert 'http' in logging['excluded'] and 'http' not in (logging['debug'] + logging['trace'])\n+            assert is_excluded(logging, 'http')\n             assert 'abc' not in (logging['excluded'] + logging['debug'] + logging['trace'])\n-            assert 'rpc' in logging['debug'] and 'rpc' not in (logging['excluded'] + logging['trace'])\n-            assert 'net' in logging['debug'] and 'net' not in (logging['excluded'] + logging['trace'])\n+            assert is_debug(logging, 'rpc')\n+            assert is_debug(logging, 'net')\n\n         self.log.info(\"Test that -notrace,-trace=0,-trace=none clear previously specified trace options\")\n         disable_trace_options = [\n@@ -110,17 +117,17 @@ class LoggingTest(BitcoinTestFramework):\n             # Every category before disable_trace_opt will be ignored, including the invalid 'abc'\n             self.restart_node(0, ['-debug=0', '-trace=http', '-trace=abc', disable_trace_opt, '-trace=rpc', '-trace=net'])\n             logging = self.nodes[0].logging()\n-            assert 'http' in logging['excluded'] and 'http' not in (logging['debug'] + logging['trace'])\n+            assert is_excluded(logging, 'http')\n             assert 'abc' not in (logging['excluded'] + logging['debug'] + logging['trace'])\n-            assert 'rpc' in logging['trace'] and 'rpc' not in (logging['excluded'] + logging['debug'])\n-            assert 'net' in logging['trace'] and 'net' not in (logging['excluded'] + logging['debug'])\n+            assert is_trace(logging, 'rpc')\n+            assert is_trace(logging, 'net')\n\n             # Check that log values can be changed\n             logging = self.nodes[0].logging([\"mempool\"], [\"net\"], [\"http\"])\n-            assert 'net' in logging['excluded'] and 'net' not in (logging['debug'] + logging['trace'])\n-            assert 'mempool' in logging['debug'] and 'mempool' not in (logging['excluded'] + logging['trace'])\n-            assert 'http' in logging['trace'] and 'http' not in (logging['excluded'] + logging['debug'])\n-            assert 'rpc' in logging['trace'] and 'rpc' not in (logging['excluded'] + logging['debug']) # unchanged\n+            assert is_excluded(logging, 'net')\n+            assert is_debug(logging, 'mempool')\n+            assert is_trace(logging, 'http')\n+            assert is_trace(logging, 'rpc')  # unchanged\n\n         self.log.info(\"Test -logips formatting in net logs\")\n         self.restart_node(0, ['-debug=net', '-logips=1'])\n\n```"
  },
  {
   "t": "2026-04-02T16:22:43Z",
   "kind": "review_comment",
   "who": "stickies-v",
   "assoc": "MEMBER",
   "path": "src/logging.cpp",
   "commit": "befe8183dcdbb7e5ee4502b1391b1be6cc20324f",
   "in_reply_to": null,
   "text": "Review note: my understanding is that we could just as well only use this map for `Trace` overrides, but I think you're cleaning up this map logic in a follow-up so there's not much point overhauling things more than necessary?"
  },
  {
   "t": "2026-04-02T16:31:00Z",
   "kind": "review_comment",
   "who": "stickies-v",
   "assoc": "MEMBER",
   "path": "doc/release-notes-34038.md",
   "commit": "befe8183dcdbb7e5ee4502b1391b1be6cc20324f",
   "in_reply_to": null,
   "text": "nit: took me a few reads to properly parse the second sentence. Attempted simplification:\n```suggestion\n- The `logging` RPC command is now able to switch trace level logs on\n  and off, via its third parameter. Since a boolean can no longer represent a\n  category's state, the output format is changed to three arrays (excluded,\n  debug, trace) showing each category's logging level.. The\n  `-deprecatedrpc=logging` startup option can currently be used to revert to the\n  old format. (PR#34038)\n```"
  },
  {
   "t": "2026-04-02T16:35:36Z",
   "kind": "review",
   "who": "stickies-v",
   "assoc": "MEMBER",
   "state": "APPROVED",
   "commit": "6eee953bb8ee6fe465838fa7ce6cb4ff64ade543",
   "text": "ACK 6eee953bb8ee6fe465838fa7ce6cb4ff64ade543\n\nThe new `-debug` and `-trace` startup options are a lot easier to use, and although I think the RPC interface is getting increasingly unwieldy, this seems to be the best approach minimizing breaking changes to expose the trace level there too. There's quite a few places where internal logging logic could be cleaned up (left various comments in previous reviews), but my understanding is many of these will be addressed in future work that is better done in separate PRs, which I agree with."
  },
  {
   "t": "2026-04-02T18:05:32Z",
   "kind": "review_comment",
   "who": "ajtowns",
   "assoc": "MEMBER",
   "path": "src/logging.cpp",
   "commit": "befe8183dcdbb7e5ee4502b1391b1be6cc20324f",
   "in_reply_to": 3029052145,
   "text": "Yeah"
  },
  {
   "t": "2026-04-02T18:21:10Z",
   "kind": "force_push",
   "who": "ajtowns",
   "commit": "286ef780fd77f3f30d56e69a8a7f83c1736ea87f"
  },
  {
   "t": "2026-04-02T18:21:33Z",
   "kind": "comment",
   "who": "ajtowns",
   "assoc": "MEMBER",
   "text": "Bumped to address review comments."
  },
  {
   "t": "2026-04-03T02:35:51Z",
   "kind": "comment",
   "who": "stickies-v",
   "assoc": "MEMBER",
   "text": "re-ACK 286ef780fd77f3f30d56e69a8a7f83c1736ea87f"
  },
  {
   "t": "2026-05-07T13:26:41Z",
   "kind": "force_push",
   "who": "ajtowns",
   "commit": "5b114839c24f2298cd57c3d724f85f39400d6832"
  },
  {
   "t": "2026-05-07T13:32:50Z",
   "kind": "comment",
   "who": "ajtowns",
   "assoc": "MEMBER",
   "text": "Rebased past #21283 (psbtv2)"
  },
  {
   "t": "2026-05-07T13:41:01Z",
   "kind": "comment",
   "who": "stickies-v",
   "assoc": "MEMBER",
   "text": "re-ACK 5b114839c24f2298cd57c3d724f85f39400d6832"
  },
  {
   "t": "2026-05-11T13:30:03Z",
   "kind": "review",
   "who": "sedited",
   "assoc": "MEMBER",
   "state": "APPROVED",
   "commit": "5b114839c24f2298cd57c3d724f85f39400d6832",
   "text": "tACK 5b114839c24f2298cd57c3d724f85f39400d6832"
  },
  {
   "t": "2026-05-14T22:06:24Z",
   "kind": "comment",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "text": "Looking at 5b114839c24f2298cd57c3d724f85f39400d6832 and I plan to review, but will give my first impression here (sorry if I have made any mistakes.)\n\nIt seems like all we need to do here is to assign log levels to categories.\n\nSo if you want debug logging for the mempool, and info for everything else you could write:\n\n- `-loglevel=mempool:debug`\n- `bitcoin-cli logging '{\"mempool\": \"debug\"}'`\n\nOr if you want debug logging for everything, except info logging for libevent you could write:\n\n- `-loglevel=debug,libevent:info`\n- `bitcoin-cli logging '{\"all\": \"debug\", \"libevent\": \"info\"}'`\n\nOr if you want debug logging for everything, but tracing for the wallet you could write:\n\n- `-loglevel=debug,walletdb:trace`\n- `bitcoin-cli logging '{\"all\": \"debug\", \"walletdb\": \"trace\"}'`\n\n(Additionally, the `logging` RPC could return a map of categories to levels `{\"net\": \"info\", \"tor\", \"debug\", ...}`. And existing `-debug` and `-debugexclude` options could be kept for convenenience/compatibility, but be redundant and no longer necessary.)\n\nBy comparison the approach in this PR of requiring overlapping `-debug` `-trace` `-debugexclude` options to be used to control logging seems counterintuitive and unusual. The naming is confusing because you'd expect `-debug` and `-trace` to affect different mechanisms, but in this case they interact with each other (tracing turns on debug logging), and sometimes interact in odd ways (disabling debug logging does not turn off tracing). And `-debugexclude` interacts with `-trace` when based on its name you might expect only expect to interact with `-debug`.\n\nThe RPC interface here also seems awkward. Instead of straightforwardly assigning a level to a category, you place a category in one of three arrays. Instead of straightforwardly retrieving the level associated with a category, you search 3 arrays and check which one contains the category. Another peculiarity is that the RPC accepts category arrays in one order (debug, exclude, trace) but evaluates them in a different order (debug, trace, exclude).\n\nI guess my question with this PR is whether the straightforward-seeming approach of allowing the `-loglevel` option and `logging` RPC to directly assign categories to levels as described above was considered? Or is there some drawback to this approach? It would seem to remove complexity and provide a more standard interface."
  },
  {
   "t": "2026-05-18T12:36:25Z",
   "kind": "comment",
   "who": "stickies-v",
   "assoc": "MEMBER",
   "text": "FYI @ryanofsky we had a similar conversation here earlier: https://github.com/bitcoin/bitcoin/pull/34038#discussion_r2928796887 . I generally agree with your design suggestions, and have made similar ones myself, but I also agree with @ajtowns that less breakage is an important factor here.\n\nI think the current approach is fine in that it exposes what people need, with minimal breaking changes. I suspect very, very few people will actually use trace logging, so wasting too much of our own time discussing this and wasting downstream time to update their systems might not be a good trade-off for an interface that I'd find objectively a lot better but will benefit almost no real users. Enabling one/a few debug categories seems like the path most people will generally use, and is perfectly fine as-is."
  },
  {
   "t": "2026-05-20T11:00:14Z",
   "kind": "comment",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "text": "re: https://github.com/bitcoin/bitcoin/pull/34038#issuecomment-4477734070\n\n[quoted text omitted]\nI think the approach I'm suggesting is actually more backwards-compatible than the current PR, not less. IMO, the current `-debug/-debugexclude/logging` interface for turning debug logging on and off for different categories is simple and widely used and shouldn't be changed.\n\nBut trying to shoehorn tracing into the `-debug/-debugexclude/logging` interface is messy. It is also unnecessary, because we already have a `-loglevel` option that can straightforwardly be extended to work standalone and give complete control over logging. You can see an implementation in 159068f64108894df7976953aae2abbe4fc69f4 and 1818ee947547f742a6ca20a8d97f1953b20fb98 ([branch](https://github.com/ryanofsky/bitcoin/commits/pr/loglevel)). (Note: The branch is based on [#35322](https://github.com/bitcoin/bitcoin/pull/35322), but these changes don't require it.)"
  },
  {
   "t": "2026-05-21T22:47:48Z",
   "kind": "comment",
   "who": "ajtowns",
   "assoc": "MEMBER",
   "text": "[quoted text omitted]\n\nStaying compatible with a command line option that is marked debug-only and is largely unused, while completely breaking compatibility with an RPC that has been around for nine years does not seem \"more\" backwards-compatible.\n\n[quoted text omitted]\n`-debug` and `-debugexclude` are existing supported options, not something introduced by this PR, and not something that are particularly counterintuitive or unusual. Adding parallel options for trace level debugging likewise does not strike me as counterintuitive or unusual, though obviously that is a matter of opinion. If you want to have a productive discussion on that topic then #30486 remains available for that."
  },
  {
   "t": "2026-05-22T03:12:32Z",
   "kind": "force_push",
   "who": "ajtowns",
   "commit": "77ac0f81cd90234667ef7a2b7c05d53fb1239bd2"
  },
  {
   "t": "2026-05-22T03:51:45Z",
   "kind": "comment",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "text": "re: https://github.com/bitcoin/bitcoin/pull/34038#issuecomment-4513375922\n\n[quoted text omitted]\nRight. That's not what I am suggesting. My implementation in 1818ee947547f742a6ca20a8d97f1953b20fb98b ([branch](https://github.com/ryanofsky/bitcoin/commits/pr/loglevel), [release notes](https://github.com/ryanofsky/bitcoin/blob/1818ee947547f742a6ca20a8d97f1953b20fb98b/doc/release-notes-35322.md)) is more backwards compatible than this PR because it keeps behavior of `-debug/-debugexclude/logging` API unchanged.\n\nUnlike this PR, there is no `-deprecatedrpc=logging`. There isn't a confusing interface with three arrays specified in one order and evaluated in a different order. There isn't a new command line option `-trace` which interacts with `-debug` and `-debugexclude` while sounding unrelated, not having a documented precedence, and not supporting supporting basic things like tracing by default but using debug for specific categories.\n\nThis PR accomplishes two goals which I think are worthwhile: 1-Making it easier to turn on tracing. Currently turning on tracing requires specifying `-debug` in order for `-loglevel=trace` to work, which never made sense. 2-Making it possible to switch between trace/debug/info levels at runtime which was not previously possible. These goals are worthwhile because tracing is underused, leading to not enough information being available when debugging low level issues, and too much information logged at debug level that should use trace instead.\n\nBut the interface is too complicated and too confusing.\n\nWe do not need and should not want 3 command line options interacting with each other to control logging. We should provide a **single** command line option that straightforwardly allows assigning log levels to log categories, like normal logging frameworks allow, that works without other arguments and gives complete control over logging.\n\nThis is implemented in 159068f64108894df7976953aae2abbe4fc69f4f and is a simple change.\n\n[quoted text omitted]\n100% agreed. The `-debug` and `-debugexclude` options are convenient and a little quirky but mostly intuitive.  There's no reason not to continue supporting them. -debug becomes a synonym for -loglevel=debug, and -debugexclude becomes a synonym for -loglevel=info.\n\n[quoted text omitted]\nTrying to shoehorn tracing into -debug/-debugexclude is counterintuitive and broken for the reasons described above.\n\n[quoted text omitted]\nSounds good, I hadn't seen this issue. It's good that you opened it and it describes the problems with the current situation well.\n\nThere's just happens to be a simpler solution than the one described: Have a setting that allows assigning info/trace/debug levels to categories including \"all\", and have an RPC that does the same thing. Let info level be a synonym for debugging off. Let debug level be a synonym for debugging on. Let trace add tracing."
  },
  {
   "t": "2026-05-23T08:44:24Z",
   "kind": "review",
   "who": "sedited",
   "assoc": "MEMBER",
   "state": "APPROVED",
   "commit": "77ac0f81cd90234667ef7a2b7c05d53fb1239bd2",
   "text": "Re-ACK 77ac0f81cd90234667ef7a2b7c05d53fb1239bd2"
  },
  {
   "t": "2026-05-26T11:03:26Z",
   "kind": "comment",
   "who": "stickies-v",
   "assoc": "MEMBER",
   "text": "re-ACK 77ac0f81cd, no changes since 5b114839c24f2298cd57c3d724f85f39400d6832 except for trivially addressing merge conflict"
  },
  {
   "t": "2026-06-25T04:48:06Z",
   "kind": "force_push",
   "who": "ajtowns",
   "commit": "df78e3bf12ab77ed1d7770e5369d87f20fe62c61"
  },
  {
   "t": "2026-06-26T02:57:57Z",
   "kind": "force_push",
   "who": "ajtowns",
   "commit": "a9f22f598be947a31cd04fba1fae8027d90326d6"
  },
  {
   "t": "2026-06-26T11:53:30Z",
   "kind": "comment",
   "who": "stickies-v",
   "assoc": "MEMBER",
   "text": "re-ACK a9f22f598be947a31cd04fba1fae8027d90326d6 to address merge conflict from libevent removal, slight update to the functional test"
  },
  {
   "t": "2026-07-03T07:08:50Z",
   "kind": "force_push",
   "who": "ajtowns",
   "commit": "befe8183dcdbb7e5ee4502b1391b1be6cc20324f"
  },
  {
   "t": "2026-07-03T14:13:08Z",
   "kind": "review_comment",
   "who": "stickies-v",
   "assoc": "MEMBER",
   "path": "test/functional/rpc_misc.py",
   "commit": "befe8183dcdbb7e5ee4502b1391b1be6cc20324f",
   "in_reply_to": null,
   "text": "nit: `assert_equal(True, 'libevent' ...` is just a longer way to say `assert('libevent'...`?\n```suggestion\n        assert('libevent' not in all_result[\"debug\"])\n```"
  },
  {
   "t": "2026-07-03T16:04:10Z",
   "kind": "review",
   "who": "stickies-v",
   "assoc": "MEMBER",
   "state": "APPROVED",
   "commit": "befe8183dcdbb7e5ee4502b1391b1be6cc20324f",
   "text": "re-ACK befe8183dcdbb7e5ee4502b1391b1be6cc20324f"
  },
  {
   "t": "2026-07-04T08:00:27Z",
   "kind": "review_comment",
   "who": "ajtowns",
   "assoc": "MEMBER",
   "path": "test/functional/rpc_misc.py",
   "commit": "befe8183dcdbb7e5ee4502b1391b1be6cc20324f",
   "in_reply_to": 3520392020,
   "text": "It's preserving the style introduced in #35597 *shrug*"
  },
  {
   "t": "2026-07-04T08:40:15Z",
   "kind": "review_comment",
   "who": "stickies-v",
   "assoc": "MEMBER",
   "path": "test/functional/rpc_misc.py",
   "commit": "befe8183dcdbb7e5ee4502b1391b1be6cc20324f",
   "in_reply_to": 3520392020,
   "text": "I don't think that's a good motivation, but it's a nit, so not a blocker"
  },
  {
   "t": "2026-07-04T08:55:30Z",
   "kind": "review",
   "who": "sedited",
   "assoc": "MEMBER",
   "state": "APPROVED",
   "commit": "befe8183dcdbb7e5ee4502b1391b1be6cc20324f",
   "text": "Re-ACK befe8183dcdbb7e5ee4502b1391b1be6cc20324f"
  },
  {
   "t": "2026-07-15T03:02:23Z",
   "kind": "comment",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "text": "Concept ACK because making trace logging more accessible is a good goal, but approach NACK because having three options (`-debug`, `-trace`, `-debugexclude`) interact to control log levels is unnecessarily confusing when a single option should cover it.\n\nThe interactions aren't obvious from the names: `-trace=net` also enables debug logging, `-debugexclude=net` disables trace even though the name only mentions debug, and when both `-debug` and `-trace` apply to the same category, trace wins and the debug setting is ignored. That last point makes some reasonable configurations impossible: you can't set trace as the default log level while keeping specific categories at debug, because any per-category `-debug=<cat>` will be overridden by `-trace=all`. In most logging frameworks this is just `-loglevel=trace,net:debug`.\n\nI posted a more detailed analysis in #30486 and implemented an alternative in #35387 that handles all of this with a single option and is fully backwards compatible. If #35387 has design problems of its own, I'm interested to hear them and would withdraw this nack."
  },
  {
   "t": "2026-08-09T16:48:58Z",
   "kind": "comment",
   "who": "ajtowns",
   "assoc": "MEMBER",
   "text": "I don't agree with @ryanofsky's criticisms; I'm happy to debate the objective ones in #30486 (over a year old now!), or accept majority opinion on the subjective ones if anyone is willing to offer opinions."
  },
  {
   "t": "2026-08-09T19:31:08Z",
   "kind": "comment",
   "who": "sedited",
   "assoc": "MEMBER",
   "text": "I am fine with the changes here.\n\n[quoted text omitted]\nThis seems like the desired behaviour to me.\n\n[quoted text omitted]\nThis does seem like a reasonable thing to support, but I guess the analog of it is enumerating the categories at trace level, so there is a way to achieve the same behavior?"
  },
  {
   "t": "2026-08-21T08:37:20Z",
   "kind": "comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "text": "What is the status here? Looks like this is a feature with 80+ comments, but a recent question, and is tagged for 32.x, but it missed the feature freeze?\n\nMaybe it can be merged into 32.x, or removed from the milestone and merged early into 33.x? This way, there is a full release cycle to allow for fixups, if there is need for any of them?"
  },
  {
   "t": "2026-08-21T11:08:59Z",
   "kind": "comment",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "text": "re: https://github.com/bitcoin/bitcoin/pull/34038#issuecomment-5367551824\n\n[quoted text omitted]\nThis shouldn't be in the 32.0 milestone. There's only [one LogTrace](https://github.com/bitcoin/bitcoin/blob/f5e91c6fbae7dc0377dd382814ace063f50e2741/src/wallet/sqlite.cpp#L55) statement in the codebase (in sqlite code) so even if this PR were merged now it wouldn't benefit users. The goal of this PR, which is worthwhile, is to make it more practical to add `LogTrace` statements in the future. But this is not an urgent problem and the issue it addresses #30486 was open for 2 years without a comment.\n\nThe approach this PR takes to resolve the issue is also confusing and breaks RPC compatibilty and there is a simpler approach without these drawbacks in #35387 that should be considered and evaluated."
  },
  {
   "t": "2026-08-21T11:18:46Z",
   "kind": "comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "text": "Ok, removing milestone for now. It can be merged into 33.x, possibly early."
  }
 ],
 "labels_log": [
  {
   "t": "2025-12-10T06:01:32Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2025-12-10T11:26:40Z",
   "action": "labeled",
   "label": "Needs release note",
   "who": "fanquake"
  },
  {
   "t": "2025-12-11T01:52:09Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2025-12-11T03:48:59Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2025-12-11T21:36:13Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2025-12-14T14:08:27Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2025-12-15T05:12:33Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2025-12-18T12:42:36Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2025-12-20T06:49:43Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2025-12-20T07:45:49Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2025-12-20T09:41:05Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-01-27T13:32:42Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-01-28T05:00:45Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-01-28T18:55:42Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-01-29T02:09:01Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-02-07T23:59:13Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-02-11T03:43:41Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-02-11T04:15:35Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-02-11T05:33:05Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-02-20T11:10:20Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-02-20T13:26:46Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-02-20T13:37:33Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-02-24T12:25:18Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-03-11T16:18:38Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-03-11T17:42:24Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-03-20T01:51:25Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-03-20T06:30:03Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-03-24T10:46:23Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-03-24T13:03:49Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-03-31T17:37:28Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-04-02T07:14:07Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-04-28T08:13:44Z",
   "action": "unlabeled",
   "label": "Needs release note",
   "who": "maflcko"
  },
  {
   "t": "2026-05-05T13:48:34Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-05-07T14:08:11Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-06-22T14:22:44Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-06-25T05:08:49Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-06-25T05:48:59Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-06-26T04:00:27Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-06-30T17:31:29Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-07-03T08:14:25Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-07-03T09:11:46Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-07-04T09:16:26Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  }
 ],
 "state_log": [
  {
   "t": "2026-02-11T03:33:30Z",
   "kind": "renamed",
   "who": "ajtowns",
   "from": "logging: API improvements",
   "to": "logging: replace -loglevel with -trace, various API improvements"
  },
  {
   "t": "2026-03-11T16:18:36Z",
   "kind": "renamed",
   "who": "ajtowns",
   "from": "logging: replace -loglevel with -trace, various API improvements",
   "to": "logging: replace -loglevel with -trace, expose trace logging via RPC"
  },
  {
   "t": "2026-03-12T13:51:04Z",
   "kind": "milestoned",
   "who": "ajtowns"
  },
  {
   "t": "2026-08-21T11:17:52Z",
   "kind": "demilestoned",
   "who": "maflcko"
  }
 ],
 "text_chars": 54920,
 "text_tokens_estimate": 13730,
 "changed_paths": [],
 "files": [],
 "test_lines": null,
 "git": null,
 "input_hash": "bdf2cde9fa61839d",
 "extracted_at": "2026-09-17T16:15:31+00:00"
}