-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Preserve whitespaces for stacktraces #11
Comments
Note that it probably is a bug reporter issue since the JSON diagnostics contains the same thing, maybe worth checking the server logs response to ensure it remains formatted? |
I looked into this, but the json report is already formatted this way. I need to investigate on the metabase side and see how are error logs stored and see if it’s possible to maintain line breaks and other formatting |
Thanks, I fear that’d be the case let’s see if that’s easy enough to do
|
I used Claude to the rescue and it gave me this code, which worked nicely to highlight Metabase frames from the server stacktraces, and offering to hide the default clojure ones. import React, { useState } from 'react';
const StacktraceFormatter = () => {
const [input, setInput] = useState('');
const [showAllFrames, setShowAllFrames] = useState(false);
const prettyPrintClojure = (str) => {
let depth = 0;
let inString = false;
let formatted = '';
for (let i = 0; i < str.length; i++) {
const char = str[i];
// Handle string literals
if (char === '"' && str[i - 1] !== '\\') {
inString = !inString;
formatted += char;
continue;
}
if (inString) {
formatted += char;
continue;
}
// Handle brackets and braces
if (char === '{' || char === '[' || char === '(') {
depth++;
formatted += char + '\n' + ' '.repeat(depth);
} else if (char === '}' || char === ']' || char === ')') {
depth--;
formatted += '\n' + ' '.repeat(depth) + char;
} else if (char === ' ' && (str[i - 1] === ',' || str[i - 1] === '}' || str[i - 1] === ']' || str[i - 1] === ')')) {
formatted += '\n' + ' '.repeat(depth);
} else {
formatted += char;
}
}
return formatted;
};
const preprocessStacktrace = (trace) => {
if (!trace) return '';
return trace.replace(/\t+at\s+|\s+at\s+/g, '\n at ');
};
const formatStacktrace = (trace) => {
if (!trace) return [];
const preprocessed = preprocessStacktrace(trace);
const lines = preprocessed.split('\n');
const formattedLines = [];
// Process first line (exception)
if (lines[0]) {
const match = lines[0].match(/^([^{[\n]+)({.+|[.+])/s);
if (match) {
const [_, prefix, clojureData] = match;
formattedLines.push({
type: 'exception',
raw: lines[0].trim(),
formatted: prefix + '\n' + prettyPrintClojure(clojureData)
});
} else {
formattedLines.push({
type: 'exception',
raw: lines[0].trim(),
formatted: lines[0].trim()
});
}
}
// Process stack frames
const framePattern = /\s*at\s+([a-zA-Z0-9.$_/]+(?:\$[^(]+)?)\s*\(([\w.]+):(\d+)\)/;
lines.slice(1).forEach(line => {
const match = line.trim().match(framePattern);
if (match) {
const [_, fullMethod, file, lineNum] = match;
const lastDotIndex = fullMethod.lastIndexOf('.');
const namespace = fullMethod.substring(0, lastDotIndex);
const method = fullMethod.substring(lastDotIndex + 1);
const isMetabaseFrame = namespace.startsWith('metabase');
if (showAllFrames || isMetabaseFrame) {
formattedLines.push({
type: 'frame',
namespace,
method,
file,
lineNum: parseInt(lineNum),
raw: line.trim(),
isMetabaseFrame
});
}
}
});
return formattedLines;
};
const frames = formatStacktrace(input);
const metabaseFrames = frames.filter(f => f.type === 'frame' && f.isMetabaseFrame);
return (
<div className="p-4 max-w-4xl">
<textarea
className="w-full p-2 border rounded mb-4 font-mono text-sm h-48"
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="Paste Clojure stacktrace here..."
/>
<div className="mb-4 flex items-center space-x-4">
<label className="flex items-center space-x-2">
<input
type="checkbox"
checked={showAllFrames}
onChange={(e) => setShowAllFrames(e.target.checked)}
className="rounded"
/>
<span className="text-sm">Show all frames</span>
</label>
{metabaseFrames.length > 0 && (
<span className="text-sm text-gray-600">
{metabaseFrames.length} Metabase frames
</span>
)}
</div>
<div className="bg-gray-50 p-4 rounded border">
{frames.map((line, i) => {
if (line.type === 'exception') {
return (
<div key={i} className="font-mono text-sm mb-4 pl-4 border-l-4 border-red-500">
<div className="text-gray-800 whitespace-pre">{line.formatted}</div>
</div>
);
}
return (
<div
key={i}
className={`font-mono text-sm mb-1 ${line.isMetabaseFrame ? 'pl-4 border-l-4 border-yellow-500 bg-yellow-50' : ''}`}
>
<span className="text-gray-500">at </span>
<span className={line.isMetabaseFrame ? 'text-blue-600' : 'text-gray-600'}>{line.namespace}</span>
<span className="text-purple-600">.{line.method}</span>
<span className="text-gray-600"> (</span>
<span className="text-green-600">{line.file}:{line.lineNum}</span>
<span className="text-gray-600">)</span>
</div>
);
})}
</div>
</div>
);
};
export default StacktraceFormatter; |
It looks like we're not preserving whitespaces making it close to impossible to read the stacktrace:
The text was updated successfully, but these errors were encountered: