Skip to content

Commit

Permalink
Merge pull request #26 from jobindex/mojo-template-integration
Browse files Browse the repository at this point in the history
Mojo::Template integration
  • Loading branch information
pmb0 authored Nov 18, 2024
2 parents d7f5b11 + 1b63487 commit ca26533
Show file tree
Hide file tree
Showing 4 changed files with 160 additions and 2 deletions.
2 changes: 2 additions & 0 deletions lib/Sentry/Integration.pm
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ use Sentry::Hub::Scope;
use Sentry::Integration::DBI;
use Sentry::Integration::DieHandler;
use Sentry::Integration::MojoUserAgent;
use Sentry::Integration::MojoTemplate;
use Sentry::Integration::LwpUserAgent;

my @Global_integrations = (
Sentry::Integration::DieHandler->new,
Sentry::Integration::DBI->new,
Sentry::Integration::MojoUserAgent->new,
Sentry::Integration::MojoTemplate->new,
Sentry::Integration::LwpUserAgent->new,
);

Expand Down
65 changes: 65 additions & 0 deletions lib/Sentry/Integration/MojoTemplate.pm
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package Sentry::Integration::MojoTemplate;
use Mojo::Base 'Sentry::Integration::Base', -signatures;

use Try::Tiny;
use Sentry::Util 'around';

has tracing => 1;
has fix_stacktrace => 1;

sub setup_once ($self, $add_global_event_processor, $get_current_hub) {
around(
'Mojo::Template',
render => sub ($orig, $mojo_template, @args) {
my $hub = $get_current_hub->();
my $parent_span = $self->tracing && $hub->get_current_scope->get_span;
my $output;
$hub->with_scope(sub ($scope) {
my $namespace = $mojo_template->namespace;

my $span;
if ($parent_span) {
$span = $parent_span->start_child({
op => 'mojo.template',
description => $mojo_template->name,
data => {
compiled => $mojo_template->compiled ? 'yes' : 'no',
},
});
$scope->set_span($span);
}

try {
$output = $orig->($mojo_template, @args);
if ($self->fix_stacktrace && ref $output && $output->isa('Mojo::Exception')) {
_fix_template_stack_frames($namespace, $output);
}
} finally {
$span->finish() if $span;
};
});
return $output;
}
);
}

sub _fix_template_stack_frames($namespace, $exc) {
for my $frame ($exc->frames->@*) {
# Frames coming from Mojo templates will have their module set to
# $namespace which is not very useful since it will be the same for all
# templates. Additionally, if using Mojolicious::Plugin::EPRenderer, the
# namespace will contain a hash value, which means it will mess up issue
# grouping.
# Remove module and subroutine from the frame so that Sentry falls back
# to using the filename in the UI and for grouping.
if ($frame->[0] && $frame->[0] eq $namespace && $frame->[1]) {
$frame->[0] = ''; # module
$frame->[3] = ''; # subroutine
} elsif ($frame->[3]) {
# Remove namespace from subroutine name
$frame->[3] =~ s/^${namespace}:://;
}
}
}

1;
11 changes: 9 additions & 2 deletions lib/Sentry/Util.pm
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use Mojo::Loader qw(load_class);
use Mojo::Util qw(dumper monkey_patch);
use UUID::Tiny ':std';

our @EXPORT_OK = qw(uuid4 truncate merge around);
our @EXPORT_OK = qw(uuid4 truncate merge around restore_original);

sub uuid4 {
my $uuid = create_uuid_as_string(UUID_V4);
Expand Down Expand Up @@ -39,9 +39,16 @@ sub around ($package, $method, $cb) {

monkey_patch $package, $method => sub { $cb->($orig, @_) };

$Patched{$key} = 1;
$Patched{$key} = $orig;

return;
}

sub restore_original ($package, $method) {
my $key = $package . '::' . $method;
my $orig = $Patched{$key} or return;
monkey_patch $package, $method, $orig;
delete $Patched{$key};
}

1;
84 changes: 84 additions & 0 deletions t/integration-mojo-template.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
use Mojo::Base -strict, -signatures;

use Mojo::File;
# curfile missing in Mojolicious@^8. The dependency shall not be updated for
# the time being. For this reason `curfile` is duplicated for now.
# use lib curfile->sibling('lib')->to_string;
# See https://github.com/mojolicious/mojo/blob/4093223cae00eb516e38f2226749d2963597cca3/lib/Mojo/File.pm#L36
use lib Mojo::File->new(Cwd::realpath((caller)[1]))->sibling('lib')->to_string;

use Mojo::Template;
use Sentry::Hub;
use Sentry::Integration::MojoTemplate;
use Sentry::Tracing::Span;
use Sentry::Util qw(restore_original);
use Test::Spec;

describe 'Sentry::Integration::MojoTemplate' => sub {
my $integration;
my $hub;

before each => sub {
$hub = Sentry::Hub->new;

$integration = Sentry::Integration::MojoTemplate->new(
tracing => 1,
fix_stacktrace => 1,
);

$integration->setup_once(sub { }, sub {$hub});
};

after each => sub {
restore_original 'Mojo::Template', 'render';
};

it 'creates a span when rendering template' => sub {
my $scope = $hub->get_current_scope;
my $span = Sentry::Tracing::Span->new();
$scope->set_span($span);

my $mt = Mojo::Template->new(name => 'tmpl foo');
$mt->render('foo');

is scalar $span->spans->@*, 1;
my %tmpl_span = $span->spans->[0]->%*;
is $tmpl_span{description}, 'tmpl foo';
is $tmpl_span{op}, 'mojo.template';
is_deeply $tmpl_span{data}, { compiled => 'no' };
};

it 'fixes stacktrace' => sub {
$integration->fix_stacktrace(1);
my $scope = $hub->get_current_scope;
my $span = Sentry::Tracing::Span->new();
$scope->set_span($span);

my $mt = Mojo::Template->new(name => 'tmpl foo', namespace => 'Mojo::Template::Sandbox::deadbeef');
my $output = $mt->render('<% die "boom"; %>');

is ref $output, 'Mojo::Exception';
my ($module, $filename, undef, $subroutine) = $output->frames->[0]->@*;
is $module, '';
is $filename, 'tmpl foo';
is $subroutine, '';
};

it 'does not fix stacktrace when fix_stacktrace is false' => sub {
$integration->fix_stacktrace(0);
my $scope = $hub->get_current_scope;
my $span = Sentry::Tracing::Span->new();
$scope->set_span($span);

my $mt = Mojo::Template->new(name => 'tmpl foo', namespace => 'Mojo::Template::Sandbox::deadbeef');
my $output = $mt->render('<% die "boom"; %>');

is ref $output, 'Mojo::Exception';
my ($module, $filename, undef, $subroutine) = $output->frames->[0]->@*;
is $module, 'Mojo::Template::Sandbox::deadbeef';
is $filename, 'tmpl foo';
is $subroutine, 'Mojo::Template::__ANON__';
};
};

runtests;

0 comments on commit ca26533

Please sign in to comment.