lib/Search/Elasticsearch/Error.pm (119 lines of code) (raw):
# Licensed to Elasticsearch B.V. under one or more contributor
# license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright
# ownership. Elasticsearch B.V. licenses this file to you under
# the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
package Search::Elasticsearch::Error;
our $DEBUG = 0;
@Search::Elasticsearch::Error::Internal::ISA = __PACKAGE__;
@Search::Elasticsearch::Error::Param::ISA = __PACKAGE__;
@Search::Elasticsearch::Error::NoNodes::ISA = __PACKAGE__;
@Search::Elasticsearch::Error::ProductCheck::ISA = __PACKAGE__;
@Search::Elasticsearch::Error::Unauthorized::ISA = __PACKAGE__;
@Search::Elasticsearch::Error::Forbidden::ISA = __PACKAGE__;
@Search::Elasticsearch::Error::Illegal::ISA = __PACKAGE__;
@Search::Elasticsearch::Error::Request::ISA = __PACKAGE__;
@Search::Elasticsearch::Error::Timeout::ISA = __PACKAGE__;
@Search::Elasticsearch::Error::Cxn::ISA = __PACKAGE__;
@Search::Elasticsearch::Error::Serializer::ISA = __PACKAGE__;
@Search::Elasticsearch::Error::Conflict::ISA
= ( 'Search::Elasticsearch::Error::Request', __PACKAGE__ );
@Search::Elasticsearch::Error::Missing::ISA
= ( 'Search::Elasticsearch::Error::Request', __PACKAGE__ );
@Search::Elasticsearch::Error::RequestTimeout::ISA
= ( 'Search::Elasticsearch::Error::Request', __PACKAGE__ );
@Search::Elasticsearch::Error::ContentLength::ISA
= ( __PACKAGE__, 'Search::Elasticsearch::Error::Request' );
@Search::Elasticsearch::Error::SSL::ISA
= ( __PACKAGE__, 'Search::Elasticsearch::Error::Cxn' );
@Search::Elasticsearch::Error::BadGateway::ISA
= ( 'Search::Elasticsearch::Error::Cxn', __PACKAGE__ );
@Search::Elasticsearch::Error::Unavailable::ISA
= ( 'Search::Elasticsearch::Error::Cxn', __PACKAGE__ );
@Search::Elasticsearch::Error::GatewayTimeout::ISA
= ( 'Search::Elasticsearch::Error::Cxn', __PACKAGE__ );
use overload (
'""' => '_stringify',
'cmp' => '_compare',
);
use Data::Dumper();
#===================================
sub new {
#===================================
my ( $class, $type, $msg, $vars, $caller ) = @_;
return $type if ref $type;
$caller ||= 0;
my $error_class = 'Search::Elasticsearch::Error::' . $type;
$msg = 'Unknown error' unless defined $msg;
local $DEBUG = 2 if $type eq 'Internal';
my $stack = $class->_stack;
my $self = bless {
type => $type,
text => $msg,
vars => $vars,
stack => $stack,
}, $error_class;
return $self;
}
#===================================
sub is {
#===================================
my $self = shift;
for (@_) {
return 1 if $self->isa("Search::Elasticsearch::Error::$_");
}
return 0;
}
#===================================
sub _stringify {
#===================================
my $self = shift;
local $Data::Dumper::Terse = 1;
local $Data::Dumper::Indent = !!$DEBUG;
unless ( $self->{msg} ) {
my $stack = $self->{stack};
my $caller = $stack->[0];
$self->{msg} = sprintf( "[%s] ** %s, called from sub %s at %s line %d.",
$self->{type}, $self->{text}, @{$caller}[ 3, 1, 2 ] );
if ( $self->{vars} ) {
$self->{msg} .= sprintf( " With vars: %s\n",
Data::Dumper::Dumper $self->{vars} );
}
if ( @$stack > 1 ) {
$self->{msg}
.= sprintf( "Stacktrace:\n%s\n", $self->stacktrace($stack) );
}
}
return $self->{msg};
}
#===================================
sub _compare {
#===================================
my ( $self, $other, $swap ) = @_;
$self .= '';
( $self, $other ) = ( $other, $self ) if $swap;
return $self cmp $other;
}
#===================================
sub _stack {
#===================================
my $self = shift;
my $caller = shift() || 2;
my @stack;
while ( my @caller = caller( ++$caller ) ) {
next if $caller[0] eq 'Try::Tiny';
if ( $caller[3] =~ /^(.+)::__ANON__\[(.+):(\d+)\]$/ ) {
@caller = ( $1, $2, $3, '(ANON)' );
}
elsif ( $caller[1] =~ /^\(eval \d+\)/ ) {
$caller[3] = "modified(" . $caller[3] . ")";
}
next
if $caller[0] =~ /^Search::Elasticsearch/
and ( $DEBUG < 2 or $caller[3] eq 'Try::Tiny::try' );
push @stack, [ @caller[ 0, 1, 2, 3 ] ];
last unless $DEBUG > 1;
}
return \@stack;
}
#===================================
sub stacktrace {
#===================================
my $self = shift;
my $stack = shift || $self->_stack();
my $o = sprintf "%s\n%-4s %-50s %-5s %s\n%s\n",
'-' x 80, '#', 'Package', 'Line', 'Sub-routine', '-' x 80;
my $i = 1;
for (@$stack) {
$o .= sprintf "%-4d %-50s %4d %s\n", $i++, @{$_}[ 0, 2, 3 ];
}
return $o .= ( '-' x 80 ) . "\n";
}
#===================================
sub TO_JSON {
#===================================
my $self = shift;
return $self->_stringify;
}
1;
# ABSTRACT: Errors thrown by Search::Elasticsearch
=head1 DESCRIPTION
Errors thrown by Search::Elasticsearch are error objects, which can include
a stack trace and information to help debug problems. An error object
consists of the following:
{
type => $type, # eg Missing
text => 'Error message',
vars => {...}, # vars which may help to explain the error
stack => [...], # a stack trace
}
The C<$Search::Elasticsearch::Error::DEBUG> variable can be set to C<1> or C<2>
to increase the verbosity of errors.
Error objects stringify to a human readable error message when used in text
context (for example: C<print 'Oh no! '.$error>). They also support the C<TO_JSON>
method to support conversion to JSON when L<JSON/convert_blessed> is enabled.
=head1 ERROR CLASSES
The following error classes are defined:
=over
=item * C<Search::Elasticsearch::Error::Param>
A bad parameter has been passed to a method.
=item * C<Search::Elasticsearch::Error::Request>
There was some generic error performing your request in Elasticsearch.
This error is triggered by HTTP status codes C<400> and C<500>. This class
has the following sub-classes:
=over
=item * C<Search::Elasticsearch::Error::Unauthorized>
Invalid (or no) username/password provided as C<userinfo> for a password
protected service. These errors are triggered by the C<401> HTTP status code.
=item * C<Search::Elasticsearch::Error::Missing>
A resource that you requested was not found. These errors are triggered
by the C<404> HTTP status code.
=item * C<Elastisearch::Error::Conflict>
Your request could not be performed because of some conflict. For instance,
if you try to delete a document with a particular version number, and the
document has already changed, it will throw a C<Conflict> error. If it can,
it will include the C<current_version> in the error vars. This error
is triggered by the C<409> HTTP status code.
=item * C<Search::Elasticsearch::Error::ContentLength>
The request body was longer than the
L<max_content_length|Search::Elasticsearch::Role::Cxn/max_content_length>.
=item * C<Search::Elasticsearch::Error::RequestTimeout>
The request took longer than the specified C<timeout>. Currently only
applies to the
L<cluster_health|Search::Elasticsearch::Client::6_0::Direct::Cluster/cluster_health()>
request.
=back
=item * C<Search::Elasticsearch::Error::Timeout>
The request timed out.
=item * C<Search::Elasticsearch::Error::Cxn>
There was an error connecting to a node in the cluster. This error
indicates node failure and will be retried on another node.
This error has the following sub-classes:
=over
=item * C<Search::Elasticsearch::Error::Unavailable>
The current node is unable to handle your request at the moment. Your
request will be retried on another node. This error is triggered by
the C<503> HTTP status code.
=item * C<Search::Elasticsearch::Error::BadGateway>
A proxy between the client and Elasticsearch is unable to connect to Elasticsearch.
This error is triggered by the C<502> HTTP status code.
=item * C<Search::Elasticsearch::Error::GatewayTimeout>
A proxy between the client and Elasticsearch is unable to connect to Elasticsearch
within its own timeout. This error is triggered by the C<504> HTTP status code.
=item * C<Search::Elasticsearch::Error::SSL>
There was a problem validating the SSL certificate. Not all
backends support this error type.
=back
=item * C<Search::Elasticsearch::Error::Forbidden>
Either the cluster was unable to process the request because it is currently
blocking, eg there are not enough master nodes to form a cluster, or
because the authenticated user is trying to perform an unauthorized
action. This error is triggered by the C<403> HTTP status code.
=item * C<Search::Elasticsearch::Error::Illegal>
You have attempted to perform an illegal operation.
For instance, you attempted to use a Scroll helper in a different process
after forking.
=item * C<Search::Elasticsearch::Error::Serializer>
There was an error serializing a variable or deserializing a string.
=item * C<Elasticsarch::Error::Internal>
An internal error occurred - please report this as a bug in
this module.
=back