How to Get Console Logs From a User (Without DevTools)
October 26, 2026 · 2 min read · by the Tracebird team
To get console logs from a user, either walk them through DevTools and have them save the output, or record the console in your app and attach it to every report automatically. The first works once. The second works every time.
Every support page tells a user to press F12, open the Console tab, right-click and save. It is a fair answer for one stubborn bug. It is a poor answer for a tester who just wants to tell you that the checkout button hangs.
Why asking users for console logs fails
The usual guide has five steps and three browsers to cover. Chrome and Edge let you right-click the console and save it. Firefox has a "Save all messages to file" option. Safari needs the Develop menu turned on first.
That is where most people stop. Even a patient tester will open the console after the bug happened, and by then the page may have reloaded and the log is empty. You get a screenshot of an unrelated warning, or nothing.
If a bug depends on network calls, you also have to ask for a HAR file, which is a second round of instructions.
The manual way, when you have no choice
If you must ask a person, keep the message short and ordered:
- Open the page where the problem happens, then open DevTools (F12, or Cmd+Option+I on a Mac).
- Go to the Console tab and turn on "Preserve log" so a reload does not wipe it.
- Repeat the steps that break the page.
- Right-click inside the console, choose save, and attach the file to your reply.
- Tell us the browser, its version and the URL.
Ask for the browser and URL every time. A log without them is hard to place, and it leads to the classic cannot reproduce dead end.
The automatic way: record the console in your app
You can wrap the console methods in a small script and keep a rolling buffer of the last entries. When something goes wrong, you send the buffer with the report.
const buffer = [];
for (const level of ["log", "warn", "error"]) {
const original = console[level];
console[level] = (...args) => {
buffer.push({ level, time: Date.now(), message: args.map(String).join(" ") });
if (buffer.length > 200) buffer.shift();
original.apply(console, args);
};
}
window.addEventListener("error", (e) => {
buffer.push({ level: "error", time: Date.now(), message: e.message });
});
This catches what your own code prints and uncaught errors. It does not catch everything: errors raised before the script loads are missed, browser-level messages never pass through your wrapper, and objects need careful serialising. Strip tokens, emails and anything personal before the buffer leaves the device.
Logs alone are not the whole story
A console log tells you what failed, not what the user did before it. Pair it with the failed network calls, the page URL and a recording of the session, and a report turns from a guess into something you can act on.
That is the idea behind Tracebird: one SDK for web and Flutter that attaches the annotated capture, console, network calls and session replay to each report, so your testers never have to open DevTools. Tracebird also groups duplicates, which helps when ten testers hit the same error. If you are on Flutter, see how to attach logs to a Flutter bug report.
Whichever route you pick, decide it before the next release. A rolling buffer takes an afternoon to add, and the first time a tester hits an error you can read in full, it pays for itself.