summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorByron Jones <bjones@mozilla.com>2013-04-22 18:54:36 +0200
committerByron Jones <bjones@mozilla.com>2013-04-22 18:54:36 +0200
commitd07b2dda47346c3c5b94c3019d0fdfa5165247eb (patch)
treeb075f34b742726a3dd0ef94559646e09db255d71
parentbaf36f72baa2095b92b298fd45d9cbb522cdca87 (diff)
downloadbugzilla-d07b2dda47346c3c5b94c3019d0fdfa5165247eb.tar.gz
bugzilla-d07b2dda47346c3c5b94c3019d0fdfa5165247eb.tar.xz
Bug 750170: switch from arecibo to sentry for error reporting
-rw-r--r--Bugzilla/Config/Advanced.pm2
-rw-r--r--Bugzilla/Error.pm14
-rw-r--r--Bugzilla/Install/Filesystem.pm2
-rw-r--r--Bugzilla/Sentry.pm (renamed from Bugzilla/Arecibo.pm)228
-rw-r--r--[-rwxr-xr-x]sentry.pl (renamed from arecibo.pl)48
-rw-r--r--template/en/default/admin/params/advanced.html.tmpl6
-rw-r--r--template/en/default/global/code-error.html.tmpl2
7 files changed, 161 insertions, 141 deletions
diff --git a/Bugzilla/Config/Advanced.pm b/Bugzilla/Config/Advanced.pm
index a5ae3048a..5e51fbecc 100644
--- a/Bugzilla/Config/Advanced.pm
+++ b/Bugzilla/Config/Advanced.pm
@@ -71,7 +71,7 @@ use constant get_param_list => (
},
{
- name => 'arecibo_server',
+ name => 'sentry_uri',
type => 't',
default => '',
},
diff --git a/Bugzilla/Error.pm b/Bugzilla/Error.pm
index e49f466d6..08978fa93 100644
--- a/Bugzilla/Error.pm
+++ b/Bugzilla/Error.pm
@@ -28,7 +28,7 @@ use base qw(Exporter);
@Bugzilla::Error::EXPORT = qw(ThrowCodeError ThrowTemplateError ThrowUserError ThrowErrorPage);
-use Bugzilla::Arecibo;
+use Bugzilla::Sentry;
use Bugzilla::Constants;
use Bugzilla::WebService::Constants;
use Bugzilla::Util;
@@ -110,9 +110,8 @@ sub _throw_error {
message => \$message });
if (Bugzilla->error_mode == ERROR_MODE_WEBPAGE) {
- if (arecibo_should_notify($vars->{error})) {
+ if (sentry_should_notify($vars->{error})) {
$vars->{maintainers_notified} = 1;
- $vars->{uid} = arecibo_generate_id();
$vars->{processed} = {};
} else {
$vars->{maintainers_notified} = 0;
@@ -123,8 +122,7 @@ sub _throw_error {
|| ThrowTemplateError($template->error());
if ($vars->{maintainers_notified}) {
- arecibo_handle_error(
- $vars->{error}, $vars->{processed}->{error_message}, $vars->{uid});
+ sentry_handle_error($vars->{error}, $vars->{processed}->{error_message});
}
}
elsif (Bugzilla->error_mode == ERROR_MODE_TEST) {
@@ -206,8 +204,7 @@ sub ThrowTemplateError {
$vars->{'template_error_msg'} = $template_err;
$vars->{'error'} = "template_error";
- $vars->{'uid'} = arecibo_generate_id();
- arecibo_handle_error('error', $template_err, $vars->{'uid'});
+ sentry_handle_error('error', $template_err);
$vars->{'template_error_msg'} =~ s/ at \S+ line \d+\.\s*$//;
my $template = Bugzilla->template;
@@ -218,7 +215,6 @@ sub ThrowTemplateError {
my $maintainer = html_quote(Bugzilla->params->{'maintainer'});
my $error = html_quote($vars->{'template_error_msg'});
my $error2 = html_quote($template->error());
- my $uid = html_quote($vars->{'uid'});
print <<END;
<tt>
<p>
@@ -232,7 +228,7 @@ sub ThrowTemplateError {
-->
<p>
The <a href="mailto:$maintainer">Bugzilla maintainers</a> have
- been notified of this error [#$uid].
+ been notified of this error.
</p>
</tt>
END
diff --git a/Bugzilla/Install/Filesystem.pm b/Bugzilla/Install/Filesystem.pm
index 86978163b..1abac0154 100644
--- a/Bugzilla/Install/Filesystem.pm
+++ b/Bugzilla/Install/Filesystem.pm
@@ -159,7 +159,7 @@ sub FILESYSTEM {
'runtests.pl' => { perms => OWNER_EXECUTE },
'jobqueue.pl' => { perms => OWNER_EXECUTE },
'migrate.pl' => { perms => OWNER_EXECUTE },
- 'arecibo.pl' => { perms => OWNER_EXECUTE },
+ 'sentry.pl' => { perms => OWNER_EXECUTE },
'install-module.pl' => { perms => OWNER_EXECUTE },
'Bugzilla.pm' => { perms => CGI_READ },
diff --git a/Bugzilla/Arecibo.pm b/Bugzilla/Sentry.pm
index 08ff11c20..e07c2eb44 100644
--- a/Bugzilla/Arecibo.pm
+++ b/Bugzilla/Sentry.pm
@@ -5,66 +5,34 @@
# This Source Code Form is "Incompatible With Secondary Licenses", as
# defined by the Mozilla Public License, v. 2.0.
-package Bugzilla::Arecibo;
+package Bugzilla::Sentry;
use strict;
use warnings;
use base qw(Exporter);
our @EXPORT = qw(
- arecibo_handle_error
- arecibo_generate_id
- arecibo_should_notify
+ sentry_handle_error
+ sentry_should_notify
);
use Apache2::Log;
use Apache2::SubProcess;
use Carp;
use Data::Dumper;
-use Email::Date::Format qw(email_gmdate);
+use DateTime;
use File::Temp;
use LWP::UserAgent;
use Sys::Hostname;
+use URI;
use Bugzilla::Constants;
+use Bugzilla::RNG qw(irand);
use Bugzilla::Util;
use Bugzilla::WebService::Constants;
use constant CONFIG => {
- # 'types' maps from the error message to types and priorities
- types => [
- {
- type => 'the_schwartz',
- boost => -10,
- match => [
- qr/TheSchwartz\.pm/,
- ],
- },
- {
- type => 'database_error',
- boost => -10,
- match => [
- qr/DBD::mysql/,
- qr/Can't connect to the database/,
- ],
- },
- {
- type => 'patch_reader',
- boost => +5,
- match => [
- qr#/PatchReader/#,
- ],
- },
- {
- type => 'uninitialized_warning',
- boost => 0,
- match => [
- qr/Use of uninitialized value/,
- ],
- },
- ],
-
- # 'codes' lists the code-errors which are sent to arecibo
+ # 'codes' lists the code-errors which are sent to sentry
codes => [qw(
bug_error
chart_datafile_corrupt
@@ -78,32 +46,58 @@ use constant CONFIG => {
token_generation_error
)],
- # any error messages matching these regex's will not be sent to arecibo
+ # any error messages matching these regex's will not be sent to sentry
ignore => [
qr/Software caused connection abort/,
qr/Could not check out .*\/cvsroot/,
],
+
+ # (ab)use the logger to classify error/warning types
+ logger => [
+ {
+ match => [
+ qr/DBD::mysql/,
+ qr/Can't connect to the database/,
+ ],
+ logger => 'database_error',
+ },
+ {
+ match => [ qr#/PatchReader/# ],
+ logger => 'patchreader',
+ },
+ {
+ match => [ qr/Use of uninitialized value/ ],
+ logger => 'uninitialized_warning',
+ },
+ ],
};
-sub arecibo_generate_id {
- return sprintf("%s.%s", (time), $$);
+sub sentry_generate_id {
+ return sprintf('%04x%04x%04x%04x%04x%04x%04x%04x',
+ irand(0xffff), irand(0xffff),
+ irand(0xffff),
+ irand(0x0fff) | 0x4000,
+ irand(0x3fff) | 0x8000,
+ irand(0xffff), irand(0xffff), irand(0xffff)
+ );
}
-sub arecibo_should_notify {
+sub sentry_should_notify {
my $code_error = shift;
- return grep { $_ eq $code_error } @{CONFIG->{codes}};
+ return grep { $_ eq $code_error } @{ CONFIG->{codes} };
}
-sub arecibo_handle_error {
- my $class = shift;
+sub sentry_handle_error {
+ my $level = shift;
my @message = split(/\n/, shift);
- my $id = arecibo_generate_id();
+ my $id = sentry_generate_id();
- my $is_error = $class eq 'error';
- if ($class ne 'error' && $class ne 'warning') {
+ my $is_error = $level eq 'error';
+ if ($level ne 'error' && $level ne 'warning') {
# it's a code-error
- return 0 unless arecibo_should_notify($class);
+ return 0 unless sentry_should_notify($level);
$is_error = 1;
+ $level = 'error';
}
# build traceback
@@ -115,9 +109,9 @@ sub arecibo_handle_error {
#local $Carp::MaxArgNums = 0;
local $Carp::MaxArgNums = -1;
local $Carp::CarpInternal{'CGI::Carp'} = 1;
- local $Carp::CarpInternal{'Bugzilla::Error'} = 1;
- local $Carp::CarpInternal{'Bugzilla::Arecibo'} = 1;
- $traceback = Carp::longmess();
+ local $Carp::CarpInternal{'Bugzilla::Error'} = 1;
+ local $Carp::CarpInternal{'Bugzilla::Sentry'} = 1;
+ $traceback = trim(Carp::longmess());
}
# strip timestamp
@@ -126,85 +120,91 @@ sub arecibo_handle_error {
}
my $message = join(" ", map { trim($_) } grep { $_ ne '' } @message);
- # don't send to arecibo unless configured
- my $arecibo_server = Bugzilla->params->{arecibo_server} || '';
- my $send_to_arecibo = $arecibo_server ne '';
+ # determine logger
+ my $logger;
+ foreach my $config (@{ CONFIG->{logger} }) {
+ foreach my $re (@{ $config->{match} }) {
+ if ($message =~ $re) {
+ $logger = $config->{logger};
+ last;
+ }
+ }
+ last if $logger;
+ }
+ $logger ||= $level;
+
+ # don't send to sentry unless configured
+ my $send_to_sentry = Bugzilla->params->{sentry_uri} ? 1 : 0;
# web service filtering
- if ($send_to_arecibo
+ if ($send_to_sentry
&& (Bugzilla->error_mode == ERROR_MODE_DIE_SOAP_FAULT || Bugzilla->error_mode == ERROR_MODE_JSON_RPC))
{
my ($code) = $message =~ /^(-?\d+): /;
if ($code
&& !($code == ERROR_UNKNOWN_FATAL || $code == ERROR_UNKNOWN_TRANSIENT))
{
- $send_to_arecibo = 0;
+ $send_to_sentry = 0;
}
}
# message content filtering
- if ($send_to_arecibo) {
- foreach my $re (@{CONFIG->{ignore}}) {
+ if ($send_to_sentry) {
+ foreach my $re (@{ CONFIG->{ignore} }) {
if ($message =~ $re) {
- $send_to_arecibo = 0;
+ $send_to_sentry = 0;
last;
}
}
}
+ # for now, don't send patchreader errors to sentry
+ $send_to_sentry = 0
+ if $logger eq 'patchreader';
+
# log to apache's error_log
- if ($send_to_arecibo) {
- $message .= " [#$id]";
+ if ($send_to_sentry) {
+ _write_to_error_log("$message [#$id]", $is_error);
} else {
$traceback =~ s/\n/ /g;
- $message .= " $traceback";
+ _write_to_error_log("$message $traceback", $is_error);
}
- _write_to_error_log($message, $is_error);
- return 0 unless $send_to_arecibo;
+ return 0 unless $send_to_sentry;
- # set the error type and priority from the message content
- $message = join("\n", grep { $_ ne '' } @message);
- my $type = '';
- my $priority = $class eq 'error' ? 3 : 10;
- foreach my $rh_type (@{CONFIG->{types}}) {
- foreach my $re (@{$rh_type->{match}}) {
- if ($message =~ $re) {
- $type = $rh_type->{type};
- $priority += $rh_type->{boost};
- last;
- }
+ my $user_data = undef;
+ eval {
+ my $user = Bugzilla->user;
+ if ($user->id) {
+ $user_data = {
+ id => $user->login,
+ name => $user->name,
+ };
}
- last if $type ne '';
- }
- $type ||= $class;
- $priority = 1 if $priority < 1;
- $priority = 10 if $priority > 10;
-
- my $username = '';
- eval { $username = Bugzilla->user->login };
+ };
- my $request = '';
- foreach my $name (sort { lc($a) cmp lc($b) } keys %ENV) {
- $request .= "$name=$ENV{$name}\n";
- }
- chomp($request);
-
- my $data = [
- ip => remote_ip(),
- msg => $message,
- priority => $priority,
- server => hostname(),
- request => $request,
- status => '500',
- timestamp => email_gmdate(),
- traceback => $traceback,
- type => $type,
- uid => $id,
- url => Bugzilla->cgi->self_url,
- user_agent => $ENV{HTTP_USER_AGENT},
- username => $username,
- ];
+ my $uri = URI->new(Bugzilla->cgi->self_url);
+ $uri->query(undef);
+
+ my $data = {
+ event_id => $id,
+ message => $message,
+ timestamp => DateTime->now->iso8601(),
+ level => $level,
+ platform => 'Other',
+ logger => $logger,
+ server_name => hostname(),
+ 'sentry.interfaces.User' => $user_data,
+ 'sentry.interfaces.Http' => {
+ url => $uri->as_string,
+ method => $ENV{REQUEST_METHOD},
+ query_string => $ENV{QUERY_STRING},
+ env => \%ENV,
+ },
+ extra => {
+ stacktrace => $traceback,
+ },
+ };
my $fh = File::Temp->new( UNLINK => 0 );
if (!$fh) {
@@ -215,7 +215,7 @@ sub arecibo_handle_error {
close($fh) or die $!;
my $filename = $fh->filename;
- my $command = bz_locations()->{'cgi_path'} . "/arecibo.pl '$filename' &";
+ my $command = bz_locations()->{'cgi_path'} . "/sentry.pl '$filename' &";
system($command);
return 1;
}
@@ -244,14 +244,14 @@ sub _in_eval {
return $in_eval;
}
-sub _arecibo_die_handler {
+sub _sentry_die_handler {
my $message = shift;
$message =~ s/^undef error - //;
# avoid recursion, and check for CGI::Carp::die failures
my $in_cgi_carp_die = 0;
for (my $stack = 1; my $sub = (caller($stack))[3]; $stack++) {
- return if $sub =~ /:_arecibo_die_handler$/;
+ return if $sub =~ /:_sentry_die_handler$/;
$in_cgi_carp_die = 1 if $sub =~ /CGI::Carp::die$/;
}
@@ -274,7 +274,7 @@ sub _arecibo_die_handler {
$in_cgi_carp_die ||
($nested_error && $nested_error !~ /\bModPerl::Util::exit\b/)
) {
- arecibo_handle_error('error', $message);
+ sentry_handle_error('error', $message);
# and call the normal error management
# (ISE for web pages, error response for web services, etc)
@@ -283,18 +283,18 @@ sub _arecibo_die_handler {
exit;
}
-sub install_arecibo_handler {
+sub install_sentry_handler {
require CGI::Carp;
- CGI::Carp::set_die_handler(\&_arecibo_die_handler);
+ CGI::Carp::set_die_handler(\&_sentry_die_handler);
$main::SIG{__WARN__} = sub {
return if _in_eval();
- arecibo_handle_error('warning', shift);
+ sentry_handle_error('warning', shift);
};
}
BEGIN {
if ($ENV{SCRIPT_NAME} || $ENV{MOD_PERL}) {
- install_arecibo_handler();
+ install_sentry_handler();
}
}
diff --git a/arecibo.pl b/sentry.pl
index 93c34c999..b5b9c3f31 100755..100644
--- a/arecibo.pl
+++ b/sentry.pl
@@ -8,9 +8,9 @@
# defined by the Mozilla Public License, v. 2.0.
#
-# report errors to arecibo
+# report errors to sentry
# expects a filename with a Data::Dumper serialised parameters
-# called by Bugzilla::Arecibo
+# called by Bugzilla::Sentry
#
use strict;
@@ -22,10 +22,15 @@ use lib "$Bin/lib";
use Bugzilla;
use Bugzilla::Constants;
+use Bugzilla::RNG qw(irand);
+use Fcntl qw(:flock);
use File::Slurp;
+use HTTP::Request::Common;
+use JSON ();
+use LWP::UserAgent;
use POSIX qw(setsid nice);
use Safe;
-use Fcntl qw(:flock);
+use URI;
Bugzilla->usage_mode(USAGE_MODE_CMDLINE);
nice(19);
@@ -36,9 +41,9 @@ open(STDOUT, '>/dev/null');
open(STDERR, '>/dev/null');
setsid();
-# grab arecibo server url
-my $arecibo_server = Bugzilla->params->{arecibo_server} || '';
-exit(1) unless $arecibo_server;
+# grab sentry server url
+my $sentry_uri = Bugzilla->params->{sentry_uri} || '';
+exit(1) unless $sentry_uri;
# read data dump
exit(1) unless my $filename = shift;
@@ -50,15 +55,34 @@ my $cpt = new Safe;
$cpt->reval($dump) || exit(1);
my $data = ${$cpt->varglob('VAR1')};
+# split the sentry uri
+my $uri = URI->new($sentry_uri);
+my ($public_key, $secret_key) = split(/:/, $uri->userinfo);
+$uri->userinfo(undef);
+my $project_id = $uri->path;
+$project_id =~ s/^\///;
+$uri->path("/api/$project_id/store/");
+
+# build the message
+my $message = JSON->new->utf8(1)->pretty(0)->allow_nonref(1)->encode($data);
+my %header = (
+ 'X-Sentry-Auth' => sprintf(
+ "Sentry sentry_version=%s, sentry_timestamp=%s, sentry_key=%s, sentry_client=%s, sentry_secret=%s",
+ '2.0',
+ (time),
+ $public_key,
+ 'bugzilla/4.2',
+ $secret_key,
+ ),
+ 'Content-Type' => 'application/json'
+);
+
# ensure we send warnings one at a time per webhead
flock(DATA, LOCK_EX);
-# and post to arecibo
-my $agent = LWP::UserAgent->new(
- agent => 'bugzilla.mozilla.org',
- timeout => 10, # seconds
-);
-$agent->post($arecibo_server, $data);
+# and post to sentry
+my $request = POST $uri->canonical, %header, Content => $message;
+my $response = LWP::UserAgent->new->request($request);
__DATA__
this exists so the flock() code works.
diff --git a/template/en/default/admin/params/advanced.html.tmpl b/template/en/default/admin/params/advanced.html.tmpl
index 1cf0c344f..5301ff2cf 100644
--- a/template/en/default/admin/params/advanced.html.tmpl
+++ b/template/en/default/admin/params/advanced.html.tmpl
@@ -82,8 +82,8 @@
disable_bug_updates =>
"When enabled, all updates to $terms.bugs will be blocked.",
- arecibo_server =>
+ sentry_uri =>
"When set, important errors and warnings will be sent to the"
- _ " specified Arecibo server. Enter the Arecibo server's full URL;"
- _ " eg <code>https://arecibo.example.com/v/1/</code>.",
+ _ " specified Sentry server. Enter the full API KEY URL."
+ _ " eg <code>https://01234567890123456780123456780123:01234567890123456780123456780123@errormill.mozilla.org/10</code>.",
} %]
diff --git a/template/en/default/global/code-error.html.tmpl b/template/en/default/global/code-error.html.tmpl
index 704d3ad16..d98f2578c 100644
--- a/template/en/default/global/code-error.html.tmpl
+++ b/template/en/default/global/code-error.html.tmpl
@@ -506,7 +506,7 @@
admindocslinks = admindocslinks
%]
-[%# return the generated error_message for arecibo %]
+[%# return the generated error_message for sentry %]
[% processed.error_message = error_message %]
<p>