Skip to content

Commit ffa9dd2

Browse files
committed
feat: improve stack trace formatting
1 parent 6883948 commit ffa9dd2

File tree

6 files changed

+116
-9
lines changed

6 files changed

+116
-9
lines changed

resources/views/comment.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
## Error Details
21
**Type:** {level}
32
**Message:** {message}
43

resources/views/issue.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
## Error Details
21
**Log Level:** {level}
32
**Class:** {class}
43
**Message:** {message}

src/Issues/Formatters/ExceptionFormatter.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public function format(LogRecord $record): array
2929
return [
3030
'message' => $exception->getMessage(),
3131
'simplified_stack_trace' => $header."\n[stacktrace]\n".$this->stackTraceFormatter->format($stackTrace),
32-
'full_stack_trace' => $header."\n[stacktrace]\n".$stackTrace,
32+
'full_stack_trace' => $header."\n[stacktrace]\n".$this->stackTraceFormatter->format($stackTrace, collapseVendorFrames: false),
3333
];
3434
}
3535

src/Issues/Formatters/StackTraceFormatter.php

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ class StackTraceFormatter
99
{
1010
private const VENDOR_FRAME_PLACEHOLDER = '[Vendor frames]';
1111

12-
public function format(string $stackTrace): string
12+
public function format(string $stackTrace, bool $collapseVendorFrames = true): string
1313
{
1414
return collect(explode("\n", $stackTrace))
1515
->filter(fn ($line) => ! empty(trim($line)))
16-
->map(function ($line) {
16+
->map(function ($line) use ($collapseVendorFrames) {
1717
if (trim($line) === '"}') {
1818
return '';
1919
}
@@ -30,15 +30,15 @@ public function format(string $stackTrace): string
3030

3131
// Stack trace lines start with #\d. Here we pad the numbers 0-9
3232
// with a preceding zero to keep everything in line visually.
33-
$line = preg_replace('/^(\e\[0m)#(\d)(?!\d)/', '$1#0$2', $line);
33+
$line = preg_replace('/^#(\d)(?!\d)/', '#0$1', $line);
3434

35-
if ($this->isVendorFrame($line)) {
35+
if ($collapseVendorFrames && $this->isVendorFrame($line)) {
3636
return self::VENDOR_FRAME_PLACEHOLDER;
3737
}
3838

3939
return $line;
4040
})
41-
->pipe($this->collapseVendorFrames(...))
41+
->pipe(fn ($lines) => $collapseVendorFrames ? $this->collapseVendorFrames($lines) : $lines)
4242
->join("\n");
4343
}
4444

@@ -56,6 +56,7 @@ private function isVendorFrame($line): bool
5656
{
5757
return str_contains((string) $line, self::VENDOR_FRAME_PLACEHOLDER)
5858
|| str_contains((string) $line, '/vendor/') && ! Str::isMatch("/BoundMethod\.php\([0-9]+\): App/", $line)
59+
|| str_contains((string) $line, '/artisan')
5960
|| str_ends_with($line, '{main}');
6061
}
6162

tests/Issues/Formatters/StackTraceFormatterTest.php

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
<?php
22

3+
namespace Naoray\LaravelGithubMonolog\Tests\Issues\Formatters;
4+
35
use Naoray\LaravelGithubMonolog\Issues\Formatters\StackTraceFormatter;
46

57
beforeEach(function () {
@@ -54,3 +56,110 @@
5456
->toContain('/app/Services/TestService.php')
5557
->not->toContain('[Vendor frames]');
5658
});
59+
60+
test('it replaces base path in stack traces', function () {
61+
$formatter = new StackTraceFormatter();
62+
$basePath = base_path();
63+
64+
$stackTrace = <<<TRACE
65+
#0 {$basePath}/app/Services/ImageService.php(25): Spatie\\Image\\Image->loadFile()
66+
#1 {$basePath}/vendor/laravel/framework/src/Foundation/Application.php(1235): App\\Services\\ImageService->process()
67+
#2 {$basePath}/artisan(13): Illuminate\\Foundation\\Application->run()
68+
TRACE;
69+
70+
// Test simplified trace (with vendor frame collapsing)
71+
expect($formatter->format($stackTrace, true))
72+
->toBe(<<<TRACE
73+
#00 /app/Services/ImageService.php(25): Spatie\\Image\\Image->loadFile()
74+
[Vendor frames]
75+
TRACE
76+
);
77+
78+
// Test full trace (without vendor frame collapsing)
79+
expect($formatter->format($stackTrace, false))
80+
->toBe(<<<TRACE
81+
#00 /app/Services/ImageService.php(25): Spatie\\Image\\Image->loadFile()
82+
#01 /vendor/laravel/framework/src/Foundation/Application.php(1235): App\\Services\\ImageService->process()
83+
#02 /artisan(13): Illuminate\\Foundation\\Application->run()
84+
TRACE
85+
);
86+
});
87+
88+
test('it handles empty base path correctly', function () {
89+
$formatter = new StackTraceFormatter();
90+
91+
$stackTrace = <<<TRACE
92+
#0 /absolute/path/to/app/Services/ImageService.php(25): someMethod()
93+
#1 /vendor/package/src/File.php(10): otherMethod()
94+
TRACE;
95+
96+
$result = $formatter->format($stackTrace, true);
97+
98+
expect($result)
99+
->toBe(<<<TRACE
100+
#00 /absolute/path/to/app/Services/ImageService.php(25): someMethod()
101+
[Vendor frames]
102+
TRACE
103+
);
104+
});
105+
106+
test('it preserves non-stack-trace lines', function () {
107+
$formatter = new StackTraceFormatter();
108+
109+
$stackTrace = <<<TRACE
110+
[2024-03-21 12:00:00] Error: Something went wrong
111+
#0 /app/Services/Service.php(25): someMethod()
112+
#1 /vendor/package/src/File.php(10): otherMethod()
113+
TRACE;
114+
115+
$result = $formatter->format($stackTrace, true);
116+
117+
expect($result)
118+
->toBe(<<<TRACE
119+
[2024-03-21 12:00:00] Error: Something went wrong
120+
#00 /app/Services/Service.php(25): someMethod()
121+
[Vendor frames]
122+
TRACE
123+
);
124+
});
125+
126+
test('it recognizes artisan lines as vendor frames', function () {
127+
$formatter = new StackTraceFormatter();
128+
$basePath = base_path();
129+
130+
$stackTrace = <<<TRACE
131+
#0 {$basePath}/app/Console/Commands/ImportCommand.php(25): handle()
132+
#1 {$basePath}/artisan(13): Illuminate\\Foundation\\Application->run()
133+
#2 {$basePath}/artisan(37): require()
134+
TRACE;
135+
136+
// Test simplified trace (with vendor frame collapsing)
137+
expect($formatter->format($stackTrace, true))
138+
->toBe(<<<TRACE
139+
#00 /app/Console/Commands/ImportCommand.php(25): handle()
140+
[Vendor frames]
141+
TRACE
142+
);
143+
144+
// Test that artisan lines are preserved in full trace
145+
expect($formatter->format($stackTrace, false))
146+
->toBe(<<<TRACE
147+
#00 /app/Console/Commands/ImportCommand.php(25): handle()
148+
#01 /artisan(13): Illuminate\\Foundation\\Application->run()
149+
#02 /artisan(37): require()
150+
TRACE
151+
);
152+
});
153+
154+
test('it collapses multiple artisan lines into single vendor frame', function () {
155+
$formatter = new StackTraceFormatter();
156+
157+
$stackTrace = <<<TRACE
158+
#0 /artisan(13): Illuminate\\Foundation\\Application->run()
159+
#1 /vendor/laravel/framework/src/Foundation/Application.php(1235): handle()
160+
#2 /artisan(37): require()
161+
TRACE;
162+
163+
expect($formatter->format($stackTrace, true))
164+
->toBe('[Vendor frames]');
165+
});

tests/Issues/TemplateRendererTest.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,6 @@
9191
);
9292

9393
expect($rendered)
94-
->toContain('## Error Details')
9594
->toContain('**Type:** ERROR')
9695
->toContain('<!-- Signature: test -->');
9796
});

0 commit comments

Comments
 (0)