{
 "number": 35387,
 "repo": "bitcoin/bitcoin",
 "url": "https://github.com/bitcoin/bitcoin/pull/35387",
 "title": "logging: make trace logging easily usable",
 "author": "ryanofsky",
 "author_association": "CONTRIBUTOR",
 "created_at": "2026-05-26T20:34:12Z",
 "updated_at": "2026-08-28T13:38:59Z",
 "age_days": 113,
 "draft": false,
 "labels": [],
 "milestone": null,
 "base": "master",
 "head_sha": "dffb8cf9e7bda3a1bf1f26e1760e486006ae6a63",
 "head_ref": "pr/loglevel",
 "head_repo": "ryanofsky/bitcoin",
 "head_history": [
  {
   "t": "2026-05-27T00:50:35Z",
   "sha": "828a0287fa194b7b94e600a10e06e21f74434366"
  },
  {
   "t": "2026-06-24T14:40:27Z",
   "sha": "bcf1c44df17a2c6ec3ff04582ee435b67cf2f187"
  },
  {
   "t": "2026-07-14T00:37:24Z",
   "sha": "dffb8cf9e7bda3a1bf1f26e1760e486006ae6a63"
  }
 ],
 "additions": 290,
 "deletions": 41,
 "changed_files": 11,
 "commit_count": 2,
 "size_bucket": "M",
 "mergeable_state": "blocked",
 "bot": {
  "drahtbot": {
   "present": true,
   "reviews": {},
   "conflicts": [
    {
     "number": 35322,
     "title": "logging: streamline Logger state and drop redundant methods",
     "author": "ryanofsky"
    },
    {
     "number": 34038,
     "title": "logging: replace -loglevel with -trace, expose trace logging via RPC",
     "author": "ajtowns"
    },
    {
     "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": {},
 "acks_tally": {
  "ack": 0,
  "stale_ack": 0,
  "concept_ack": 0,
  "approach_ack": 0,
  "nack": 0,
  "concept_nack": 0,
  "approach_nack": 0
 },
 "reviews": {
  "approved": 0,
  "changes_requested": 0,
  "distinct_reviewers": [
   "stickies-v"
  ]
 },
 "signals": {
  "needs_rebase": false,
  "ci_failed": false,
  "mergeable_state": "blocked",
  "last_author_activity": "2026-07-14T00:37:24Z",
  "last_reviewer_activity": "2026-05-27T10:46:40Z",
  "last_reviewer": "stickies-v",
  "author_silent_days": 65,
  "waiting_on_author_days": 0,
  "days_since_update": 20
 },
 "refs": {
  "mentioned": [
   34038,
   34411,
   35597
  ],
  "depends_on": [],
  "fixes": [],
  "linked_issues": [],
  "references": [
   {
    "number": 34038,
    "type": "pull",
    "state": "open",
    "merged": false,
    "merged_at": null,
    "title": "logging: replace -loglevel with -trace, expose trace logging via RPC"
   },
   {
    "number": 34411,
    "type": "pull",
    "state": "closed",
    "merged": true,
    "merged_at": "2026-06-23",
    "title": "Full Libevent removal"
   },
   {
    "number": 35597,
    "type": "pull",
    "state": "closed",
    "merged": true,
    "merged_at": "2026-06-30",
    "title": "logging: More fully remove libevent log category"
   }
  ],
  "conflicts": [
   35322,
   34038,
   17783,
   17581,
   17580,
   17493
  ]
 },
 "stack": {
  "shares_commits_with": [],
  "based_on": [],
  "base_for": []
 },
 "review_paths": [
  "src/rpc/node.cpp"
 ],
 "body": "**Problem**: Currently it is difficult to enable `trace` logging.\n\nThe `-loglevel=trace` setting works, but does nothing by default and needs to be combined with `-debug` settings to have an effect.\n\nAdditionally, it is not possible to switch between `debug` and `trace` logging at runtime without restarting the node. These problems make `trace` logging less useful than it could be, effectively make existing `trace` logs undiscoverable, and making it not worthwhile to add new tracing.\n\n**Solution**: Fix these issues by allowing the `-loglevel` option to work without an accompanying `-debug` option, and by adding a matching `loglevel` RPC that allows log levels to be configured at runtime.\n\n**Usage examples**:\n```bash\nbitcoin rpc loglevel                               # See current log levels\nbitcoin rpc loglevel trace                         # Set global level to -loglevel=trace\nbitcoin rpc loglevel debug                         # Set global level to -loglevel=debug\nbitcoin rpc loglevel info                          # Set global level to -loglevel=info\nbitcoin rpc loglevel libevent=info                 # Set category level to -loglevel=libevent:info\nbitcoin rpc loglevel trace net=debug libevent=info # Set global & category levels to -loglevel=trace,net:debug,libevent:info\n```\n\n**Compatibility:** This PR is backwards compatible and doesn't change the `-debug` and `-debugexclude` options (which become synonyms for `-loglevel=debug` and `-loglevel=info`, respectively). The `logging` RPC is also mostly unchanged, except now it validates category names before updating log levels, instead of failing with half-applied changes. Also the `logging` RPC now only toggles between `info` and `debug` levels instead of trying to remember previously assigned levels, which was needlessly confusing.",
 "commits": [
  {
   "sha": "c3700df0201e0d53a28df52450421fdfd2f87b5e",
   "date": "2026-07-13T20:29:09Z",
   "message": "logging: make -loglevel work standalone without -debug\n\nPreviously, -loglevel only set level thresholds but required -debug to\nbe separately specified to actually enable log categories. This made\n-loglevel harder to use than necessary.\n\nAfter this change, -loglevel enables categories automatically:\n- A global level (-loglevel=debug) enables all categories at that level,\n  replacing the need for -debug=1.\n- A per-category level (-loglevel=net:trace) enables that category,\n  replacing the need for -debug=net.\n- Comma-separated entries in one argument are now supported\n  (-loglevel=debug,net:trace).\n\nThe three logging options now have a fixed processing order and clear\nprecedence: -debug is processed first (lowest precedence), then\n-loglevel, then -debugexclude (highest precedence). This ordering holds\nregardless of the order arguments appear on the command line or in\nconfig files. Equivalences:\n  -debug=1        ==  -loglevel=debug\n  -debug=<cat>    ==  -loglevel=<cat>:debug\n  -debugexclude=<cat>  ==  -loglevel=<cat>:info\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>"
  },
  {
   "sha": "dffb8cf9e7bda3a1bf1f26e1760e486006ae6a63",
   "date": "2026-07-13T20:29:09Z",
   "message": "rpc: add loglevel RPC\n\nAdd loglevel RPC to control log levels.\n\nCo-Authored-By: stickies-v <stickies-v@protonmail.com>\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>"
  }
 ],
 "timeline": [
  {
   "t": "2026-05-27T00:50:35Z",
   "kind": "force_push",
   "who": "ryanofsky",
   "commit": "828a0287fa194b7b94e600a10e06e21f74434366"
  },
  {
   "t": "2026-05-27T00:50:52Z",
   "kind": "comment",
   "who": "ryanofsky",
   "assoc": "CONTRIBUTOR",
   "text": "Updated 2e4ac6d568af2ae0bf3e81748ba1a63d0cdb71b8 -> 828a0287fa194b7b94e600a10e06e21f74434366 ([`pr/loglevel.2`](https://github.com/ryanofsky/bitcoin/commits/pr/loglevel.2) -> [`pr/loglevel.3`](https://github.com/ryanofsky/bitcoin/commits/pr/loglevel.3), [compare](https://github.com/ryanofsky/bitcoin/compare/pr/loglevel.2..pr/loglevel.3)) to fix clang-tidy and other ci errors https://github.com/bitcoin/bitcoin/actions/runs/26473571406/job/77954549771?pr=35387\n\nRebased 828a0287fa194b7b94e600a10e06e21f74434366 -> bcf1c44df17a2c6ec3ff04582ee435b67cf2f187 ([`pr/loglevel.3`](https://github.com/ryanofsky/bitcoin/commits/pr/loglevel.3) -> [`pr/loglevel.4`](https://github.com/ryanofsky/bitcoin/commits/pr/loglevel.4), [compare](https://github.com/ryanofsky/bitcoin/compare/pr/loglevel.3-rebase..pr/loglevel.4)) due to conflict with #34411 (Full Libevent removal)\n\nRebased bcf1c44df17a2c6ec3ff04582ee435b67cf2f187 -> dffb8cf9e7bda3a1bf1f26e1760e486006ae6a63 ([`pr/loglevel.4`](https://github.com/ryanofsky/bitcoin/commits/pr/loglevel.4) -> [`pr/loglevel.5`](https://github.com/ryanofsky/bitcoin/commits/pr/loglevel.5), [compare](https://github.com/ryanofsky/bitcoin/compare/pr/loglevel.4-rebase..pr/loglevel.5)) due to conflict with #35597"
  },
  {
   "t": "2026-05-27T10:46:32Z",
   "kind": "review_comment",
   "who": "stickies-v",
   "assoc": "CONTRIBUTOR",
   "path": "src/rpc/node.cpp",
   "commit": "828a0287fa194b7b94e600a10e06e21f74434366",
   "in_reply_to": null,
   "text": "nit: the `MaybeArg` helper is better suited here:\n\ngit diff on 828a0287fa\n\n```diff\ndiff --git a/src/rpc/node.cpp b/src/rpc/node.cpp\nindex 6de4562495..88bf0404f9 100644\n--- a/src/rpc/node.cpp\n+++ b/src/rpc/node.cpp\n@@ -235,7 +235,7 @@ static RPCMethod loglevel()\n             \"The valid log levels are: \" + LogInstance().LogLevelsString() + \"\\n\"\n             ,\n                 {\n-                    {\"all\", RPCArg::Type::STR, RPCArg::Optional::OMITTED, \"Log level to set for all categories.\"},\n+                    {\"level\", RPCArg::Type::STR, RPCArg::Optional::OMITTED, \"Log level to set for all categories.\"},\n                     {\"categories\", RPCArg::Type::OBJ_NAMED_PARAMS, RPCArg::Optional::OMITTED, \"Per-category log levels.\", std::move(category_args)},\n                 },\n                 RPCResult{\n@@ -255,21 +255,19 @@ static RPCMethod loglevel()\n     std::vector<std::pair<BCLog::LogFlags, BCLog::Level>> changes;\n\n     // Optional positional \"level\" param.\n-    if (!request.params[0].isNull()) {\n-        const std::string level_str = request.params[0].get_str();\n-        const auto level = BCLog::Logger::GetLogLevel(level_str);\n+    if (auto level_str{self.MaybeArg<std::string_view>(\"level\")}) {\n+        const auto level = BCLog::Logger::GetLogLevel(*level_str);\n         if (!level || *level > BCLog::Level::Info) {\n-            throw JSONRPCError(RPC_INVALID_PARAMETER, \"unknown log level \\\"\" + level_str + \"\\\". Valid values: \" + LogInstance().LogLevelsString());\n+            throw JSONRPCError(RPC_INVALID_PARAMETER, tfm::format(\"unknown log level \\\"%s\\\". Valid values: %s\", *level_str, LogInstance().LogLevelsString()));\n         }\n         changes.emplace_back(BCLog::ALL, *level);\n     }\n\n     // Named \"categories\" params: applied in the order they appear in the request.\n     // Category names are validated by OBJ_NAMED_PARAMS, so GetLogCategory always succeeds here.\n-    if (!request.params[1].isNull()) {\n-        const UniValue& cats = request.params[1].get_obj();\n-        for (const std::string& cat : cats.getKeys()) {\n-            const std::string level_str = cats[cat].get_str();\n+    if (auto cats{self.MaybeArg<UniValue>(\"categories\")}) {\n+        for (const std::string& cat : cats->getKeys()) {\n+            const std::string level_str = (*cats)[cat].get_str();\n             const auto level = BCLog::Logger::GetLogLevel(level_str);\n             if (!level || *level > BCLog::Level::Info) {\n                 throw JSONRPCError(RPC_INVALID_PARAMETER, \"unknown log level \\\"\" + level_str + \"\\\". Valid values: \" + LogInstance().LogLevelsString());\n\n```"
  },
  {
   "t": "2026-05-27T10:46:40Z",
   "kind": "review",
   "who": "stickies-v",
   "assoc": "CONTRIBUTOR",
   "state": "COMMENTED",
   "commit": "828a0287fa194b7b94e600a10e06e21f74434366",
   "text": "This looks like an elegant approach, I think it's an intuitive RPC and `-loglevel` interface. The PR description should probably reference #34038 as an alternativate?\n\nI don't think we should have 2 parallel active logging RPC methods, so imo this PR should mark `logging` deprecated, and we should keep it around for a long time as it's probably heavily used."
  },
  {
   "t": "2026-06-24T14:40:27Z",
   "kind": "force_push",
   "who": "ryanofsky",
   "commit": "bcf1c44df17a2c6ec3ff04582ee435b67cf2f187"
  },
  {
   "t": "2026-07-14T00:37:24Z",
   "kind": "force_push",
   "who": "ryanofsky",
   "commit": "dffb8cf9e7bda3a1bf1f26e1760e486006ae6a63"
  }
 ],
 "labels_log": [
  {
   "t": "2026-05-26T22:07:37Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-05-27T02:09:51Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-06-22T14:10:43Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-06-24T16:00:34Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-06-30T17:24:06Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-07-14T00:45:07Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  }
 ],
 "state_log": [
  {
   "t": "2026-08-21T11:09:28Z",
   "kind": "ready_for_review",
   "who": "ryanofsky"
  }
 ],
 "text_chars": 7321,
 "text_tokens_estimate": 1830,
 "changed_paths": [
  "doc/release-notes-35387.md",
  "src/init.cpp",
  "src/init/common.cpp",
  "src/init/common.h",
  "src/logging.cpp",
  "src/logging.h",
  "src/rpc/client.cpp",
  "src/rpc/node.cpp",
  "src/test/fuzz/rpc.cpp",
  "src/test/logging_tests.cpp",
  "test/functional/feature_logging.py"
 ],
 "files": [
  {
   "path": "doc/release-notes-35387.md",
   "add": 27,
   "del": 0
  },
  {
   "path": "src/init.cpp",
   "add": 1,
   "del": 0
  },
  {
   "path": "src/init/common.cpp",
   "add": 44,
   "del": 12
  },
  {
   "path": "src/init/common.h",
   "add": 1,
   "del": 0
  },
  {
   "path": "src/logging.cpp",
   "add": 14,
   "del": 2
  },
  {
   "path": "src/logging.h",
   "add": 5,
   "del": 6
  },
  {
   "path": "src/rpc/client.cpp",
   "add": 1,
   "del": 0
  },
  {
   "path": "src/rpc/node.cpp",
   "add": 95,
   "del": 19
  },
  {
   "path": "src/test/fuzz/rpc.cpp",
   "add": 1,
   "del": 0
  },
  {
   "path": "src/test/logging_tests.cpp",
   "add": 1,
   "del": 0
  },
  {
   "path": "test/functional/feature_logging.py",
   "add": 100,
   "del": 2
  }
 ],
 "test_lines": 104,
 "git": {
  "head": "dffb8cf9e7bda3a1bf1f26e1760e486006ae6a63",
  "head_matches_backup": true,
  "base": "1ab1fdd4696b269db0c93e240478d589eeb37151",
  "commits": [
   {
    "sha": "c3700df020",
    "subject": "logging: make -loglevel work standalone without -debug",
    "files": 6,
    "add": 99,
    "del": 14
   },
   {
    "sha": "dffb8cf9e7",
    "subject": "rpc: add loglevel RPC",
    "files": 7,
    "add": 191,
    "del": 27
   }
  ],
  "patch_truncated": false
 },
 "input_hash": "04ccf7161b42337b",
 "extracted_at": "2026-09-17T16:15:31+00:00"
}