# -- # Copyright (C) 2001-2019 OTRS AG, https://otrs.com/ # -- # This software comes with ABSOLUTELY NO WARRANTY. For details, see # the enclosed file COPYING for license information (GPL). If you # did not receive this file, see https://www.gnu.org/licenses/gpl-3.0.txt. # -- package Kernel::Modules::AdminPerformanceLog; use strict; use warnings; our $ObjectManagerDisabled = 1; sub new { my ( $Type, %Param ) = @_; # allocate new hash for object my $Self = {%Param}; bless( $Self, $Type ); return $Self; } sub Run { my ( $Self, %Param ) = @_; my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); my $ConfigObject = $Kernel::OM->Get('Kernel::Config'); # is performance log disabled? if ( !$ConfigObject->Get('PerformanceLog') ) { $LayoutObject->Block( Name => 'Disabled', Data => { %Param, }, ); # create & return output my $Output = $LayoutObject->Header(); $Output .= $LayoutObject->NavigationBar(); $Output .= $LayoutObject->Output( TemplateFile => 'AdminPerformanceLog', Data => \%Param, ); $Output .= $LayoutObject->Footer(); return $Output; } $LayoutObject->Block( Name => 'Enabled', Data => { %Param, }, ); # reset log file if ( $Self->{Subaction} eq 'Reset' ) { # challenge token check for write action $LayoutObject->ChallengeTokenCheck(); if ( !$Self->_DatabaseReset() ) { $LayoutObject->FatalError(); } else { # redirect return $LayoutObject->Redirect( OP => "Action=$Self->{Action}", ); } } # show detail view elsif ( $Self->{Subaction} eq 'View' ) { $LayoutObject->Block( Name => 'ActionList' ); $LayoutObject->Block( Name => 'ActionOverview' ); my $ParamObject = $Kernel::OM->Get('Kernel::System::Web::Request'); my %Action = (); my $MaxRequest = 0; my $Slot = 60; my $MinuteSlot = $ParamObject->GetParam( Param => 'Minute' ); $Param{Minute} = $MinuteSlot; my $Interface = $ParamObject->GetParam( Param => 'Interface' ); my $Module = $ParamObject->GetParam( Param => 'Module' ); if ( $MinuteSlot < 31 ) { $Slot = 1; } elsif ( $MinuteSlot < 61 ) { $Slot = 2; } elsif ( $MinuteSlot < 121 ) { $Slot = 5; } elsif ( $MinuteSlot < 1141 ) { $Slot = 30; } my $Data = $Self->_DatabaseRead(); $LayoutObject->Block( Name => 'View', Data => { Age => $LayoutObject->CustomerAge( Age => $MinuteSlot * 60, Space => ' ' ), Interface => $Interface || '-', Module => $Module || '-', Period => $Slot, }, ); $Param{Age} = $LayoutObject->CustomerAge( Age => $MinuteSlot * 60, Space => ' ' ); $Param{Interface} = $Interface; $Param{Module} = $Module; my $Minute = 0; my $Count = 1; while ( $Count <= $MinuteSlot ) { ROW: for my $Row ( reverse @{$Data} ) { if ( $Row->[0] < ( time() - ( 60 * $Minute ) ) && $Row->[0] > ( time() - ( 60 * ( $Minute + $Slot ) ) ) ) { # for each action my $ModuleCurrent = ''; if ( $Row->[4] =~ /^(.+?|)Action=(.+?)(&.*|)$/ ) { $ModuleCurrent = $2; if ( $Row->[4] =~ /Subaction=(.+?)(&.*|)$/ ) { $ModuleCurrent .= '&' . $1; } if ($Interface) { if ( !$Module && $Row->[1] ne $Interface ) { next ROW; } if ( $Module && $Module ne $ModuleCurrent ) { next ROW; } } $Action{$Minute}->{Count}++; if ( $MaxRequest < $Action{$Minute}->{Count} ) { $MaxRequest = $Action{$Minute}->{Count}; } if ( $Action{$Minute}->{Sum} ) { $Action{$Minute}->{Sum} = $Action{$Minute}->{Sum} + $Row->[2]; } else { $Action{$Minute}->{Sum} = $Row->[2]; } if ( !defined( $Action{$Minute}->{Max} ) ) { $Action{$Minute}->{Max} = $Row->[2]; } elsif ( $Action{$Minute}->{Max} < $Row->[2] ) { $Action{$Minute}->{Max} = $Row->[2]; } if ( !defined( $Action{$Minute}->{Min} ) ) { $Action{$Minute}->{Min} = $Row->[2]; } elsif ( $Action{$Minute}->{Min} > $Row->[2] ) { $Action{$Minute}->{Min} = $Row->[2]; } } } elsif ( $Row->[0] < ( time() - ( 60 * $Minute ) ) ) { last ROW; } } $Minute = $Minute + $Slot; $Count = $Count + $Slot; } $Minute = 0; $Count = 1; while ( $Count <= $MinuteSlot ) { my $DateTimeObject = $Kernel::OM->Create('Kernel::System::DateTime'); $DateTimeObject->Subtract( Minutes => $Minute * 60 ); # set output class if ( $Action{$Minute} ) { my $Average = $Action{$Minute}->{Sum} / $Action{$Minute}->{Count}; $Average =~ s/^(.*\.\d\d).+?$/$1/g; my $I = 100 / $MaxRequest; my $W = $Action{$Minute}->{Count} * $I || 1; $LayoutObject->Block( Name => 'ViewRow', Data => { %{ $Action{$Minute} }, Average => $Average, Date => $DateTimeObject->ToString(), Width => $W . "%", }, ); } else { $LayoutObject->Block( Name => 'ViewRow', Data => { Min => 0, Max => 0, Count => $Action{$Minute}->{Count} || '0', Average => 0, Date => $DateTimeObject->ToString(), Width => '0%', }, ); } $Minute = $Minute + $Slot; $Count = $Count + $Slot; } # create & return output my $Output = $LayoutObject->Header(); $Output .= $LayoutObject->NavigationBar(); $Output .= $LayoutObject->Output( TemplateFile => 'AdminPerformanceLog', Data => \%Param, ); $Output .= $LayoutObject->Footer(); return $Output; } # show overview else { # get avarage times my $Data = []; if ( $ConfigObject->Get('PerformanceLog') ) { # check file size if ( $Self->_DatabaseCheck() ) { $LayoutObject->Block( Name => 'Reset', Data => { Size => sprintf "%.1f MB", ( $Self->_DatabaseCheck() / ( 1024 * 1024 ) ), }, ); my $Output = $LayoutObject->Header(); $Output .= $LayoutObject->NavigationBar(); $Output .= $LayoutObject->Output( TemplateFile => 'AdminPerformanceLog', Data => \%Param, ); $Output .= $LayoutObject->Footer(); return $Output; } else { $Data = $Self->_DatabaseRead(); } } $LayoutObject->Block( Name => 'Overview', ); for my $Minute ( 5, 30, 60, 2 * 60, 24 * 60, 2 * 24 * 60 ) { my %Count = (); my %Action = (); my %Sum = (); my %Max = (); my %Min = (); ROW: for my $Row ( reverse @{$Data} ) { if ( $Row->[0] > time() - ( 60 * $Minute ) ) { # whole $Count{ $Row->[1] }++; if ( $Sum{ $Row->[1] } ) { $Sum{ $Row->[1] } = $Sum{ $Row->[1] } + $Row->[2]; } else { $Sum{ $Row->[1] } = $Row->[2]; } if ( !defined( $Max{ $Row->[1] } ) ) { $Max{ $Row->[1] } = $Row->[2]; } elsif ( $Max{ $Row->[1] } < $Row->[2] ) { $Max{ $Row->[1] } = $Row->[2]; } if ( !defined( $Min{ $Row->[1] } ) ) { $Min{ $Row->[1] } = $Row->[2]; } elsif ( $Min{ $Row->[1] } > $Row->[2] ) { $Min{ $Row->[1] } = $Row->[2]; } # for each action if ( $Row->[4] =~ /^(.+?|)Action=(.+?)(&.*|)$/ ) { my $Module = $2; if ( $Row->[4] =~ /Subaction=(.+?)(&.*|)$/ ) { $Module .= '&' . $1; } $Action{$Module}->{Count}->{ $Row->[1] }++; if ( $Action{$Module}->{Sum}->{ $Row->[1] } ) { $Action{$Module}->{Sum}->{ $Row->[1] } = $Action{$Module}->{Sum}->{ $Row->[1] } + $Row->[2]; } else { $Action{$Module}->{Sum}->{ $Row->[1] } = $Row->[2]; } if ( !defined( $Action{$Module}->{Max}->{ $Row->[1] } ) ) { $Action{$Module}->{Max}->{ $Row->[1] } = $Row->[2]; } elsif ( $Action{$Module}->{Max}->{ $Row->[1] } < $Row->[2] ) { $Action{$Module}->{Max}->{ $Row->[1] } = $Row->[2]; } if ( !defined( $Action{$Module}->{Min}->{ $Row->[1] } ) ) { $Action{$Module}->{Min}->{ $Row->[1] } = $Row->[2]; } elsif ( $Action{$Module}->{Min}->{ $Row->[1] } > $Row->[2] ) { $Action{$Module}->{Min}->{ $Row->[1] } = $Row->[2]; } } } else { last ROW; } } if (%Sum) { $LayoutObject->Block( Name => 'OverviewTable', Data => { Age => $LayoutObject->CustomerAge( Age => $Minute * 60, Space => ' ' ), }, ); } for my $Interface (qw(Agent Customer Public)) { if ( defined $Sum{$Interface} ) { my $Average = $Sum{$Interface} / $Count{$Interface}; $Average =~ s/^(.*\.\d\d).+?$/$1/g; $LayoutObject->Block( Name => 'OverviewInterface', Data => { Interface => $Interface, Average => $Average, Count => $Count{$Interface} || 0, Minute => $Minute, Sum => $Sum{$Interface} || '0', Max => $Max{$Interface} || '0', Min => $Min{$Interface} || '0', }, ); for my $Module ( sort keys %Action ) { if ( defined $Action{$Module}->{Sum}->{$Interface} ) { my $Average = $Action{$Module}->{Sum}->{$Interface} / $Action{$Module}->{Count}->{$Interface}; $Average =~ s/^(.*\.\d\d).+?$/$1/g; $LayoutObject->Block( Name => 'OverviewRow', Data => { Interface => $Interface, Module => $Module, Average => $Average, Minute => $Minute, Count => $Action{$Module}->{Count}->{$Interface} || '0', Sum => $Action{$Module}->{Sum}->{$Interface} || '0', Max => $Action{$Module}->{Max}->{$Interface} || '0', Min => $Action{$Module}->{Min}->{$Interface} || '0', }, ); } } } } } # create & return output my $Output = $LayoutObject->Header(); $Output .= $LayoutObject->NavigationBar(); $Output .= $LayoutObject->Output( TemplateFile => 'AdminPerformanceLog', Data => \%Param, ); $Output .= $LayoutObject->Footer(); return $Output; } return; } sub _DatabaseCheck { my ( $Self, %Param ) = @_; my $ConfigObject = $Kernel::OM->Get('Kernel::Config'); my $File = $ConfigObject->Get('PerformanceLog::File'); # check file size my $FileSize = -s $File; if ( $FileSize > ( 1024 * 1024 * $ConfigObject->Get('PerformanceLog::FileMax') ) ) { return $FileSize; } else { return; } } sub _DatabaseReset { my ( $Self, %Param ) = @_; my $File = $Kernel::OM->Get('Kernel::Config')->Get('PerformanceLog::File'); if ( !$Kernel::OM->Get('Kernel::System::Main')->FileDelete( Location => $File ) ) { return; } else { return 1; } } sub _DatabaseRead { my ( $Self, %Param ) = @_; my @Data = (); my $File = $Kernel::OM->Get('Kernel::Config')->Get('PerformanceLog::File'); my $ArrayRef = $Kernel::OM->Get('Kernel::System::Main')->FileRead( Location => $File, Mode => 'utf8', # optional - binmode|utf8 Result => 'ARRAY', # optional - SCALAR|ARRAY ); if ($ArrayRef) { for ( @{$ArrayRef} ) { my $Line = $_; my @Row = split( /::/, $Line ); push( @Data, \@Row ); } } return \@Data; } 1;