You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ngcp-panel/lib/NGCP/Panel/Utils/Message.pm

337 lines
10 KiB

package NGCP::Panel::Utils::Message;
use Catalyst;
use Sipwise::Base;
use Data::Dumper;
use DateTime qw();
use DateTime::Format::RFC3339 qw();
use Time::HiRes qw();
sub get_log_params {
my %params = @_;
my $c = $params{c};
my $type = $params{type};
my $data = $params{data};
# tx_id
my $log_tx = DateTime->from_epoch(epoch => Time::HiRes::time);
my $log_tx_id = sprintf '%X', $log_tx->strftime('%s%N');
# package and method depending on the request type (normal or api)
my $called = '';
if ($type eq 'api_request') {
$called = sprintf 'API[%s]/%s',
$c->request->method,
$c->request->path;
$c->session->{api_request_tx_id} = $log_tx_id;
} elsif ($type eq 'api_response') {
$called = sprintf 'API[%s %d]/%s',
$c->request->method,
$c->response->code,
$c->request->path;
if ($c->session->{api_request_tx_id}) {
$log_tx_id = $c->session->{api_request_tx_id};
}
} else {
my $caller = (caller 2)[3];
$caller !~ /::/ and $caller = (caller 3)[3];
if ($caller) {
my @caller = split(/::/, $caller);
$#caller >= 3 and $called = join('::', @caller[-3...-1]);
}
}
# remote user
my $r_user = '';
if ($c->user_exists) {
if ($c->user->roles eq 'admin' || $c->user->roles eq 'reseller') {
$r_user = $c->user->login;
} else {
$r_user = $c->user->webusername . '@' . $c->user->domain->domain;
}
}
# remote ip
my $r_ip = $c->request->address;
$r_ip =~ s/^::ffff://; # ipv4 in ipv6 form -> ipv4
# parameters
my $data_str;
my $data_ref = ref($data) ? $data :
$type eq 'api_request'
? $c->request->query_params
: $c->request->parameters;
if ($data_ref) {
my $log_passwords = $c->config->{security}{log_passwords} || 0;
my %parsed_data_ref = map { $_ => defined $data_ref->{$_}
? length($data_ref->{$_}) > 500
? '*too big*'
: (!$log_passwords && $_ =~ /password/i
&& $data_ref->{$_})
? '*' x 5
: $data_ref->{$_}
: 'undef'
} keys %$data_ref;
$data_str = Data::Dumper->new([ \%parsed_data_ref ])
->Terse(1)
->Maxdepth(1)
->Dump;
} elsif ($data) {
$data_str = $data;
}
if ($data_str) {
$data_str =~ s/\n//g;
$data_str =~ s/\s+/ /g;
} else {
$data_str = '';
}
if (length($data_str) > 100000) {
# trim long messages
$data_str = "{ data => 'Msg size is too big' }";
}
return {
tx_id => $log_tx_id,
called => $called,
r_user => $r_user,
r_ip => $r_ip,
data => $data_str,
};
}
sub error {
my %params = @_;
my $c = $params{c};
my $type = $params{type};
my $data = $params{data};
my $desc = $params{desc};
my $cname = $params{cname};
my $log = $params{log};
my $error = $params{error};
$type //= 'panel';
$desc //= '';
my $log_params = get_log_params(c => $c,
type => $type,
data => $data, );
defined $cname and $log_params->{called} =~ s/__ANON__/$cname/;
my $msg = ''; # sent to the log file
my $log_msg = ''; # optional log info
my $usr_type = 'error';
my $usr_text = $desc;
if (defined $log)
{
if (ref($log)) {
$log_msg = Data::Dumper->new([ $log ])
->Terse(1)
->Maxdepth(1)
->Dump;
} else {
$log_msg = $log
}
$log_msg =~ s/\n//g;
$log_msg =~ s/\s+/ /g;
}
if (not defined $error) {
$msg = $desc;
}
elsif (my ($host) = $error =~ /problem connecting to (\S+, port [0-9]+)/)
{
$log_msg = "$desc ($error)";
$usr_text = "$desc (A service could not be reached, $host)";
}
elsif (ref($error) eq "ARRAY" && @$error >= 2 && $error->[1] eq "showdetails")
{
$msg = "$desc (@$error[0])";
$usr_text = "$desc (@$error[0])";
}
elsif (ref($error) eq "HASH" && $error->{showdetails})
{
my $error_msg = $error->{error} ? ' '.$error->{error} : '';
$msg = "$desc ($error->{showdetails}$error_msg)";
$usr_text = "$desc ($error->{showdetails})";
}
elsif (not $error->isa('DBIx::Class::Exception'))
{
$msg = "$desc ($error)";
$usr_text = $desc;
}
elsif (my ($dup) = $error =~ /(Duplicate entry \S*)/)
{
$msg = "$desc ($error)";
$usr_text = "$desc ($dup)";
}
elsif (my ($excerpt) = $error =~ /(Column \S+ cannot be null)/)
{
$msg = "$desc ($error)";
$usr_text = "$desc ($excerpt)";
}
else
{
$msg = "$desc ($error)";
}
my $logstr = 'IP=%s CALLED=%s TX=%s USER=%s DATA=%s MSG="%s" LOG="%s"';
my $rc = $c->log->error(
sprintf $logstr, @{$log_params}{qw(r_ip called tx_id r_user data)}, $msg, $log_msg);
if ($type eq 'panel') {
$c->flash(messages => [{ type => $usr_type,
text => sprintf '%s [%s]',
$usr_text,
$log_params->{tx_id},
}]);
$c->stash(panel_error_message => $msg);
}
return $rc;
}
sub info {
my %params = @_;
my $c = $params{c};
my $type = $params{type};
my $data = $params{data};
my $desc = $params{desc};
my $cname = $params{cname};
my $log = $params{log};
$type //= 'panel';
$desc //= '';
my $log_params = get_log_params(c => $c,
type => $type,
data => $data, );
defined $cname and $log_params->{called} =~ s/__ANON__/$cname/;
my $msg = $desc; # sent to the log file
my $log_msg = ''; # optional log info
my $usr_type = 'info';
my $usr_text = $desc;
if (defined $log) {
if (ref($log)) {
$log_msg = Data::Dumper->new([ $log ])
->Terse(1)
->Maxdepth(1)
->Dump;
} else {
$log_msg = $log
}
$log_msg =~ s/\n//g;
$log_msg =~ s/\s+/ /g;
}
my $logstr = 'IP=%s CALLED=%s TX=%s USER=%s DATA=%s MSG="%s" LOG="%s"';
my $rc = $c->log->info(
sprintf $logstr,
@{$log_params}{qw(r_ip called tx_id r_user data)}, $msg, $log_msg);
if ($type eq 'panel') {
$c->flash(messages => [{ type => $usr_type, text => $usr_text }]);
}
return $rc;
}
1;
__END__
=encoding UTF-8
=head1 NAME
NGCP::Panel::Utils::Message
=head1 DESCRIPTION
Parse messages for log and Web display.
Usage in the code:
Error:
NGCP::Panel::Utils::Message::error(
c => $c,
error => $e,
desc => $c->loc('Failed to create domain.'),
);
Info:
NGCP::Panel::Utils::Message::info(
c => $c,
desc => $c->loc('Domain successfully created'),
);
Check "Params" section for the "error" and the "info" methods for extended usage
Notes:
- If you use the code inside an anonymous function (sub { })
it will appear in the log as CALLED=....::__ANON__
you might want to use "$cname" then to specify the caller name
manually (there is an example in "Params" below).
- If you do not want a message to appear on the GUI
use "$type => 'internal' (basically anything but 'panel',
there may be more logic around different types but currently
there is only a check if ($type eq 'panel') { show on GUI ... })
=head1 INTERFACE
=head2 Functions
=head3 C<get_log_params>
Used by "info" and "error" methods and returns parsed data for logging
- Do not this the method directly -
=head3 C<error>
Shows the error message on the panel. Also logs everything to the logger.
Params:
$c (required)
$desc (required) - main log message (will apear on the GUI).
$log - additional information that will be added to LOG= only (no GUI).
$error - error log message, will be parsed and may also appear on the GUI
$type - 'panel' by default, means the message will go to both the log and the GUI,
anything else but 'panel' is written only to the log.
Expandable with new types if necessary.
Current types 'panel','internal','api_request','api_response'
$data - hash containing what will be written into the DATA= part of the log message
(by default $c->request->params is used for the data source)
$cname - Custom function name for CALLED= e.g. Controller::Domain::create
as the caller and if you need to something else
$cname => 'custom_create' leads to
CALLED=Controller:Domain::custom_create in the log
=head3 C<info>
Shows the info message on the panel. Also logs everything to the logger.
Params:
$c (required)
$desc (required) - main log message (will apear on the GUI).
$log - additional information that will be added to LOG= only (no GUI).
$type - 'panel' by default, means the message will go to both the log and the GUI,
anything else but 'panel' is written only to the log.
Expandable with new types if necessary.
Current types 'panel','internal','api_request','api_response'
$data - hash containing what will be written into the DATA= part of the log message
(by default $c->request->params is used for the data source)
$cname - Custom function name for CALLED= e.g. Controller::Domain::create
as the caller and if you need to something else
$cname => 'custom_create' leads to
CALLED=Controller:Domain::custom_create in the log
=head1 AUTHOR
=head1 LICENSE
This library is free software. You can redistribute it and/or modify
it under the same terms as Perl itself.