my $charset = 'utf-8';
my $csv_sep_char = ',';
my %current_sessions = ();
+my %pgb_current_sessions = ();
my $incr_date = '';
my $last_incr_date = '';
my $anonymize = 0;
my $logfile_list = '';
my $enable_checksum = 0;
my $timezone = 0;
+my $pgbouncer_only = 0;
my $rebuild = 0;
-
my $NUMPROGRESS = 10000;
my @DIMENSIONS = (800, 300);
my $RESRC_URL = '';
'journalctl=s' => \$journalctl_cmd,
'pid-dir=s' => \$PID_DIR,
'rebuild!' => \$rebuild,
+ 'pgbouncer-only!' => \$pgbouncer_only,
);
die "FATAL: use pgbadger --help\n" if (not $result);
my $full_error_regex = qr/^(WARNING|ERROR|FATAL|PANIC|DETAIL|HINT|STATEMENT|CONTEXT)/;
my $main_error_regex = qr/^(WARNING|ERROR|FATAL|PANIC)/;
+my $pgbouncer_log_format = qr/^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\.\d+(?: [A-Z\+\-\d]{3,6})? (\d+) ([^\s]+) (.\-0x[0-9a-f\.]*): ([0-9a-zA-Z\_\[\]\-\.]*)\/([0-9a-zA-Z\_\[\]\-\.]*)\@([a-zA-Z0-9\-\.]+|\[local\]|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|[0-9a-fA-F:]+)?[:\d]* (.*)/;
+my @pgb_prefix_params = ('t_timestamp', 't_pid', 't_loglevel', 't_session_id', 't_dbname', 't_dbuser', 't_client', 't_query');
+
+my $pgbouncer_log_parse1 = qr/^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\.\d+(?: [A-Z\+\-\d]{3,6})? (\d+) ([^\s]+) (.*)/;
+my @pgb_prefix_parse1 = ('t_timestamp', 't_pid', 't_loglevel', 't_query');
+my $pgbouncer_log_parse2 = qr/^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\.\d+(?: [A-Z\+\-\d]{3,6})? (\d+) ([^\s]+) (.\-0x[0-9a-f\.]*): ([0-9a-zA-Z\_\[\]\-\.]*)\/([0-9a-zA-Z\_\[\]\-\.]*)\@([a-zA-Z0-9\-\.]+|\[local\]|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|[0-9a-fA-F:]+)?[:\d]* (.*)/;
+my @pgb_prefix_parse2 = ('t_timestamp', 't_pid', 't_loglevel', 't_session_id', 't_dbname', 't_dbuser', 't_client', 't_query');
+
# Set syslog prefix regex
my $other_syslog_line =
qr/^(...)\s+(\d+)\s(\d+):(\d+):(\d+)(?:\s[^\s]+)?\s([^\s]+)\s([^\s\[]+)\[(\d+)\]:(?:\s\[[^\]]+\])?\s\[(\d+)\-\d+\]\s*(.*)/;
$orphan_syslog_line = qr/^(\d+-\d+)-(\d+)T(\d+):(\d+):(\d+)(?:.[^\s]+)?\s([^\s]+)\s(?:[^\s]+\s)?(?:[^\s]+\s)?([^\s\[]+)\[(\d+)\]:/;
}
+
# Set default top query
$top ||= 20;
# Where statistics are stored
my %overall_stat = ();
+my %pgb_overall_stat = ();
my %overall_checkpoint = ();
my @top_slowest = ();
my %normalyzed_info = ();
my %error_info = ();
+my %pgb_error_info = ();
my %logs_type = ();
my %per_minute_info = ();
+my %pgb_per_minute_info = ();
my %lock_info = ();
my %tempfile_info = ();
my %cancelled_info = ();
my %connection_info = ();
+my %pgb_connection_info = ();
my %database_info = ();
my %application_info = ();
my %user_info = ();
my %host_info = ();
my %session_info = ();
+my %pgb_session_info = ();
my %conn_received = ();
my %checkpoint_info = ();
my %autovacuum_info = ();
my $wn = &get_week_number($last_year, $last_month, $last_day);
# Get the days of the current week where binary files must be preserved
my @wdays = &get_wdays_per_month($wn - 1, "$last_year-$last_month");
-
# Find obsolete dir days that shoud be cleaned
unless(opendir(DIR, "$outdir")) {
localdie("Error: can't opendir $outdir: $!");
my $td = timediff($t1, $t0);
&logmsg('DEBUG', "the log statistics gathering took:" . timestr($td));
-
if (!$incremental && ($#given_log_files >= 0) ) {
&logmsg('LOG', "Ok, generating $extension report...");
&logmsg('HINT', "maybe there's no new entries in your log since last run.");
}
&build_incremental_reports(@build_directories);
+
}
my $t2 = Benchmark->new;
the same time.
--rebuild : used to rebuild all html reports in incremental
output directories where there is binary data files.
+ --pgbouncer-only : only show pgbouncer related menu in the header.
pgBadger is able to parse a remote log file using a passwordless ssh connection.
Use the -r or --remote-host to set the host ip address or hostname. There's also
}
&logmsg('LOG', "Ok, generating global index to access incremental reports...");
-
+
$fh = new IO::File ">$outdir/index.html";
if (not defined $fh) {
localdie("FATAL: can't write to $outdir/index.html, $!\n");
# Empty where statistics are stored
%overall_stat = ();
+ %pgb_overall_stat = ();
%overall_checkpoint = ();
@top_slowest = ();
@top_tempfile_info = ();
@top_locked_info = ();
%normalyzed_info = ();
%error_info = ();
+ %pgb_error_info = ();
%logs_type = ();
%per_minute_info = ();
+ %pgb_per_minute_info = ();
%lock_info = ();
%tempfile_info = ();
%cancelled_info = ();
%connection_info = ();
+ %pgb_connection_info = ();
%database_info = ();
%application_info = ();
%session_info = ();
+ %pgb_session_info = ();
%conn_received = ();
%checkpoint_info = ();
%autovacuum_info = ();
&logmsg('DEBUG', "Starting reading file $remote_host:$logfile...");
}
- if ($format eq 'csv') {
+ # Detect if we are parsing a pgbouncer file
+ my ($is_pgbouncer_format, $retcode, $msg) = &detect_pgbouncer_log($logfile, $saved_last_line{datetime}, 1);
+
+ # Parse pgbouncer logfile
+ if ($is_pgbouncer_format) {
+
+ my $time_pattern = qr/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/;
+ my $cur_pid = '';
+ my @matches = ();
+ my $goon = 0;
+ my $has_exclusion = 0;
+ if ($#exclude_line >= 0) {
+ $has_exclusion = 1;
+ }
+ &logmsg('DEBUG', "Start parsing at offset $start_offset of file $logfile to " . ($stop_offset || $totalsize));
+ if ($start_offset) {
+ # Move to the starting offset position in file
+ $lfile->seek($start_offset, 0);
+ }
+ while (my $line = <$lfile>) {
+
+ # We received a signal
+ last if ($terminate);
+
+ # Get current size/offset in the log file
+ $cursize += length($line);
+ $current_offset += length($line);
+
+ # Replace CR/LF by LF
+ $line =~ s/\r//;
+
+ # Start to exclude from parsing any desired lines
+ if ($has_exclusion >= 0) {
+
+ # Log line matches the excluded regex
+ my $ef = 0;
+ map { $ef = 1, last if ($line =~ /$_/is); } @exclude_line;
+ next if ($ef);
+ }
+
+ chomp($line);
+ $nlines++;
+ next if (!$line);
+
+ if (!$tmpoutfile) {
+ if ($progress && (($nlines % $NUMPROGRESS) == 0)) {
+ if ($totalsize) {
+ print STDERR &progress_bar($cursize, $stop_offset || $totalsize, 25, '=');
+ } else {
+ print STDERR ".";
+ }
+ }
+ } else {
+ if ($progress && (($nlines % $NUMPROGRESS) == 0)) {
+ $pipe->print("$cursize\n");
+ $cursize = 0;
+ }
+ }
+
+ %prefix_vars = ();
+
+ @matches = ($line =~ $pgbouncer_log_parse1);
+ if ($#matches >= 0) {
+ for (my $i = 0 ; $i <= $#pgb_prefix_parse1 ; $i++) {
+ $prefix_vars{$pgb_prefix_parse1[$i]} = $matches[$i];
+ }
+
+ # Get time detailed information
+ ($prefix_vars{'t_year'}, $prefix_vars{'t_month'}, $prefix_vars{'t_day'}, $prefix_vars{'t_hour'},
+ $prefix_vars{'t_min'}, $prefix_vars{'t_sec'}) = ($prefix_vars{'t_timestamp'} =~ $time_pattern);
+
+ # Skip unwanted lines
+ if ($#exclude_time >= 0) {
+ foreach (@exclude_time) {
+ if ($prefix_vars{'t_timestamp'} =~ /$_/) {
+ return;
+ }
+ }
+ }
+ next if ($from && ($from gt $prefix_vars{'t_timestamp'}));
+ if ($to && ($to lt $prefix_vars{'t_timestamp'})) {
+ if (!$tmpoutfile) {
+ if ($totalsize) {
+ print STDERR &progress_bar($cursize, $stop_offset || $totalsize, 25, '=');
+ } else {
+ print STDERR ".";
+ }
+ } else {
+ $pipe->print("$cursize\n");
+ $cursize = 0;
+ }
+ $getout = 2;
+ last;
+ }
+
+ # Jump to the last line parsed if required
+ next if (!&check_incremental_position($prefix_vars{'t_timestamp'}, $line));
+
+ # Extract other information from the line
+ @matches = ($line =~ $pgbouncer_log_parse2);
+ if ($#matches >= 0) {
+ for (my $i = 0 ; $i <= $#pgb_prefix_parse2 ; $i++) {
+ $prefix_vars{$pgb_prefix_parse2[$i]} = $matches[$i];
+ }
+ $prefix_vars{'t_client'} = _gethostbyaddr($prefix_vars{'t_client'}) if ($dns_resolv && $prefix_vars{'t_client'});
+ } else {
+ # pgBouncer Statistics appears each minutes in the log
+ if ($prefix_vars{'t_query'} =~ /Stats: (\d+) req\/s, in (\d+) b\/s, out (\d+) b\/s,query (\d+) us/) {
+ $prefix_vars{'t_loglevel'} = 'STATS';
+ $prefix_vars{'t_req/s'} = $1;
+ $prefix_vars{'t_inbytes/s'} = $2;
+ $prefix_vars{'t_outbytes/s'} = $3;
+ $prefix_vars{'t_avgduration'} = $4;
+ }
+ }
+
+ # Store the current timestamp of the log line
+ &store_current_timestamp($prefix_vars{'t_timestamp'});
+
+ # Check if the log line should be excluded from the report
+ if (&validate_log_line($prefix_vars{'t_pid'})) {
+ $prefix_vars{'t_host'} = 'stderr'; # this unused variable is used to store format information when log format is not syslog
+
+ # Process the log line
+ &parse_pgbouncer();
+ }
+
+ } else {
+ # unknown format
+ &logmsg('DEBUG', "Unknown pgbouncer line format: $line");
+ }
+
+ last if (($stop_offset > 0) && ($current_offset >= $stop_offset));
+ }
+ if ($last_parsed) {
+ $last_line{current_pos} = $current_offset;
+ }
+
+ # Parse PostgreSQL log file with CSV format
+ } elsif ($format eq 'csv') {
require Text::CSV_XS;
my $csv = Text::CSV_XS->new(
}
}
+# Method used to check if the log file is produced by pgbouncer
+sub detect_pgbouncer_log
+{
+ my ($file, $saved_date, $look_at_beginning) = @_;
+
+ my ($lfile, $totalsize, $iscompressed) = &get_log_file($file);
+
+ # Compressed files do not allow seeking
+ if ($iscompressed) {
+ close($lfile);
+ return (1, "log file is compressed");
+
+ }
+
+ my ($gsec, $gmin, $ghour, $gmday, $gmon, $gyear, $gwday, $gyday, $gisdst) = localtime(time);
+ $gyear += 1900;
+ my $CURRENT_DATE = $gyear . sprintf("%02d", $gmon + 1) . sprintf("%02d", $gmday);
+
+ %prefix_vars = ();
+ my $startoffset = 0;
+ # If seeking is not explicitely disabled
+ if (!$look_at_beginning) {
+ # do not seek if filesize is smaller than the seek position
+ if ($saved_last_line{current_pos} < $totalsize) {
+ $lfile->seek($saved_last_line{current_pos} || 0, 0);
+ $startoffset = $saved_last_line{current_pos} || 0;
+ }
+ }
+
+ my $more_lines = 0;
+ my $ispgbouncerlog = 0;
+ while (my $line = <$lfile>) {
+
+ $more_lines++;
+
+ $line =~ s/\r//;
+
+ my @matches = ($line =~ $pgbouncer_log_format);
+ if ($#matches >= 0) {
+ $ispgbouncerlog++;
+ for (my $i = 0 ; $i <= $#pgb_prefix_params ; $i++) {
+ $prefix_vars{$pgb_prefix_params[$i]} = $matches[$i];
+ }
+ }
+ next if (!$prefix_vars{'t_timestamp'});
+ # This file has already been parsed
+ if ($saved_date gt $prefix_vars{'t_timestamp'}) {
+ close($lfile);
+ return ($ispgbouncerlog, 0, "timestamp $prefix_vars{'t_timestamp'} read at offset $startoffset is lower than saved timestamp: $saved_date");
+ } else {
+ last;
+ }
+ }
+ close($lfile);
+
+ if (!$more_lines) {
+ close($lfile);
+ return ($ispgbouncerlog, 0, "there no new lines in this file");
+ }
+
+ return ($ispgbouncerlog, 1, "reach the end of check_file_changed() with start date: $saved_date and file size: $totalsize") ;
+}
+
+
# Method used to check if the file stores logs after the last incremental position or not
# This position should have been saved in the incremental file and read in the $last_parsed at
# start up. Here we just verify that the first date in file is before the last incremental date.
# Normalise alias with quote
$orig_query =~ s/AS\s+"([^"]+)"/'AS "' . remove_alias($1) . '"'/eigs;
-
+
# Remove string content
$orig_query =~ s/\\'//gs;
$orig_query =~ s/'[^']*'/''/gs;
}
}
+# Stores top N error sample from pgbouncer log
+sub pgb_set_top_error_sample
+{
+ my ($q, $date, $real_error, $db, $user, $remote) = @_;
+
+ # Stop when we have our number of samples
+ if (!exists $pgb_error_info{$q}{date} || ($#{$pgb_error_info{$q}{date}} < $sample)) {
+ push(@{$pgb_error_info{$q}{date}}, $date);
+ push(@{$pgb_error_info{$q}{error}}, $real_error);
+ push(@{$pgb_error_info{$q}{db}}, $db);
+ push(@{$pgb_error_info{$q}{user}}, $user);
+ push(@{$pgb_error_info{$q}{remote}}, $remote);
+ }
+}
+
sub dump_as_text
{
$idx++;
}
}
+
if (!$disable_query && (scalar keys %normalyzed_info > 0)) {
print $fh "\n- Most frequent queries (N) --------------------------------------------\n\n";
print $fh "Rank Times executed Total duration Min/Max/Avg duration (s) Query\n";
&show_error_as_text();
}
+
+ # Show pgbouncer session per database statistics
+ if (exists $pgb_session_info{database}) {
+ print $fh "\n- pgBouncer sessions per database --------------------------------------------\n\n";
+ print $fh "Database Count Total Duration Avg duration (s)\n";
+ foreach my $d (sort keys %{$pgb_session_info{database}}) {
+ print $fh "$d - ", &comma_numbers($pgb_session_info{database}{$d}{count}), " ",
+ &convert_time($pgb_session_info{database}{$d}{duration}), " ",
+ &convert_time($pgb_session_info{database}{$d}{duration} / $pgb_session_info{database}{$d}{count}), "\n";
+ }
+ }
+
+ # Show pgbouncer session per user statistics
+ if (exists $pgb_session_info{user}) {
+ print $fh "\n- pgBouncer sessions per user ------------------------------------------------\n\n";
+ print $fh "User Count Total Duration Avg duration (s)\n";
+ foreach my $d (sort keys %{$pgb_session_info{user}}) {
+ print $fh "$d - ", &comma_numbers($pgb_session_info{user}{$d}{count}), " ", &convert_time($pgb_session_info{user}{$d}{duration}),
+ " ", &convert_time($pgb_session_info{user}{$d}{duration} / $pgb_session_info{user}{$d}{count}), "\n";
+ }
+ }
+
+ # Show pgbouncer session per host statistics
+ if (exists $pgb_session_info{host}) {
+ print $fh "\n- pgBouncer sessions per host ------------------------------------------------\n\n";
+ print $fh "User Count Total Duration Avg duration (s)\n";
+ foreach my $d (sort keys %{$pgb_session_info{host}}) {
+ print $fh "$d - ", &comma_numbers($pgb_session_info{host}{$d}{count}), " ", &convert_time($pgb_session_info{host}{$d}{duration}),
+ " ", &convert_time($pgb_session_info{host}{$d}{duration} / $pgb_session_info{host}{$d}{count}), "\n";
+ }
+ }
+
+ # Show pgbouncer session per application statistics
+ if (exists $pgb_session_info{app}) {
+ print $fh "\n- pgBouncer sessions per application -----------------------------------------\n\n";
+ print $fh "Application Count Total Duration Avg duration (s)\n";
+ foreach my $d (sort keys %{$pgb_session_info{app}}) {
+ print $fh "$d - ", &comma_numbers($pgb_session_info{app}{$d}{count}), " ", &convert_time($pgb_session_info{app}{$d}{duration}),
+ " ", &convert_time($pgb_session_info{app}{$d}{duration} / $pgb_session_info{app}{$d}{count}), "\n";
+ }
+ }
+
+
+ # Show pgbouncer connection per database statistics
+ if (exists $pgb_connection_info{database}) {
+ print $fh "\n- pgBouncer sonnections per database -----------------------------------------\n\n";
+ print $fh "Database User Count\n";
+ foreach my $d (sort keys %{$pgb_connection_info{database}}) {
+ print $fh "$d - ", &comma_numbers($pgb_connection_info{database}{$d}), "\n";
+ foreach my $u (sort keys %{$pgb_connection_info{user}}) {
+ next if (!exists $pgb_connection_info{database_user}{$d}{$u});
+ print $fh "\t$u ", &comma_numbers($pgb_connection_info{database_user}{$d}{$u}), "\n";
+ }
+ }
+ }
+
+ # Show pgbouncer connection per user statistics
+ if (exists $pgb_connection_info{user}) {
+ print $fh "\n- pgBouncer connections per user ---------------------------------------------\n\n";
+ print $fh "User Count\n";
+ foreach my $d (sort keys %{$pgb_connection_info{user}}) {
+ print $fh "$d - ", &comma_numbers($pgb_connection_info{user}{$d}), "\n";
+ }
+ }
+
+ # Show pgbouncer connection per host statistics
+ if (exists $pgb_connection_info{host}) {
+ print $fh "\n- pgBouncer connections per host --------------------------------------------\n\n";
+ print $fh "User Count\n";
+ foreach my $d (sort keys %{$pgb_connection_info{host}}) {
+ print $fh "$d - ", &comma_numbers($pgb_connection_info{host}{$d}), "\n";
+ }
+ }
+
+ if (!$disable_error) {
+ &show_pgb_error_as_text();
+ }
+
print $fh "\n\n";
print $fh "Report generated by pgBadger $VERSION ($project_url).\n";
}
}
+sub show_pgb_error_as_text
+{
+ return if (scalar keys %error_info == 0);
+
+ print $fh "\n- Most frequent events (N) ---------------------------------------------\n\n";
+ my $idx = 1;
+ foreach my $k (sort {$pgb_error_info{$b}{count} <=> $pgb_error_info{$a}{count}} keys %pgb_error_info) {
+ next if (!$pgb_error_info{$k}{count});
+ last if ($idx > $top);
+ if ($pgb_error_info{$k}{count} > 1) {
+ my $msg = $k;
+ print $fh "$idx) " . &comma_numbers($pgb_error_info{$k}{count}) . " - $msg\n";
+ print $fh "--\n";
+ my $j = 1;
+ for (my $i = 0 ; $i <= $#{$pgb_error_info{$k}{date}} ; $i++) {
+ print $fh "\t- Example $j: $pgb_error_info{$k}{date}[$i] - $pgb_error_info{$k}{error}[$i]\n";
+ print $fh "\t\tDatabase: $pgb_error_info{$k}{db}[$i]\n" if ($pgb_error_info{$k}{db}[$i]);
+ print $fh "\t\tUser: $pgb_error_info{$k}{user}[$i]\n" if ($pgb_error_info{$k}{user}[$i]);
+ print $fh "\t\tClient: $pgb_error_info{$k}{remote}[$i]\n" if ($pgb_error_info{$k}{remote}[$i]);
+ $j++;
+ }
+ } else {
+ print $fh "$idx) " . &comma_numbers($pgb_error_info{$k}{count}) . " - $pgb_error_info{$k}{error}[0]\n";
+ print $fh "--\n";
+ print $fh "\t- Date: $pgb_error_info{$k}{date}[0]\n";
+ print $fh "\t\tDatabase: $pgb_error_info{$k}{db}[0]\n" if ($pgb_error_info{$k}{db}[0]);
+ print $fh "\t\tUser: $pgb_error_info{$k}{user}[0]\n" if ($pgb_error_info{$k}{user}[0]);
+ print $fh "\t\tClient: $pgb_error_info{$k}{remote}[0]\n" if ($pgb_error_info{$k}{remote}[0]);
+ }
+ $idx++;
+ }
+
+}
+
+
sub html_header
{
my $uri = shift;
<a data-placement="bottom" rel="tooltip" data-original-title="PostgreSQL Log Analyzer" href="" id="pgbadger-brand" class="brand">$pgbadger_logo $report_title</a>
<ul class="nav collapse in" id="navigation">
};
- if (!$error_only) {
+ if (!$error_only && !$pgbouncer_only) {
print $fh qq{
<li id="menu-overview" class="dropdown"><a class="dropdown-toggle" data-toggle="dropdown" href="#">Overview <span class="caret"></span></a>
<ul class="dropdown-menu">
</li>
};
}
+
if (!$disable_session) {
print $fh qq{
<li id="menu-sessions" class="dropdown"><a class="dropdown-toggle" data-toggle="dropdown" href="#">Sessions <span class="caret"></span></a>
};
}
}
- if (!$disable_error) {
+ if (!$disable_error && !$pgbouncer_only) {
print $fh qq{
<li id="menu-events" class="dropdown"><a class="dropdown-toggle" data-toggle="dropdown" href="#">Events <span class="caret"></span></a>
<ul class="dropdown-menu">
</li>
};
}
+
+ if (exists $pgb_overall_stat{peak}) {
+ print $fh qq{
+ <li id="menu-pgbouncer" class="dropdown"><a class="dropdown-toggle" data-toggle="dropdown" href="#">PgBouncer <span class="caret"></span></a>
+ <ul class="dropdown-menu">
+};
+ if (!$disable_hourly) {
+ print $fh qq{
+ <li><a href="#pgbsql-traffic">Request Troughput</a></li>
+ <li><a href="#pgbbytes-traffic">Bytes I/O Throughput</a></li>
+ <li><a href="#pgbduration-traffic">Queries Average duration</a></li>
+ <li class="divider"></li>
+ <li><a href="#pgbsimultaneous-sessions">Simultaneous sessions</a></li>
+};
+ }
+ print $fh qq{
+ <li><a href="#pgbhistogram-session-times">Histogram of sessions times</a></li>
+ <li><a href="#pgbsessions-per-database">Sessions per database</a></li>
+ <li><a href="#pgbsessions-per-user">Sessions per user</a></li>
+ <li><a href="#pgbsessions-per-host">Sessions per host</a></li>
+ <li class="divider"></li>
+};
+ if (!$disable_hourly) {
+ print $fh qq{
+ <li><a href="#pgbestablished-connections">Established connections</a></li>
+};
+ }
+ print $fh qq{
+ <li><a href="#pgbconnections-per-database">Connections per database</a></li>
+ <li><a href="#pgbconnections-per-user">Connections per user</a></li>
+ <li><a href="#pgbconnections-per-host">Connections per host</a></li>
+ <li class="divider"></li>
+ <li><a href="#pgbqueries-most-frequent-waiting">Most frequent waiting queries (N)</a></li>
+ </ul>
+ </li>
+};
+ }
+
print $fh qq{
</ul>
<a class="pull-right btn btn-info" id="pop-infos" rel="popover" data-html="true" data-placement="bottom" data-original-title="Log Info" data-content="$global_info">
}
-sub compute_query_graphs
+sub print_pgbouncer_stats
{
- my %graph_data = ();
- if ($graph) {
- foreach my $tm (sort {$a <=> $b} keys %per_minute_info) {
- $tm =~ /(\d{4})(\d{2})(\d{2})/;
- my $y = $1 - 1900;
- my $mo = $2 - 1;
- my $d = $3;
- foreach my $h ("00" .. "23") {
- next if (!exists $per_minute_info{$tm}{$h});
- my %q_dataavg = ();
- my %a_dataavg = ();
- my %c_dataavg = ();
- my %s_dataavg = ();
- my %p_dataavg = ();
+ my $request_peak = 0;
+ my $request_peak_date = '';
+ foreach (sort {$pgb_overall_stat{'peak'}{$b}{t_req} <=> $pgb_overall_stat{'peak'}{$a}{t_req}} keys %{$pgb_overall_stat{'peak'}}) {
+ $request_peak = &comma_numbers($pgb_overall_stat{'peak'}{$_}{t_req});
+ $request_peak_date = $_;
+ last;
+ }
+
+ my $inbytes_peak = 0;
+ my $inbytes_peak_date = '';
+ foreach (sort {$pgb_overall_stat{'peak'}{$b}{t_inbytes} <=> $pgb_overall_stat{'peak'}{$a}{t_inbytes}} keys %{$pgb_overall_stat{'peak'}}) {
+ $inbytes_peak = &comma_numbers($pgb_overall_stat{'peak'}{$_}{t_inbytes});
+ $inbytes_peak_date = $_;
+ last;
+ }
+
+ my $outbytes_peak = 0;
+ my $outbytes_peak_date = '';
+ foreach (sort {$pgb_overall_stat{'peak'}{$b}{t_outbytes} <=> $pgb_overall_stat{'peak'}{$a}{t_outbytes}} keys %{$pgb_overall_stat{'peak'}}) {
+ $outbytes_peak = &comma_numbers($pgb_overall_stat{'peak'}{$_}{t_outbytes});
+ $outbytes_peak_date = $_;
+ last;
+ }
+
+ my $avgduration_peak = 0;
+ my $avgduration_peak_date = '';
+ foreach (sort {$pgb_overall_stat{'peak'}{$b}{t_avgduration} <=> $pgb_overall_stat{'peak'}{$a}{t_avgduration}} keys %{$pgb_overall_stat{'peak'}}) {
+ $avgduration_peak = &convert_time($pgb_overall_stat{'peak'}{$_}{t_avgduration});
+ $avgduration_peak_date = $_;
+ last;
+ }
+
+ print $fh qq{
+ <div id="pgbsql-traffic" class="analysis-item row-fluid">
+ <h2 class=""><i class="icon-road"></i> Request Troughput</h2>
+ <div class="span3">
+ <h3 class="">Key values</h3>
+ <div class="well key-figures">
+ <ul>
+ <li><span class="figure">$request_peak queries/s</span> <span class="figure-label">Request Peak</span></li>
+ <li><span class="figure">$request_peak_date</span> <span class="figure-label">Date</span></li>
+ </ul>
+ </div>
+ </div>
+ <div class="span8">
+$drawn_graphs{pgb_requestpersecond_graph}
+ </div>
+ </div><!-- end of sql-traffic -->
+};
+ delete $drawn_graphs{pgb_requestpersecond_graph};
+
+ print $fh qq{
+ <div id="pgbbytes-traffic" class="analysis-item row-fluid">
+ <h2 class=""><i class="icon-road"></i> Bytes I/O Throughput</h2>
+ <div class="span3">
+ <h3 class="">Key values</h3>
+ <div class="well key-figures">
+ <ul>
+ <li><span class="figure">$inbytes_peak Bytes/s</span> <span class="figure-label">In Bytes Peak</span></li>
+ <li><span class="figure">$inbytes_peak_date</span> <span class="figure-label">Date</span></li>
+ <li><span class="figure">$outbytes_peak Bytes/s</span> <span class="figure-label">Out Bytes Peak</span></li>
+ <li><span class="figure">$outbytes_peak_date</span> <span class="figure-label">Date</span></li>
+ </ul>
+ </div>
+ </div>
+ <div class="span8">
+$drawn_graphs{pgb_bytepersecond_graph}
+ </div>
+ </div><!-- end of select-traffic -->
+};
+ delete $drawn_graphs{pgb_bytepersecond_graph};
+
+ print $fh qq{
+ <div id="pgbduration-traffic" class="analysis-item row-fluid">
+ <h2 class=""><i class="icon-time"></i> Queries Average duration</h2>
+ <div class="span3">
+ <h3 class="">Key values</h3>
+ <div class="well key-figures">
+ <ul>
+ <li><span class="figure">$avgduration_peak</span> <span class="figure-label">Average Duration Peak</span></li>
+ <li><span class="figure">$avgduration_peak_date</span> <span class="figure-label">Date</span></li>
+ </ul>
+ </div>
+ </div>
+ <div class="span8">
+$drawn_graphs{pgb_avgduration_graph}
+ </div>
+ </div><!-- end of duration-traffic -->
+};
+ delete $drawn_graphs{pgb_avgduration_graph};
+
+}
+
+sub compute_query_graphs
+{
+ my %graph_data = ();
+ if ($graph) {
+
+ foreach my $tm (sort {$a <=> $b} keys %per_minute_info) {
+ $tm =~ /(\d{4})(\d{2})(\d{2})/;
+ my $y = $1 - 1900;
+ my $mo = $2 - 1;
+ my $d = $3;
+ foreach my $h ("00" .. "23") {
+ next if (!exists $per_minute_info{$tm}{$h});
+ my %q_dataavg = ();
+ my %a_dataavg = ();
+ my %c_dataavg = ();
+ my %s_dataavg = ();
+ my %p_dataavg = ();
foreach my $m ("00" .. "59") {
next if (!exists $per_minute_info{$tm}{$h}{$m});
}
+sub compute_pgbouncer_graphs
+{
+ my %graph_data = ();
+ if ($graph) {
+
+ foreach my $tm (sort {$a <=> $b} keys %pgb_per_minute_info) {
+ $tm =~ /(\d{4})(\d{2})(\d{2})/;
+ my $y = $1 - 1900;
+ my $mo = $2 - 1;
+ my $d = $3;
+ foreach my $h ("00" .. "23") {
+ next if (!exists $pgb_per_minute_info{$tm}{$h});
+ my %c_dataavg = ();
+ my %s_dataavg = ();
+ foreach my $m ("00" .. "59") {
+
+ my $t = timegm_nocheck(0, $m, $h, $d, $mo, $y) * 1000;
+ $t += ($timezone*1000);
+
+ next if ($t < $t_min);
+ last if ($t > $t_max);
+
+ # pgBouncer stats are generate each minutes, always keep this interval
+ $graph_data{'request'} .= "[$t, " . ($pgb_per_minute_info{$tm}{$h}{$m}{t_req} || 0) . "],";
+ $graph_data{'inbytes'} .= "[$t, " . ($pgb_per_minute_info{$tm}{$h}{$m}{t_inbytes} || 0) . "],";
+ $graph_data{'outbytes'} .= "[$t, " . ($pgb_per_minute_info{$tm}{$h}{$m}{t_outbytes} || 0) . "],";
+ $graph_data{'avgduration'} .= "[$t, " . ($pgb_per_minute_info{$tm}{$h}{$m}{t_avgduration} || 0) . "],";
+ next if (!exists $pgb_per_minute_info{$tm}{$h}{$m});
+
+ my $rd = &average_per_minutes($m, $avg_minutes);
+
+ if (exists $pgb_per_minute_info{$tm}{$h}{$m}{connection}) {
+
+ # Average per minute
+ $c_dataavg{average}{"$rd"} += $pgb_per_minute_info{$tm}{$h}{$m}{connection}{count};
+
+ # Search minimum and maximum during this minute
+ foreach my $s (keys %{$pgb_per_minute_info{$tm}{$h}{$m}{connection}{second}}) {
+ $c_dataavg{max}{"$rd"} = $pgb_per_minute_info{$tm}{$h}{$m}{connection}{second}{$s}
+ if ($pgb_per_minute_info{$tm}{$h}{$m}{connection}{second}{$s} > $c_dataavg{max}{"$rd"});
+ $c_dataavg{min}{"$rd"} = $pgb_per_minute_info{$tm}{$h}{$m}{connection}{second}{$s}
+ if ($pgb_per_minute_info{$tm}{$h}{$m}{connection}{second}{$s} < $c_dataavg{min}{"$rd"});
+ }
+ delete $pgb_per_minute_info{$tm}{$h}{$m}{connection};
+ }
+
+ if (exists $pgb_per_minute_info{$tm}{$h}{$m}{session}) {
+
+ # Average per minute
+ $s_dataavg{average}{"$rd"} += $pgb_per_minute_info{$tm}{$h}{$m}{session}{count};
+
+ # Search minimum and maximum during this minute
+ foreach my $s (keys %{$pgb_per_minute_info{$tm}{$h}{$m}{session}{second}}) {
+ $s_dataavg{max}{"$rd"} = $pgb_per_minute_info{$tm}{$h}{$m}{session}{second}{$s}
+ if ($pgb_per_minute_info{$tm}{$h}{$m}{session}{second}{$s} > $s_dataavg{max}{"$rd"});
+ $s_dataavg{min}{"$rd"} = $pgb_per_minute_info{$tm}{$h}{$m}{session}{second}{$s}
+ if ($pgb_per_minute_info{$tm}{$h}{$m}{session}{second}{$s} < $s_dataavg{min}{"$rd"});
+ }
+ delete $pgb_per_minute_info{$tm}{$h}{$m}{session};
+ }
+ }
+
+ foreach my $rd (@avgs) {
+ my $t = timegm_nocheck(0, $rd, $h, $d, $mo, $y) * 1000;
+ $t += ($timezone*1000);
+
+ next if ($t < $t_min);
+ last if ($t > $t_max);
+
+ if (scalar keys %c_dataavg) {
+ # Average connections per minute
+ $graph_data{conn_avg} .= "[$t, " . int(($c_dataavg{average}{"$rd"} || 0) / (60 * $avg_minutes)) . "],";
+ # Max connections per minute
+ $graph_data{conn_max} .= "[$t, " . ($c_dataavg{max}{"$rd"} || 0) . "],";
+
+ # Min connections per minute
+ $graph_data{conn_min} .= "[$t, " . ($c_dataavg{min}{"$rd"} || 0) . "],";
+ }
+ if (scalar keys %s_dataavg) {
+ # Average connections per minute
+ $graph_data{sess_avg} .= "[$t, " . int(($s_dataavg{average}{"$rd"} || 0) / (60 * $avg_minutes)) . "],";
+ # Max connections per minute
+ $graph_data{sess_max} .= "[$t, " . ($s_dataavg{max}{"$rd"} || 0) . "],";
+
+ # Min connections per minute
+ $graph_data{sess_min} .= "[$t, " . ($s_dataavg{min}{"$rd"} || 0) . "],";
+ }
+ }
+ }
+ }
+ foreach (keys %graph_data) {
+ $graph_data{$_} =~ s/,$//;
+ }
+ }
+
+ $drawn_graphs{'pgb_requestpersecond_graph'} = &jqplot_linegraph( $graphid++, 'pgb_requestpersecond_graph', $graph_data{request},'',,'','Request per seconds (1 minute average)', '', 'Request per second');
+
+ $drawn_graphs{'pgb_bytepersecond_graph'} = &jqplot_linegraph( $graphid++, 'pgb_bytepersecond_graph', $graph_data{inbytes},$graph_data{'outbytes'},'','Bytes I/O per seconds (1 minute average)', 'size', 'In b/s', 'Out b/s');
+
+ $drawn_graphs{'pgb_avgduration_graph'} = &jqplot_linegraph( $graphid++, 'pgb_avgduration_graph', $graph_data{avgduration},'','', 'Average query duration (1 minute average)', 'duration', 'Duration');
+
+ $drawn_graphs{'pgb_connectionspersecond_graph'} = &jqplot_linegraph( $graphid++, 'pgb_connectionspersecond_graph', $graph_data{conn_max},
+ $graph_data{conn_avg}, $graph_data{conn_min}, 'Connections per second (' . $avg_minutes . ' minutes average)',
+ 'Connections per second', 'Maximum', 'Average', 'Minimum'
+ );
+
+ $drawn_graphs{'pgb_sessionspersecond_graph'} = &jqplot_linegraph( $graphid++, 'pgb_sessionspersecond_graph', $graph_data{sess_max},
+ $graph_data{sess_avg}, $graph_data{sess_min}, 'Number of sessions (' . $avg_minutes . ' minutes average)',
+ 'Sessions', 'Maximum', 'Average', 'Minimum'
+ );
+
+}
+
sub print_established_connection
{
}
+sub print_established_pgb_connection
+{
+
+ my $connection_peak = 0;
+ my $connection_peak_date = '';
+ foreach (sort {$pgb_overall_stat{'peak'}{$b}{connection} <=> $pgb_overall_stat{'peak'}{$a}{connection}} keys %{$pgb_overall_stat{'peak'}}) {
+ $connection_peak = &comma_numbers($pgb_overall_stat{'peak'}{$_}{connection});
+ $connection_peak_date = $_;
+ last;
+ }
+
+ print $fh qq{
+ <div id="pgbestablished-connections" class="analysis-item row-fluid">
+ <h2 class=""><i class="icon-random"></i> Established Connections</h2>
+ <div class="span3">
+ <h3 class="">Key values</h3>
+ <div class="well key-figures">
+ <ul>
+ <li><span class="figure">$connection_peak connections</span> <span class="figure-label">Connection Peak</span></li>
+ <li><span class="figure">$connection_peak_date</span> <span class="figure-label">Date</span></li>
+ </ul>
+ </div>
+ </div>
+ <div class="span8">
+$drawn_graphs{pgb_connectionspersecond_graph}
+ </div>
+ </div><!-- end of Established connections -->
+};
+ delete $drawn_graphs{pgb_connectionspersecond_graph};
+
+}
+
sub print_user_connection
{
delete $drawn_graphs{userconnections_graph};
}
+sub print_user_pgb_connection
+{
+
+ my %infos = ();
+ my $total_count = 0;
+ my $c = 0;
+ my $conn_user_info = '';
+ my @main_user = ('unknown',0);
+ foreach my $u (sort keys %{$pgb_connection_info{user}}) {
+ $conn_user_info .= "<tr><td>$u</td><td>" .
+ &comma_numbers($pgb_connection_info{user}{$u}) . "</td></tr>";
+ $total_count += $pgb_connection_info{user}{$u};
+ if ($main_user[1] < $pgb_connection_info{user}{$u}) {
+ $main_user[0] = $u;
+ $main_user[1] = $pgb_connection_info{user}{$u};
+ }
+ }
+ if ($graph) {
+ my @small = ();
+ foreach my $d (sort keys %{$pgb_connection_info{user}}) {
+ if ((($pgb_connection_info{user}{$d} * 100) / ($total_count||1)) > $pie_percentage_limit) {
+ $infos{$d} = $pgb_connection_info{user}{$d} || 0;
+ } else {
+ $infos{"Sum connections < $pie_percentage_limit%"} += $pgb_connection_info{user}{$d} || 0;
+ push(@small, $d);
+ }
+ }
+ if ($#small == 0) {
+ $infos{$small[0]} = $infos{"Sum connections < $pie_percentage_limit%"};
+ delete $infos{"Sum connections < $pie_percentage_limit%"};
+ }
+ }
+ $drawn_graphs{pgb_userconnections_graph} = &jqplot_piegraph($graphid++, 'graph_pgb_userconnections', 'Connections per user', %infos);
+ $total_count = &comma_numbers($total_count);
+ print $fh qq{
+ <div class="analysis-item row-fluid" id="pgbconnections-per-user">
+ <h2><i class="icon-user"></i> Connections per user</h2>
+ <div class="span3">
+ <h3 class="">Key values</h3>
+ <div class="well key-figures">
+ <ul>
+ <li><span class="figure">$main_user[0]</span> <span class="figure-label">Main User</span></li>
+ <li><span class="figure">$total_count connections</span> <span class="figure-label">Total</span></li>
+ </ul>
+ </div>
+ </div>
+ <div class="span8">
+ <div class="tabbable">
+ <ul class="nav nav-tabs">
+ <li class="active"><a href="#pgbconnections-per-user-graph" data-toggle="tab">Chart</a></li>
+ <li><a href="#pgbconnections-per-user-table" data-toggle="tab">Table</a></li>
+ </ul>
+ <div class="tab-content">
+ <div class="tab-pane active" id="pgbconnections-per-user-graph">
+ $drawn_graphs{pgb_userconnections_graph}
+ </div>
+ <div class="tab-pane" id="pgbconnections-per-user-table">
+ <table class="table table-striped table-hover">
+ <thead>
+ <tr>
+ <th>User</th>
+ <th>Count</th>
+ </tr>
+ </thead>
+ <tbody>
+ $conn_user_info
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div><!-- end of connections per user -->
+};
+ delete $drawn_graphs{pgb_userconnections_graph};
+}
+
sub print_host_connection
{
my %infos = ();
delete $drawn_graphs{hostconnections_graph};
}
-sub print_database_connection
+sub print_host_pgb_connection
{
my %infos = ();
my $total_count = 0;
- my $conn_database_info = '';
- my @main_database = ('unknown',0);
- foreach my $d (sort keys %{$connection_info{database}}) {
- $conn_database_info .= "<tr><td>$d</td><td> </td><td>" .
- &comma_numbers($connection_info{database}{$d}) . "</td></tr>";
- $total_count += $connection_info{database}{$d};
- if ($main_database[1] < $connection_info{database}{$d}) {
- $main_database[0] = $d;
- $main_database[1] = $connection_info{database}{$d};
+ my $c = 0;
+ my $conn_host_info = '';
+ my @main_host = ('unknown',0);
+ foreach my $h (sort keys %{$pgb_connection_info{host}}) {
+ $conn_host_info .= "<tr><td>$h</td><td>" .
+ &comma_numbers($pgb_connection_info{host}{$h}) . "</td></tr>";
+ $total_count += $pgb_connection_info{host}{$h};
+ if ($main_host[1] < $pgb_connection_info{host}{$h}) {
+ $main_host[0] = $h;
+ $main_host[1] = $pgb_connection_info{host}{$h};
}
- foreach my $u (sort keys %{$connection_info{user}}) {
- next if (!exists $connection_info{database_user}{$d}{$u});
- $conn_database_info .= "<tr><td> </td><td>$u</td><td>" .
- &comma_numbers($connection_info{database_user}{$d}{$u}) . "</td></tr>";
+ }
+ if ($graph) {
+ my @small = ();
+ foreach my $d (sort keys %{$pgb_connection_info{host}}) {
+ if ((($pgb_connection_info{host}{$d} * 100) / ($total_count||1)) > $pie_percentage_limit) {
+ $infos{$d} = $pgb_connection_info{host}{$d} || 0;
+ } else {
+ $infos{"Sum connections < $pie_percentage_limit%"} += $pgb_connection_info{host}{$d} || 0;
+ push(@small, $d);
+ }
+ }
+ if ($#small == 0) {
+ $infos{$small[0]} = $infos{"Sum connections < $pie_percentage_limit%"};
+ delete $infos{"Sum connections < $pie_percentage_limit%"};
+ }
+ }
+ $drawn_graphs{pgb_hostconnections_graph} = &jqplot_piegraph($graphid++, 'graph_pgb_hostconnections', 'Connections per host', %infos);
+ $total_count = &comma_numbers($total_count);
+ print $fh qq{
+ <div class="analysis-item row-fluid" id="pgbconnections-per-host">
+ <h2><i class="icon-sitemap"></i> Connections per host</h2>
+ <div class="span3">
+ <h3 class="">Key values</h3>
+ <div class="well key-figures">
+ <ul>
+ <li><span class="figure">$main_host[0]</span> <span class="figure-label">Main host with $main_host[1] connections</span></li>
+ <li><span class="figure">$total_count</span> <span class="figure-label">Total connections</span></li>
+ </ul>
+ </div>
+ </div>
+ <div class="span8">
+ <div class="tabbable">
+ <ul class="nav nav-tabs">
+ <li class="active"><a href="#pgbconnections-per-host-graph" data-toggle="tab">Chart</a></li>
+ <li><a href="#pgbconnections-per-host-table" data-toggle="tab">Table</a></li>
+ </ul>
+ <div class="tab-content">
+ <div class="tab-pane active" id="pgbconnections-per-host-graph">
+ $drawn_graphs{pgb_hostconnections_graph}
+ </div>
+ <div class="tab-pane" id="pgbconnections-per-host-table">
+ <table class="table table-striped table-hover">
+ <thead>
+ <tr>
+ <th>Host</th>
+ <th>Count</th>
+ </tr>
+ </thead>
+ <tbody>
+ $conn_host_info
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div><!-- end of connections per host -->
+};
+
+ delete $drawn_graphs{pgb_hostconnections_graph};
+}
+
+sub print_database_connection
+{
+ my %infos = ();
+ my $total_count = 0;
+ my $conn_database_info = '';
+ my @main_database = ('unknown',0);
+ foreach my $d (sort keys %{$connection_info{database}}) {
+ $conn_database_info .= "<tr><td>$d</td><td> </td><td>" .
+ &comma_numbers($connection_info{database}{$d}) . "</td></tr>";
+ $total_count += $connection_info{database}{$d};
+ if ($main_database[1] < $connection_info{database}{$d}) {
+ $main_database[0] = $d;
+ $main_database[1] = $connection_info{database}{$d};
+ }
+ foreach my $u (sort keys %{$connection_info{user}}) {
+ next if (!exists $connection_info{database_user}{$d}{$u});
+ $conn_database_info .= "<tr><td> </td><td>$u</td><td>" .
+ &comma_numbers($connection_info{database_user}{$d}{$u}) . "</td></tr>";
}
}
if ($graph) {
delete $drawn_graphs{databaseconnections_graph};
}
+sub print_database_pgb_connection
+{
+ my %infos = ();
+ my $total_count = 0;
+ my $conn_database_info = '';
+ my @main_database = ('unknown',0);
+ foreach my $d (sort keys %{$pgb_connection_info{database}}) {
+ $conn_database_info .= "<tr><td>$d</td><td> </td><td>" .
+ &comma_numbers($pgb_connection_info{database}{$d}) . "</td></tr>";
+ $total_count += $pgb_connection_info{database}{$d};
+ if ($main_database[1] < $pgb_connection_info{database}{$d}) {
+ $main_database[0] = $d;
+ $main_database[1] = $pgb_connection_info{database}{$d};
+ }
+ foreach my $u (sort keys %{$pgb_connection_info{user}}) {
+ next if (!exists $pgb_connection_info{database_user}{$d}{$u});
+ $conn_database_info .= "<tr><td> </td><td>$u</td><td>" .
+ &comma_numbers($pgb_connection_info{database_user}{$d}{$u}) . "</td></tr>";
+ }
+ }
+ if ($graph) {
+ my @small = ();
+ foreach my $d (sort keys %{$pgb_connection_info{database}}) {
+ if ((($pgb_connection_info{database}{$d} * 100) / ($total_count||1)) > $pie_percentage_limit) {
+ $infos{$d} = $pgb_connection_info{database}{$d} || 0;
+ } else {
+ $infos{"Sum connections < $pie_percentage_limit%"} += $pgb_connection_info{database}{$d} || 0;
+ push(@small, $d);
+ }
+ }
+ if ($#small == 0) {
+ $infos{$small[0]} = $infos{"Sum connections < $pie_percentage_limit%"};
+ delete $infos{"Sum connections < $pie_percentage_limit%"};
+ }
+ }
+ $drawn_graphs{pgb_databaseconnections_graph} = &jqplot_piegraph($graphid++, 'graph_pgb_databaseconnections', 'Connections per database', %infos);
+ $total_count = &comma_numbers($total_count);
+ print $fh qq{
+ <div class="analysis-item row-fluid" id="pgbconnections-per-database">
+ <h2><i class="icon-list-alt"></i> Connections per database</h2>
+ <div class="span3">
+ <h3 class="">Key values</h3>
+ <div class="well key-figures">
+ <ul>
+ <li><span class="figure">$main_database[0]</span> <span class="figure-label">Main Database</span></li>
+ <li><span class="figure">$total_count connections</span> <span class="figure-label">Total</span></li>
+ </ul>
+ </div>
+ </div>
+ <div class="span8">
+ <div class="tabbable">
+ <ul class="nav nav-tabs">
+ <li class="active"><a href="#pgbconnections-per-database-graph" data-toggle="tab">Chart</a></li>
+ <li><a href="#pgbconnections-per-database-table" data-toggle="tab">Table</a></li>
+ </ul>
+ <div class="tab-content">
+ <div class="tab-pane active" id="pgbconnections-per-database-graph">
+ $drawn_graphs{pgb_databaseconnections_graph}
+ </div>
+ <div class="tab-pane" id="pgbconnections-per-database-table">
+ <table class="table table-striped table-hover">
+ <thead>
+ <tr>
+ <th>Database</th>
+ <th>User</th>
+ <th>Count</th>
+ </tr>
+ </thead>
+ <tbody>
+ $conn_database_info
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div><!-- end of connections per database -->
+};
+ delete $drawn_graphs{pgb_databaseconnections_graph};
+}
+
sub print_simultaneous_session
{
}
+sub print_simultaneous_pgb_session
+{
+
+ my $session_peak = 0;
+ my $session_peak_date = '';
+ foreach (sort {$pgb_overall_stat{'peak'}{$b}{session} <=> $pgb_overall_stat{'peak'}{$a}{session}} keys %{$pgb_overall_stat{'peak'}}) {
+ $session_peak = &comma_numbers($pgb_overall_stat{'peak'}{$_}{session});
+ $session_peak_date = $_;
+ last;
+ }
+
+ print $fh qq{
+ <div id="pgbsimultaneous-sessions" class="analysis-item row-fluid">
+ <h2 class=""><i class="icon-random"></i> Simultaneous sessions</h2>
+ <div class="span3">
+ <h3 class="">Key values</h3>
+ <div class="well key-figures">
+ <ul>
+ <li><span class="figure">$session_peak sessions</span> <span class="figure-label">Session Peak</span></li>
+ <li><span class="figure">$session_peak_date</span> <span class="figure-label">Date</span></li>
+ </ul>
+ </div>
+ </div>
+ <div class="span8">
+$drawn_graphs{pgb_sessionspersecond_graph}
+ </div>
+ </div><!-- end of Simultaneous sessions -->
+};
+ delete $drawn_graphs{pgb_sessionspersecond_graph};
+
+}
+
sub print_histogram_session_times
{
my %data = ();
if ($overall_stat{histogram}{session_time}{"-1"} > $most_range_value) {
$most_range = "> $histogram_session_time[-1]ms";
$most_range_value = $overall_stat{histogram}{session_time}{"-1"};
- }
-
$drawn_graphs{histogram_session_times_graph} = &jqplot_duration_histograph($graphid++, 'graph_histogram_session_times', 'Sessions', \@histogram_session_time, %data);
+ }
} else {
$histogram_info = qq{<tr><td colspan="3">$NODATA</td></tr>};
$drawn_graphs{histogram_session_times_graph} = qq{<tr><td colspan="3">$NODATA</td></tr>};
}
+
$most_range_value = &comma_numbers($most_range_value) if ($most_range_value);
print $fh qq{
- <h2><i class="icon-question-sign"></i> Sessions</h2>
<div class="analysis-item row-fluid" id="histogram-session-times">
<h2><i class="icon-signal"></i> Histogram of session times</h2>
<div class="span3">
delete $drawn_graphs{histogram_session_times_graph};
}
+sub print_histogram_pgb_session_times
+{
+ my %data = ();
+ my $histogram_info = '';
+ my $most_range = '';
+ my $most_range_value = '';
+
+ for (my $i = 1; $i <= $#histogram_session_time; $i++) {
+ $histogram_info .= "<tr><td>" . &convert_time($histogram_session_time[$i-1]) . '-' . &convert_time($histogram_session_time[$i]) . "</td><td>" . &comma_numbers($pgb_overall_stat{histogram}{session_time}{$histogram_session_time[$i-1]}) .
+ "</td><td>" . sprintf("%0.2f", ($pgb_overall_stat{histogram}{session_time}{$histogram_session_time[$i-1]} * 100) / ($pgb_overall_stat{histogram}{session_total}||1)) . "%</td></tr>";
+ $data{"$histogram_session_time[$i-1]-$histogram_session_time[$i]ms"} = ($pgb_overall_stat{histogram}{session_time}{$histogram_session_time[$i-1]} || 0);
+ if ($pgb_overall_stat{histogram}{session_time}{$histogram_session_time[$i-1]} > $most_range_value) {
+ $most_range = "$histogram_session_time[$i-1]-$histogram_session_time[$i]ms";
+ $most_range_value = $pgb_overall_stat{histogram}{session_time}{$histogram_session_time[$i-1]};
+ }
+ }
+ if ($pgb_overall_stat{histogram}{session_total} > 0) {
+ $histogram_info .= "<tr><td> > " . &convert_time($histogram_session_time[-1]) . "</td><td>" . &comma_numbers($pgb_overall_stat{histogram}{session_time}{'-1'}) .
+ "</td><td>" . sprintf("%0.2f", ($pgb_overall_stat{histogram}{session_time}{'-1'} * 100) / ($pgb_overall_stat{histogram}{session_total}||1)) . "%</td></tr>";
+ $data{"> $histogram_session_time[-1]ms"} = ($pgb_overall_stat{histogram}{session_time}{"-1"} || 0);
+ if ($pgb_overall_stat{histogram}{session_time}{"-1"} > $most_range_value) {
+ $most_range = "> $histogram_session_time[-1]ms";
+ $most_range_value = $pgb_overall_stat{histogram}{session_time}{"-1"};
+ }
+ $drawn_graphs{pgb_histogram_session_times_graph} = &jqplot_duration_histograph($graphid++, 'graph_pgb_histogram_session_times', 'Sessions', \@histogram_session_time, %data);
+ } else {
+ $histogram_info = qq{<tr><td colspan="3">$NODATA</td></tr>};
+ $drawn_graphs{pgb_histogram_session_times_graph} = qq{<tr><td colspan="3">$NODATA</td></tr>};
+ }
+
+ $most_range_value = &comma_numbers($most_range_value) if ($most_range_value);
+ print $fh qq{
+ <div class="analysis-item row-fluid" id="pgbhistogram-session-times">
+ <h2><i class="icon-signal"></i> Histogram of session times</h2>
+ <div class="span3">
+ <h3 class="">Key values</h3>
+ <div class="well key-figures">
+ <ul>
+ <li><span class="figure">$most_range_value</span> <span class="figure-label">$most_range duration</span></li>
+ </ul>
+ </div>
+ </div>
+ <div class="span8">
+ <div class="tabbable">
+ <ul class="nav nav-tabs">
+ <li class="active"><a href="#pgbhistogram-session-times-graph" data-toggle="tab">Chart</a></li>
+ <li><a href="#pgbhistogram-session-times-table" data-toggle="tab">Table</a></li>
+ </ul>
+ <div class="tab-content">
+ <div class="tab-pane active" id="pgbhistogram-session-times-graph">
+ $drawn_graphs{pgb_histogram_session_times_graph}
+ </div>
+ <div class="tab-pane" id="pgbhistogram-session-times-table">
+ <table class="table table-striped table-hover">
+ <thead>
+ <tr>
+ <th>Range</th>
+ <th>Count</th>
+ <th>Percentage</th>
+ </tr>
+ </thead>
+ <tbody>
+ $histogram_info
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div><!-- end of queries by type -->
+};
+ delete $drawn_graphs{pgb_histogram_session_times_graph};
+}
sub print_user_session
{
delete $drawn_graphs{usersessions_graph};
}
-sub print_host_session
+sub print_user_pgb_session
{
my %infos = ();
my $total_count = 0;
my $c = 0;
- my $sess_host_info = '';
- my @main_host = ('unknown',0);
- foreach my $h (sort keys %{$session_info{host}}) {
- $sess_host_info .= "<tr><td>$h</td><td>" . &comma_numbers($session_info{host}{$h}{count}) .
- "</td><td>" . &convert_time($session_info{host}{$h}{duration}) . "</td><td>" .
- &convert_time($session_info{host}{$h}{duration} / $session_info{host}{$h}{count}) .
+ my $sess_user_info = '';
+ my @main_user = ('unknown',0);
+ foreach my $u (sort keys %{$pgb_session_info{user}}) {
+ $sess_user_info .= "<tr><td>$u</td><td>" . &comma_numbers($pgb_session_info{user}{$u}{count}) .
+ "</td><td>" . &convert_time($pgb_session_info{user}{$u}{duration}), "</td><td>" .
+ &convert_time($pgb_session_info{user}{$u}{duration} / $pgb_session_info{user}{$u}{count}) .
"</td></tr>";
- $total_count += $session_info{host}{$h}{count};
- if ($main_host[1] < $session_info{host}{$h}{count}) {
- $main_host[0] = $h;
- $main_host[1] = $session_info{host}{$h}{count};
+ $total_count += $pgb_session_info{user}{$u}{count};
+ if ($main_user[1] < $pgb_session_info{user}{$u}{count}) {
+ $main_user[0] = $u;
+ $main_user[1] = $pgb_session_info{user}{$u}{count};
}
}
if ($graph) {
my @small = ();
- foreach my $d (sort keys %{$session_info{host}}) {
- if ((($session_info{host}{$d}{count} * 100) / ($total_count||1)) > $pie_percentage_limit) {
- $infos{$d} = $session_info{host}{$d}{count} || 0;
+ foreach my $d (sort keys %{$pgb_session_info{user}}) {
+ if ((($pgb_session_info{user}{$d}{count} * 100) / ($total_count||1)) > $pie_percentage_limit) {
+ $infos{$d} = $pgb_session_info{user}{$d}{count} || 0;
} else {
- $infos{"Sum sessions < $pie_percentage_limit%"} += $session_info{host}{$d}{count} || 0;
+ $infos{"Sum sessions < $pie_percentage_limit%"} += $pgb_session_info{user}{$d}{count} || 0;
push(@small, $d);
}
}
delete $infos{"Sum sessions < $pie_percentage_limit%"};
}
}
- $drawn_graphs{hostsessions_graph} = &jqplot_piegraph($graphid++, 'graph_hostsessions', 'Sessions per host', %infos);
- $sess_host_info = qq{<tr><td colspan="4">$NODATA</td></tr>} if (!$total_count);
+ $drawn_graphs{pgb_usersessions_graph} = &jqplot_piegraph($graphid++, 'graph_pgb_usersessions', 'Sessions per user', %infos);
+ $sess_user_info = qq{<tr><td colspan="5">$NODATA</td></tr>} if (!$total_count);
$total_count = &comma_numbers($total_count);
print $fh qq{
- <div class="analysis-item row-fluid" id="sessions-per-host">
- <h2><i class="icon-sitemap"></i> Sessions per host</h2>
+ <div class="analysis-item row-fluid" id="pgbsessions-per-user">
+ <h2><i class="icon-user"></i> Sessions per user</h2>
<div class="span3">
<h3 class="">Key values</h3>
<div class="well key-figures">
<ul>
- <li><span class="figure">$main_host[0]</span> <span class="figure-label">Main Host</span></li>
+ <li><span class="figure">$main_user[0]</span> <span class="figure-label">Main User</span></li>
<li><span class="figure">$total_count sessions</span> <span class="figure-label">Total</span></li>
</ul>
</div>
<div class="span8">
<div class="tabbable">
<ul class="nav nav-tabs">
- <li class="active"><a href="#sessions-per-host-graph" data-toggle="tab">Chart</a></li>
- <li><a href="#sessions-per-host-table" data-toggle="tab">Table</a></li>
+ <li class="active"><a href="#pgbsessions-per-user-graph" data-toggle="tab">Chart</a></li>
+ <li><a href="#pgbsessions-per-user-table" data-toggle="tab">Table</a></li>
</ul>
<div class="tab-content">
- <div class="tab-pane active" id="sessions-per-host-graph">
- $drawn_graphs{hostsessions_graph}
+ <div class="tab-pane active" id="pgbsessions-per-user-graph">
+ $drawn_graphs{pgb_usersessions_graph}
</div>
- <div class="tab-pane" id="sessions-per-host-table">
+ <div class="tab-pane" id="pgbsessions-per-user-table">
<table class="table table-striped table-hover">
<thead>
<tr>
- <th>Host</th>
+ <th>User</th>
<th>Count</th>
<th>Total Duration</th>
<th>Average Duration</th>
</tr>
</thead>
<tbody>
- $sess_host_info
+ $sess_user_info
</tbody>
</table>
</div>
</div>
</div>
</div>
- </div><!-- end of sessions per host -->
+ </div><!-- end of sessions per user -->
};
-
- delete $drawn_graphs{hostsessions_graph};
+ delete $drawn_graphs{pgb_usersessions_graph};
}
-sub print_app_session
+sub print_host_session
{
my %infos = ();
my $total_count = 0;
my $c = 0;
- my $sess_app_info = '';
- my @main_app = ('unknown',0);
- foreach my $h (sort keys %{$session_info{app}}) {
- $sess_app_info .= "<tr><td>$h</td><td>" . &comma_numbers($session_info{app}{$h}{count}) .
- "</td><td>" . &convert_time($session_info{app}{$h}{duration}) . "</td><td>" .
- &convert_time($session_info{app}{$h}{duration} / $session_info{app}{$h}{count}) .
+ my $sess_host_info = '';
+ my @main_host = ('unknown',0);
+ foreach my $h (sort keys %{$session_info{host}}) {
+ $sess_host_info .= "<tr><td>$h</td><td>" . &comma_numbers($session_info{host}{$h}{count}) .
+ "</td><td>" . &convert_time($session_info{host}{$h}{duration}) . "</td><td>" .
+ &convert_time($session_info{host}{$h}{duration} / $session_info{host}{$h}{count}) .
+ "</td></tr>";
+ $total_count += $session_info{host}{$h}{count};
+ if ($main_host[1] < $session_info{host}{$h}{count}) {
+ $main_host[0] = $h;
+ $main_host[1] = $session_info{host}{$h}{count};
+ }
+ }
+ if ($graph) {
+ my @small = ();
+ foreach my $d (sort keys %{$session_info{host}}) {
+ if ((($session_info{host}{$d}{count} * 100) / ($total_count||1)) > $pie_percentage_limit) {
+ $infos{$d} = $session_info{host}{$d}{count} || 0;
+ } else {
+ $infos{"Sum sessions < $pie_percentage_limit%"} += $session_info{host}{$d}{count} || 0;
+ push(@small, $d);
+ }
+ }
+ if ($#small == 0) {
+ $infos{$small[0]} = $infos{"Sum sessions < $pie_percentage_limit%"};
+ delete $infos{"Sum sessions < $pie_percentage_limit%"};
+ }
+ }
+ $drawn_graphs{hostsessions_graph} = &jqplot_piegraph($graphid++, 'graph_hostsessions', 'Connections per host', %infos);
+ $sess_host_info = qq{<tr><td colspan="4">$NODATA</td></tr>} if (!$total_count);
+ $total_count = &comma_numbers($total_count);
+ print $fh qq{
+ <div class="analysis-item row-fluid" id="sessions-per-host">
+ <h2><i class="icon-sitemap"></i> Sessions per host</h2>
+ <div class="span3">
+ <h3 class="">Key values</h3>
+ <div class="well key-figures">
+ <ul>
+ <li><span class="figure">$main_host[0]</span> <span class="figure-label">Main Host</span></li>
+ <li><span class="figure">$total_count sessions</span> <span class="figure-label">Total</span></li>
+ </ul>
+ </div>
+ </div>
+ <div class="span8">
+ <div class="tabbable">
+ <ul class="nav nav-tabs">
+ <li class="active"><a href="#sessions-per-host-graph" data-toggle="tab">Chart</a></li>
+ <li><a href="#sessions-per-host-table" data-toggle="tab">Table</a></li>
+ </ul>
+ <div class="tab-content">
+ <div class="tab-pane active" id="sessions-per-host-graph">
+ $drawn_graphs{hostsessions_graph}
+ </div>
+ <div class="tab-pane" id="sessions-per-host-table">
+ <table class="table table-striped table-hover">
+ <thead>
+ <tr>
+ <th>Host</th>
+ <th>Count</th>
+ <th>Total Duration</th>
+ <th>Average Duration</th>
+ </tr>
+ </thead>
+ <tbody>
+ $sess_host_info
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div><!-- end of sessions per host -->
+};
+
+ delete $drawn_graphs{hostsessions_graph};
+}
+
+sub print_host_pgb_session
+{
+ my %infos = ();
+ my $total_count = 0;
+ my $c = 0;
+ my $sess_host_info = '';
+ my @main_host = ('unknown',0);
+ foreach my $h (sort keys %{$pgb_session_info{host}}) {
+ $sess_host_info .= "<tr><td>$h</td><td>" . &comma_numbers($pgb_session_info{host}{$h}{count}) .
+ "</td><td>" . &convert_time($pgb_session_info{host}{$h}{duration}) . "</td><td>" .
+ &convert_time($pgb_session_info{host}{$h}{duration} / $pgb_session_info{host}{$h}{count}) .
+ "</td></tr>";
+ $total_count += $pgb_session_info{host}{$h}{count};
+ if ($main_host[1] < $pgb_session_info{host}{$h}{count}) {
+ $main_host[0] = $h;
+ $main_host[1] = $pgb_session_info{host}{$h}{count};
+ }
+ }
+ if ($graph) {
+ my @small = ();
+ foreach my $d (sort keys %{$pgb_session_info{host}}) {
+ if ((($pgb_session_info{host}{$d}{count} * 100) / ($total_count||1)) > $pie_percentage_limit) {
+ $infos{$d} = $pgb_session_info{host}{$d}{count} || 0;
+ } else {
+ $infos{"Sum sessions < $pie_percentage_limit%"} += $pgb_session_info{host}{$d}{count} || 0;
+ push(@small, $d);
+ }
+ }
+ if ($#small == 0) {
+ $infos{$small[0]} = $infos{"Sum sessions < $pie_percentage_limit%"};
+ delete $infos{"Sum sessions < $pie_percentage_limit%"};
+ }
+ }
+ $drawn_graphs{pgb_hostsessions_graph} = &jqplot_piegraph($graphid++, 'graph_pgb_hostsessions', 'Sessions per host', %infos);
+ $sess_host_info = qq{<tr><td colspan="4">$NODATA</td></tr>} if (!$total_count);
+ $total_count = &comma_numbers($total_count);
+ print $fh qq{
+ <div class="analysis-item row-fluid" id="pgbsessions-per-host">
+ <h2><i class="icon-sitemap"></i> Sessions per host</h2>
+ <div class="span3">
+ <h3 class="">Key values</h3>
+ <div class="well key-figures">
+ <ul>
+ <li><span class="figure">$main_host[0]</span> <span class="figure-label">Main Host</span></li>
+ <li><span class="figure">$total_count sessions</span> <span class="figure-label">Total</span></li>
+ </ul>
+ </div>
+ </div>
+ <div class="span8">
+ <div class="tabbable">
+ <ul class="nav nav-tabs">
+ <li class="active"><a href="#pgbsessions-per-host-graph" data-toggle="tab">Chart</a></li>
+ <li><a href="#pgbsessions-per-host-table" data-toggle="tab">Table</a></li>
+ </ul>
+ <div class="tab-content">
+ <div class="tab-pane active" id="pgbsessions-per-host-graph">
+ $drawn_graphs{pgb_hostsessions_graph}
+ </div>
+ <div class="tab-pane" id="pgbsessions-per-host-table">
+ <table class="table table-striped table-hover">
+ <thead>
+ <tr>
+ <th>Host</th>
+ <th>Count</th>
+ <th>Total Duration</th>
+ <th>Average Duration</th>
+ </tr>
+ </thead>
+ <tbody>
+ $sess_host_info
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div><!-- end of sessions per host -->
+};
+
+ delete $drawn_graphs{pgb_hostsessions_graph};
+}
+
+sub print_app_session
+{
+ my %infos = ();
+ my $total_count = 0;
+ my $c = 0;
+ my $sess_app_info = '';
+ my @main_app = ('unknown',0);
+ foreach my $h (sort keys %{$session_info{app}}) {
+ $sess_app_info .= "<tr><td>$h</td><td>" . &comma_numbers($session_info{app}{$h}{count}) .
+ "</td><td>" . &convert_time($session_info{app}{$h}{duration}) . "</td><td>" .
+ &convert_time($session_info{app}{$h}{duration} / $session_info{app}{$h}{count}) .
"</td></tr>";
$total_count += $session_info{app}{$h}{count};
if ($main_app[1] < $session_info{app}{$h}{count}) {
delete $drawn_graphs{appsessions_graph};
}
-
sub print_database_session
{
my %infos = ();
delete $drawn_graphs{databasesessions_graph};
}
+sub print_database_pgb_session
+{
+ my %infos = ();
+ my $total_count = 0;
+ my $sess_database_info = '';
+ my @main_database = ('unknown',0);
+ foreach my $d (sort keys %{$pgb_session_info{database}}) {
+ $sess_database_info .= "<tr><td>$d</td><td>" . &comma_numbers($pgb_session_info{database}{$d}{count}) .
+ "</td><td>" . &convert_time($pgb_session_info{database}{$d}{duration}) . "</td><td>" .
+ &convert_time($pgb_session_info{database}{$d}{duration} / $pgb_session_info{database}{$d}{count}) .
+ "</td></tr>";
+ $total_count += $pgb_session_info{database}{$d}{count};
+ if ($main_database[1] < $pgb_session_info{database}{$d}{count}) {
+ $main_database[0] = $d;
+ $main_database[1] = $pgb_session_info{database}{$d}{count};
+ }
+ }
+ if ($graph) {
+ my @small = ();
+ foreach my $d (sort keys %{$pgb_session_info{database}}) {
+ if ((($pgb_session_info{database}{$d}{count} * 100) / ($total_count||1)) > $pie_percentage_limit) {
+ $infos{$d} = $pgb_session_info{database}{$d}{count} || 0;
+ } else {
+ $infos{"Sum sessions < $pie_percentage_limit%"} += $pgb_session_info{database}{$d}{count} || 0;
+ push(@small, $d);
+ }
+ }
+ if ($#small == 0) {
+ $infos{$small[0]} = $infos{"Sum sessions < $pie_percentage_limit%"};
+ delete $infos{"Sum sessions < $pie_percentage_limit%"};
+ }
+ }
+ $drawn_graphs{pgb_databasesessions_graph} = &jqplot_piegraph($graphid++, 'graph_pgb_databasesessions', 'Sessions per database', %infos);
+ $sess_database_info = qq{<tr><td colspan="5">$NODATA</td></tr>} if (!$total_count);
+
+ $total_count = &comma_numbers($total_count);
+ print $fh qq{
+ <div class="analysis-item row-fluid" id="pgbsessions-per-database">
+ <h2><i class="icon-list-alt"></i> Sessions per database</h2>
+ <div class="span3">
+ <h3 class="">Key values</h3>
+ <div class="well key-figures">
+ <ul>
+ <li><span class="figure">$main_database[0]</span> <span class="figure-label">Main Database</span></li>
+ <li><span class="figure">$total_count sessions</span> <span class="figure-label">Total</span></li>
+ </ul>
+ </div>
+ </div>
+ <div class="span8">
+ <div class="tabbable">
+ <ul class="nav nav-tabs">
+ <li class="active"><a href="#pgbsessions-per-database-graph" data-toggle="tab">Chart</a></li>
+ <li><a href="#pgbsessions-per-database-table" data-toggle="tab">Table</a></li>
+ </ul>
+ <div class="tab-content">
+ <div class="tab-pane active" id="pgbsessions-per-database-graph">
+ $drawn_graphs{pgb_databasesessions_graph}
+ </div>
+ <div class="tab-pane" id="pgbsessions-per-database-table">
+ <table class="table table-striped table-hover">
+ <thead>
+ <tr>
+ <th>Database</th>
+ <th>User</th>
+ <th>Count</th>
+ <th>Total Duration</th>
+ <th>Average Duration</th>
+ </tr>
+ </thead>
+ <tbody>
+ $sess_database_info
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div><!-- end of sessions per database -->
+};
+ delete $drawn_graphs{pgb_databasesessions_graph};
+}
+
+
sub print_checkpoint
{
if (!$error_only) {
- # Overall statistics
- print $fh qq{
+ if (!$pgbouncer_only) {
+ # Overall statistics
+ print $fh qq{
<li class="slide active-slide" id="overview-slide">
};
- &print_overall_statistics();
+ &print_overall_statistics();
+ }
# Set graphs limits
$overall_stat{'first_log_ts'} =~ /^(\d+)-(\d+)-(\d+) (\d+):(\d+):(\d+)/;
$t_min = timegm_nocheck(0, $5, $4, $3, $2 - 1, $1) * 1000;
$t_max += ($timezone*1000);
$t_max += ($avg_minutes * 60000);
- if (!$disable_hourly) {
+ if (!$disable_hourly && !$pgbouncer_only) {
+
# Build graphs based on hourly stat
&compute_query_graphs();
}
- if (!$disable_connection) {
+ if (!$disable_connection && !$pgbouncer_only) {
print $fh qq{
</li>
<li class="slide" id="connections-slide">
# Draw connections information
&print_established_connection() if (!$disable_hourly);
-
# Show per database/user connections
&print_database_connection();
-
# Show per user connections
&print_user_connection();
-
# Show per client ip connections
&print_host_connection();
}
# Show session per database statistics
- if (!$disable_session) {
+ if (!$disable_session && !$pgbouncer_only) {
print $fh qq{
</li>
<li class="slide" id="sessions-slide">
# Display checkpoint and temporary files report
- if (!$disable_checkpoint) {
+ if (!$disable_checkpoint && !$pgbouncer_only) {
print $fh qq{
</li>
<li class="slide" id="checkpoints-slide">
&print_checkpoint();
}
- if (!$disable_temporary) {
+ if (!$disable_temporary && !$pgbouncer_only) {
print $fh qq{
</li>
<li class="slide" id="tempfiles-slide">
&print_tempfile_report();
}
- if (!$disable_autovacuum) {
+ if (!$disable_autovacuum && !$pgbouncer_only) {
print $fh qq{
</li>
<li class="slide" id="vacuums-slide">
}
- if (!$disable_lock) {
+ if (!$disable_lock && !$pgbouncer_only) {
print $fh qq{
</li>
<li class="slide" id="locks-slide">
&print_lock_queries_report();
}
- if (!$disable_query) {
+ if (!$disable_query && !$pgbouncer_only) {
print $fh qq{
</li>
<li class="slide" id="queries-slide">
&print_slowest_queries
}
+ # Show pgbouncer sessions and connections statistics
+ if (exists $pgb_overall_stat{peak}) {
+
+ # Build pgbouncer graph based on hourly stats
+ &compute_pgbouncer_graphs();
+
+ my $active = '';
+ $active = ' active-slide' if ($pgbouncer_only);
+ print $fh qq{
+ </li>
+ <li class="slide$active" id="pgbouncer-slide">
+ <h1 class="page-header"><i class="icon-refresh"></i> pgBouncer</h1>
+
+};
+ # Draw pgbouncer own statistics
+ &print_pgbouncer_stats() if (!$disable_hourly);
+
+ # Draw connections information
+ &print_established_pgb_connection() if (!$disable_hourly);
+
+ # Show per database/user connections
+ &print_database_pgb_connection();
+
+ # Show per user connections
+ &print_user_pgb_connection();
+
+ # Show per client ip connections
+ &print_host_pgb_connection();
+
+ # Show number of simultaneous sessions
+ &print_simultaneous_pgb_session();
+ # Show histogram for session times
+ &print_histogram_pgb_session_times();
+ # Show per database sessions
+ &print_database_pgb_session();
+ # Show per user sessions
+ &print_user_pgb_session();
+ # Show per host sessions
+ &print_host_pgb_session();
+
+ # Show Most Frequent Errors/Events
+ &show_pgb_error_as_html();
+ }
}
# Show errors report
if (!$total_logs) {
$logtype_info = qq{<tr><td colspan="7">$NODATA</td></tr>};
}
- $logs_type{ERROR} ||= 0;
- $logs_type{FATAL} ||= 0;
- $total_logs = &comma_numbers($total_logs);
- print $fh qq{
- <h1 class="page-header"><i class="icon-bullhorn"></i> Events</h1>
-
- <div class="analysis-item row-fluid" id="log-levels">
- <h2><i class="icon-tags"></i> Log levels</h2>
- <div class="span3">
- <h3 class="">Key values</h3>
- <div class="well key-figures">
- <ul>
- <li><span class="figure">$total_logs</span> <span class="figure-label">Log entries</span></li>
- </ul>
- </div>
- </div>
- <div class="span8">
- <div class="tabbable">
- <ul class="nav nav-tabs">
- <li class="active"><a href="#log-level-graph" data-toggle="tab">Chart</a></li>
- <li><a href="#log-level-table" data-toggle="tab">Table</a></li>
- </ul>
- <div class="tab-content">
- <div class="tab-pane active" id="log-level-graph">
- $drawn_graphs{logstype_graph}
- </div>
- <div class="tab-pane" id="log-level-table">
- <table class="table table-striped table-hover">
- <thead>
- <tr>
- <th>Type</th>
- <th>Count</th>
- <th>Percentage</th>
- </tr>
- </thead>
- <tbody>
- $logtype_info
- </tbody>
- </table>
- </div>
- </div>
- </div>
- </div>
- </div><!-- end of event flow -->
+ $logs_type{ERROR} ||= 0;
+ $logs_type{FATAL} ||= 0;
+ $total_logs = &comma_numbers($total_logs);
+ print $fh qq{
+ <h1 class="page-header"><i class="icon-bullhorn"></i> Events</h1>
+
+ <div class="analysis-item row-fluid" id="log-levels">
+ <h2><i class="icon-tags"></i> Log levels</h2>
+ <div class="span3">
+ <h3 class="">Key values</h3>
+ <div class="well key-figures">
+ <ul>
+ <li><span class="figure">$total_logs</span> <span class="figure-label">Log entries</span></li>
+ </ul>
+ </div>
+ </div>
+ <div class="span8">
+ <div class="tabbable">
+ <ul class="nav nav-tabs">
+ <li class="active"><a href="#log-level-graph" data-toggle="tab">Chart</a></li>
+ <li><a href="#log-level-table" data-toggle="tab">Table</a></li>
+ </ul>
+ <div class="tab-content">
+ <div class="tab-pane active" id="log-level-graph">
+ $drawn_graphs{logstype_graph}
+ </div>
+ <div class="tab-pane" id="log-level-table">
+ <table class="table table-striped table-hover">
+ <thead>
+ <tr>
+ <th>Type</th>
+ <th>Count</th>
+ <th>Percentage</th>
+ </tr>
+ </thead>
+ <tbody>
+ $logtype_info
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div><!-- end of event flow -->
+
+ <div class="analysis-item row-fluid" id="minutes-errors-levels">
+ <h2><i class="icon-tags"></i> Events distribution</h2>
+ <div class="span3">
+ <h3 class="">Key values</h3>
+ <div class="well key-figures">
+ <ul>
+ <li><span class="figure">$max_events{PANIC}</span> <span class="figure-label">PANIC entries</span></li>
+ <li><span class="figure">$max_events{FATAL}</span> <span class="figure-label">FATAL entries</span></li>
+ <li><span class="figure">$max_events{ERROR}</span> <span class="figure-label">ERROR entries</span></li>
+ <li><span class="figure">$max_events{WARNING}</span> <span class="figure-label">WARNING entries</span></li>
+ </ul>
+ </div>
+ </div>
+ <div class="span8">
+$drawn_graphs{'eventspersecond_graph'}
+ </div>
+ </div><!-- end of errors per minutes -->
+
+};
+ delete $drawn_graphs{logstype_graph};
+ delete $drawn_graphs{'eventspersecond_graph'};
+
+}
+
+sub show_error_as_html
+{
+
+ my $main_error = 0;
+ my $total = 0;
+ foreach my $k (sort {$error_info{$b}{count} <=> $error_info{$a}{count}} keys %error_info) {
+ next if (!$error_info{$k}{count});
+ $main_error = &comma_numbers($error_info{$k}{count}) if (!$main_error);
+ $total += $error_info{$k}{count};
+ }
+ $total = &comma_numbers($total);
+
+ print $fh qq{
+ <div class="analysis-item row-fluid" id="most-frequent-errors-events">
+ <h2><i class="icon-warning-sign"></i> Most Frequent Errors/Events</h2>
+ <div class="span3">
+ <h3 class="">Key values</h3>
+ <div class="well key-figures">
+ <ul>
+ <li><span class="figure">$main_error</span> <span class="figure-label">Max number of times the same event was reported</span></li>
+ <li><span class="figure">$total</span> <span class="figure-label">Total events found</span></li>
+ </ul>
+ </div>
+ </div>
+ <div class="span8">
+ <table class="table table-striped" id="most-frequent-errors-events-table">
+ <thead>
+ <tr>
+ <th>Rank</th>
+ <th>Times reported</th>
+ <th>Error</th>
+ </tr>
+ </thead>
+ <tbody>
+};
+ my $rank = 1;
+ foreach my $k (sort {$error_info{$b}{count} <=> $error_info{$a}{count}} keys %error_info) {
+ next if (!$error_info{$k}{count});
+ my $count = &comma_numbers($error_info{$k}{count});
+ my $msg = $k;
+ $msg =~ s/ERROR: (parameter "[^"]+" changed to)/LOG: $1/;
+ $msg =~ s/ERROR: (database system was shut down)/LOG: $1/;
+ $msg =~ s/ERROR: (database system was interrupted while in recovery)/LOG: $1/;
+ $msg =~ s/ERROR: (recovery has paused)/LOG: $1/;
+ $msg =~ s/ERROR: (sending cancel to blocking autovacuum)/LOG: $1/;
+ $msg =~ s/ERROR: (skipping analyze of)/LOG: $1/;
+ my $error_level_class = 'text-error';
+ if ($msg =~ /^WARNING: /) {
+ $error_level_class = 'text-warning';
+ } elsif ($msg =~ /^LOG: /) {
+ $error_level_class = 'text-success';
+ } elsif ($msg =~ /^HINT: /) {
+ $error_level_class = 'text-info';
+ } elsif ($msg =~ /^FATAL: /) {
+ $error_level_class = 'text-fatal';
+ } elsif ($msg =~ /^PANIC: /) {
+ $error_level_class = 'text-panic';
+ }
+ my $details = '';
+ my %hourly_count = ();
+ my $days = 0;
+ foreach my $d (sort keys %{$error_info{$k}{chronos}}) {
+ my $c = 1;
+ $d =~ /^\d{4}(\d{2})(\d{2})$/;
+ $days++;
+ my $zday = "$abbr_month{$1} $2";
+ foreach my $h (sort keys %{$error_info{$k}{chronos}{$d}}) {
+ $details .= "<tr><td>$zday</td><td>$h</td><td>" .
+ &comma_numbers($error_info{$k}{chronos}{$d}{$h}{count}) . "</td></tr>";
+ $zday = "";
+ foreach my $m (sort keys %{$error_info{$k}{chronos}{$d}{$h}{min}}) {
+ my $rd = &average_per_minutes($m, $histo_avg_minutes);
+ $hourly_count{"$h:$rd"} += $error_info{$k}{chronos}{$d}{$h}{min}{$m};
+ }
+ if ($#histo_avgs > 0) {
+ foreach my $rd (@histo_avgs) {
+ next if (!exists $hourly_count{"$h:$rd"});
+ $details .= "<tr><td>$zday</td><td style=\"text-align: right\">$h:$rd</td><td>" .
+ &comma_numbers($hourly_count{"$h:$rd"}) . "</td></tr>";
+ }
+ }
+ }
+ }
+ # Set graph dataset
+ my %graph_data = ();
+ foreach my $h ("00" .. "23") {
+ foreach my $rd (@histo_avgs) {
+ $graph_data{count} .= "['$h:$rd'," . (int($hourly_count{"$h:$rd"}/$days) || 0) . "],";
+ }
+ }
+ $graph_data{count} =~ s/,$//;
+ %hourly_count = ();
+
+ my $error_histo =
+ &jqplot_histograph($graphid++, 'error_graph_'.$rank, $graph_data{count}, '', 'Avg. events', '');
+
+ # Escape HTML code in error message
+ $msg = &escape_html($msg);
+ print $fh qq{
+ <tr>
+ <td>$rank</td>
+ <td>$count
+ <p><a href="#most-frequent-errors-events-details-rank-$rank" class="btn btn-mini" data-toggle="collapse">Details</a></p>
+ </td>
+ <td id="most-frequent-errors-events-examples-details-rank-$rank">
+ <pre><span class="$error_level_class">$msg</span></pre>
+ <!-- Details collapse -->
+ <div id="most-frequent-errors-events-details-rank-$rank" class="collapse">
+ <h3>Times Reported <small>Most Frequent Error / Event #$rank</small></h3>
+ $error_histo
+ <table class="table table-stripped table-condensed">
+ <thead>
+ <tr>
+ <th>Day</th>
+ <th>Hour</th>
+ <th>Count</th>
+ </tr>
+ </thead>
+ <tbody>
+ $details
+ </tbody>
+ </table>
+ <p class="pull-right"><button type="button" class="btn btn-mini" data-toggle="collapse" data-target="#most-frequent-errors-events-details-rank-$rank">x Hide</button></p>
+ </div><!-- end of details collapse -->
+};
+ print $fh qq{
+<p><button type="button" class="btn btn-mini" data-toggle="collapse" data-target="#most-frequent-errors-events-examples-rank-$rank">Examples</button></p>
+} if (($sample > 0) && ($#{$error_info{$k}{date}} >= 0));
+ print $fh qq{
+ <!-- Examples collapse -->
+ <div id="most-frequent-errors-events-examples-rank-$rank" class="collapse">
+ <dl>
+};
+
+ for (my $i = 0 ; $i <= $#{$error_info{$k}{date}} ; $i++) {
+ last if (($sample > 0) && ($i == $sample));
+ # Escape HTML code in error message
+ my $message = &escape_html($error_info{$k}{error}[$i]);
+ my $details = "Date: " . $error_info{$k}{date}[$i] . "\n";
+ if ($error_info{$k}{detail}[$i]) {
+ $details .= "Detail: " . &escape_html($error_info{$k}{detail}[$i]) . "<br/>";
+ }
+ if ($error_info{$k}{context}[$i]) {
+ $details .= "Context: " . &escape_html($error_info{$k}{context}[$i]) . "<br/>";
+ }
+ if ($error_info{$k}{hint}[$i]) {
+ $details .= "Hint: " . &escape_html($error_info{$k}{hint}[$i]) . "<br/>";
+ }
+ if ($error_info{$k}{statement}[$i]) {
+ $details .= "Statement: " . &escape_html($error_info{$k}{statement}[$i]) . "<br/>";
+ }
+ if ($error_info{$k}{db}[$i]) {
+ $details .= "<b>Database:</b> $error_info{$k}{db}[$i] <b>User:</b> $error_info{$k}{user}[$i] <b>Remote:</b> $error_info{$k}{remote}[$i] <br/>";
+ }
+ print $fh qq{
+ <dt><span class="$error_level_class">$message</span></dt>
+ <pre>$details</pre>
+};
+ }
+ print $fh qq{
+ </dl>
+ <p class="pull-right"><button type="button" class="btn btn-mini" data-toggle="collapse" data-target="#most-frequent-errors-events-examples-rank-$rank">x Hide</button></p>
+ </div>
+ <!-- end of details collapse -->
+ </td>
+ </tr>
+};
+ $rank++;
+ }
+ if (scalar keys %error_info == 0) {
+ print $fh qq{<tr><td colspan="7">$NODATA</td></tr>};
+ }
- <div class="analysis-item row-fluid" id="minutes-errors-levels">
- <h2><i class="icon-tags"></i> Events distribution</h2>
- <div class="span3">
- <h3 class="">Key values</h3>
- <div class="well key-figures">
- <ul>
- <li><span class="figure">$max_events{PANIC}</span> <span class="figure-label">PANIC entries</span></li>
- <li><span class="figure">$max_events{FATAL}</span> <span class="figure-label">FATAL entries</span></li>
- <li><span class="figure">$max_events{ERROR}</span> <span class="figure-label">ERROR entries</span></li>
- <li><span class="figure">$max_events{WARNING}</span> <span class="figure-label">WARNING entries</span></li>
- </ul>
- </div>
- </div>
- <div class="span8">
-$drawn_graphs{'eventspersecond_graph'}
+ print $fh qq{
+ </tbody>
+ </table>
</div>
- </div><!-- end of errors per minutes -->
-
+ </div><!-- end of most-frequent-errors-events -->
};
- delete $drawn_graphs{logstype_graph};
- delete $drawn_graphs{'eventspersecond_graph'};
}
-sub show_error_as_html
+sub show_pgb_error_as_html
{
my $main_error = 0;
my $total = 0;
- foreach my $k (sort {$error_info{$b}{count} <=> $error_info{$a}{count}} keys %error_info) {
- next if (!$error_info{$k}{count});
- $main_error = &comma_numbers($error_info{$k}{count}) if (!$main_error);
- $total += $error_info{$k}{count};
+ foreach my $k (sort {$pgb_error_info{$b}{count} <=> $pgb_error_info{$a}{count}} keys %pgb_error_info) {
+ next if (!$pgb_error_info{$k}{count});
+ $main_error = &comma_numbers($pgb_error_info{$k}{count}) if (!$main_error);
+ $total += $pgb_error_info{$k}{count};
}
$total = &comma_numbers($total);
print $fh qq{
- <div class="analysis-item row-fluid" id="most-frequent-errors-events">
+ <div class="analysis-item row-fluid" id="pgbmost-frequent-errors-events">
<h2><i class="icon-warning-sign"></i> Most Frequent Errors/Events</h2>
<div class="span3">
<h3 class="">Key values</h3>
</div>
</div>
<div class="span8">
- <table class="table table-striped" id="most-frequent-errors-events-table">
+ <table class="table table-striped" id="pgbmost-frequent-errors-events-table">
<thead>
<tr>
<th>Rank</th>
<tbody>
};
my $rank = 1;
- foreach my $k (sort {$error_info{$b}{count} <=> $error_info{$a}{count}} keys %error_info) {
- next if (!$error_info{$k}{count});
- my $count = &comma_numbers($error_info{$k}{count});
+ foreach my $k (sort {$pgb_error_info{$b}{count} <=> $pgb_error_info{$a}{count}} keys %pgb_error_info) {
+ next if (!$pgb_error_info{$k}{count});
+ my $count = &comma_numbers($pgb_error_info{$k}{count});
my $msg = $k;
- $msg =~ s/ERROR: (parameter "[^"]+" changed to)/LOG: $1/;
- $msg =~ s/ERROR: (database system was shut down)/LOG: $1/;
- $msg =~ s/ERROR: (database system was interrupted while in recovery)/LOG: $1/;
- $msg =~ s/ERROR: (recovery has paused)/LOG: $1/;
- $msg =~ s/ERROR: (sending cancel to blocking autovacuum)/LOG: $1/;
- $msg =~ s/ERROR: (skipping analyze of)/LOG: $1/;
my $error_level_class = 'text-error';
if ($msg =~ /^WARNING: /) {
$error_level_class = 'text-warning';
} elsif ($msg =~ /^LOG: /) {
$error_level_class = 'text-success';
- } elsif ($msg =~ /^HINT: /) {
- $error_level_class = 'text-info';
} elsif ($msg =~ /^FATAL: /) {
$error_level_class = 'text-fatal';
} elsif ($msg =~ /^PANIC: /) {
my $details = '';
my %hourly_count = ();
my $days = 0;
- foreach my $d (sort keys %{$error_info{$k}{chronos}}) {
+ foreach my $d (sort keys %{$pgb_error_info{$k}{chronos}}) {
my $c = 1;
$d =~ /^\d{4}(\d{2})(\d{2})$/;
$days++;
my $zday = "$abbr_month{$1} $2";
- foreach my $h (sort keys %{$error_info{$k}{chronos}{$d}}) {
+ foreach my $h (sort keys %{$pgb_error_info{$k}{chronos}{$d}}) {
$details .= "<tr><td>$zday</td><td>$h</td><td>" .
- &comma_numbers($error_info{$k}{chronos}{$d}{$h}{count}) . "</td></tr>";
+ &comma_numbers($pgb_error_info{$k}{chronos}{$d}{$h}{count}) . "</td></tr>";
$zday = "";
- foreach my $m (sort keys %{$error_info{$k}{chronos}{$d}{$h}{min}}) {
+ foreach my $m (sort keys %{$pgb_error_info{$k}{chronos}{$d}{$h}{min}}) {
my $rd = &average_per_minutes($m, $histo_avg_minutes);
- $hourly_count{"$h:$rd"} += $error_info{$k}{chronos}{$d}{$h}{min}{$m};
+ $hourly_count{"$h:$rd"} += $pgb_error_info{$k}{chronos}{$d}{$h}{min}{$m};
}
if ($#histo_avgs > 0) {
foreach my $rd (@histo_avgs) {
%hourly_count = ();
my $error_histo =
- &jqplot_histograph($graphid++, 'error_graph_'.$rank, $graph_data{count}, '', 'Avg. events', '');
+ &jqplot_histograph($graphid++, 'pgberror_graph_'.$rank, $graph_data{count}, '', 'Avg. events', '');
# Escape HTML code in error message
$msg = &escape_html($msg);
<tr>
<td>$rank</td>
<td>$count
- <p><a href="#most-frequent-errors-events-details-rank-$rank" class="btn btn-mini" data-toggle="collapse">Details</a></p>
+ <p><a href="#pgbmost-frequent-errors-events-details-rank-$rank" class="btn btn-mini" data-toggle="collapse">Details</a></p>
</td>
- <td id="most-frequent-errors-events-examples-details-rank-$rank">
+ <td id="pgbmost-frequent-errors-events-examples-details-rank-$rank">
<pre><span class="$error_level_class">$msg</span></pre>
<!-- Details collapse -->
- <div id="most-frequent-errors-events-details-rank-$rank" class="collapse">
+ <div id="pgbmost-frequent-errors-events-details-rank-$rank" class="collapse">
<h3>Times Reported <small>Most Frequent Error / Event #$rank</small></h3>
$error_histo
<table class="table table-stripped table-condensed">
$details
</tbody>
</table>
- <p class="pull-right"><button type="button" class="btn btn-mini" data-toggle="collapse" data-target="#most-frequent-errors-events-details-rank-$rank">x Hide</button></p>
+ <p class="pull-right"><button type="button" class="btn btn-mini" data-toggle="collapse" data-target="#pgbmost-frequent-errors-events-details-rank-$rank">x Hide</button></p>
</div><!-- end of details collapse -->
};
print $fh qq{
-<p><button type="button" class="btn btn-mini" data-toggle="collapse" data-target="#most-frequent-errors-events-examples-rank-$rank">Examples</button></p>
-} if (($sample > 0) && ($#{$error_info{$k}{date}} >= 0));
+<p><button type="button" class="btn btn-mini" data-toggle="collapse" data-target="#pgbmost-frequent-errors-events-examples-rank-$rank">Examples</button></p>
+} if (($sample > 0) && ($#{$pgb_error_info{$k}{date}} >= 0));
print $fh qq{
<!-- Examples collapse -->
- <div id="most-frequent-errors-events-examples-rank-$rank" class="collapse">
+ <div id="pgbmost-frequent-errors-events-examples-rank-$rank" class="collapse">
<dl>
};
- for (my $i = 0 ; $i <= $#{$error_info{$k}{date}} ; $i++) {
+ for (my $i = 0 ; $i <= $#{$pgb_error_info{$k}{date}} ; $i++) {
last if (($sample > 0) && ($i == $sample));
# Escape HTML code in error message
- my $message = &escape_html($error_info{$k}{error}[$i]);
- my $details = "Date: " . $error_info{$k}{date}[$i] . "\n";
- if ($error_info{$k}{detail}[$i]) {
- $details .= "Detail: " . &escape_html($error_info{$k}{detail}[$i]) . "<br/>";
- }
- if ($error_info{$k}{context}[$i]) {
- $details .= "Context: " . &escape_html($error_info{$k}{context}[$i]) . "<br/>";
- }
- if ($error_info{$k}{hint}[$i]) {
- $details .= "Hint: " . &escape_html($error_info{$k}{hint}[$i]) . "<br/>";
- }
- if ($error_info{$k}{statement}[$i]) {
- $details .= "Statement: " . &escape_html($error_info{$k}{statement}[$i]) . "<br/>";
- }
- if ($error_info{$k}{db}[$i]) {
- $details .= "<b>Database:</b> $error_info{$k}{db}[$i] <b>User:</b> $error_info{$k}{user}[$i] <b>Remote:</b> $error_info{$k}{remote}[$i] <br/>";
+ my $message = &escape_html($pgb_error_info{$k}{error}[$i]);
+ my $details = "Date: " . $pgb_error_info{$k}{date}[$i] . "\n";
+ if ($pgb_error_info{$k}{db}[$i]) {
+ $details .= "<b>Database:</b> $pgb_error_info{$k}{db}[$i] <b>User:</b> $pgb_error_info{$k}{user}[$i] <b>Remote:</b> $pgb_error_info{$k}{remote}[$i] <br/>";
}
print $fh qq{
<dt><span class="$error_level_class">$message</span></dt>
}
print $fh qq{
</dl>
- <p class="pull-right"><button type="button" class="btn btn-mini" data-toggle="collapse" data-target="#most-frequent-errors-events-examples-rank-$rank">x Hide</button></p>
+ <p class="pull-right"><button type="button" class="btn btn-mini" data-toggle="collapse" data-target="#pgbmost-frequent-errors-events-examples-rank-$rank">x Hide</button></p>
</div>
<!-- end of details collapse -->
</td>
};
$rank++;
}
- if (scalar keys %error_info == 0) {
+ if (scalar keys %pgb_error_info == 0) {
print $fh qq{<tr><td colspan="7">$NODATA</td></tr>};
}
</tbody>
</table>
</div>
- </div><!-- end of most-frequent-errors-events -->
+ </div><!-- end of pgbmost-frequent-errors-events -->
};
}
my %stats = %{ fd_retrieve($fd) };
my %_overall_stat = %{$stats{overall_stat}};
+ my %_pgb_overall_stat = %{$stats{pgb_overall_stat}};
my %_overall_checkpoint = %{$stats{overall_checkpoint}};
my %_normalyzed_info = %{$stats{normalyzed_info}};
my %_error_info = %{$stats{error_info}};
+ my %_pgb_error_info = %{$stats{pgb_error_info}};
my %_connection_info = %{$stats{connection_info}};
+ my %_pgb_connection_info = %{$stats{pgb_connection_info}};
my %_database_info = %{$stats{database_info}};
my %_application_info = %{$stats{application_info}};
my %_user_info = %{$stats{user_info}};
my %_host_info = %{$stats{host_info}};
my %_checkpoint_info = %{$stats{checkpoint_info}};
my %_session_info = %{$stats{session_info}};
+ my %_pgb_session_info = %{$stats{pgb_session_info}};
my %_tempfile_info = %{$stats{tempfile_info}};
my %_cancelled_info = %{$stats{cancelled_info}};
my %_logs_type = %{$stats{logs_type}};
my %_lock_info = %{$stats{lock_info}};
my %_per_minute_info = %{$stats{per_minute_info}};
+ my %_pgb_per_minute_info = %{$stats{pgb_per_minute_info}};
my @_top_slowest = @{$stats{top_slowest}};
my $_nlines = $stats{nlines};
my $_first_log_timestamp = $stats{first_log_timestamp};
$overall_checkpoint{peak}{$k}{walfile_usage} += $_overall_checkpoint{peak}{$k}{walfile_usage};
}
+ ### pgbouncer related overall stats ###
+ foreach my $k (keys %{$_pgb_overall_stat{peak}}) {
+ $pgb_overall_stat{peak}{$k}{connection} += $_pgb_overall_stat{peak}{$k}{connection};
+ $pgb_overall_stat{peak}{$k}{session} += $_pgb_overall_stat{peak}{$k}{session};
+ $pgb_overall_stat{peak}{$k}{t_req} += $_pgb_overall_stat{peak}{$k}{t_req};
+ $pgb_overall_stat{peak}{$k}{t_inbytes} += $_pgb_overall_stat{peak}{$k}{t_inbytes};
+ $pgb_overall_stat{peak}{$k}{t_outbytes} += $_pgb_overall_stat{peak}{$k}{t_outbytes};
+ $pgb_overall_stat{peak}{$k}{t_avgduration} += $_pgb_overall_stat{peak}{$k}{t_avgduration};
+ }
+
+ foreach my $k (keys %{$_pgb_overall_stat{histogram}{session_time}}) {
+ $pgb_overall_stat{histogram}{session_time}{$k} += $_pgb_overall_stat{histogram}{session_time}{$k};
+ }
+ $pgb_overall_stat{histogram}{session_total} += $_pgb_overall_stat{histogram}{session_total};
+
### Logs level ###
+
foreach my $l (qw(LOG WARNING ERROR FATAL PANIC DETAIL HINT STATEMENT CONTEXT)) {
$logs_type{$l} += $_logs_type{$l} if exists $_logs_type{$l};
}
}
}
+ ### pgbouncer connection_info ###
+
+ foreach my $db (keys %{ $_pgb_connection_info{database} }) {
+ $pgb_connection_info{database}{$db} += $_pgb_connection_info{database}{$db};
+ }
+
+ foreach my $db (keys %{ $_pgb_connection_info{database_user} }) {
+ foreach my $user (keys %{ $_pgb_connection_info{database_user}{$db} }) {
+ $pgb_connection_info{database_user}{$db}{$user} += $_pgb_connection_info{database_user}{$db}{$user};
+ }
+ }
+
+ foreach my $user (keys %{ $_pgb_connection_info{user} }) {
+ $pgb_connection_info{user}{$user} += $_pgb_connection_info{user}{$user};
+ }
+
+ foreach my $host (keys %{ $_pgb_connection_info{host} }) {
+ $pgb_connection_info{host}{$host} += $_pgb_connection_info{host}{$host};
+ }
+
+ $pgb_connection_info{count} += $_pgb_connection_info{count};
+
+ foreach my $day (keys %{ $_pgb_connection_info{chronos} }) {
+ foreach my $hour (keys %{ $_pgb_connection_info{chronos}{$day} }) {
+ $pgb_connection_info{chronos}{$day}{$hour}{count} += $_pgb_connection_info{chronos}{$day}{$hour}{count}
+ }
+ }
+
### log_files ###
foreach my $f (@_log_files) {
}
}
+ ### pgbouncer error_info ###
+
+ foreach my $q (keys %_pgb_error_info) {
+ $pgb_error_info{$q}{count} += $_pgb_error_info{$q}{count};
+ foreach my $day (keys %{ $_pgb_error_info{$q}{chronos} }) {
+ foreach my $hour (keys %{$_pgb_error_info{$q}{chronos}{$day}}) {
+ $pgb_error_info{$q}{chronos}{$day}{$hour}{count} += $_pgb_error_info{$q}{chronos}{$day}{$hour}{count};
+ foreach my $min (keys %{$_pgb_error_info{$q}{chronos}{$day}{$hour}{min}}) {
+ $pgb_error_info{$q}{chronos}{$day}{$hour}{min}{$min} += $_pgb_error_info{$q}{chronos}{$day}{$hour}{min}{$min};
+ }
+ }
+ }
+ for (my $i = 0; $i <= $#{$_pgb_error_info{$q}{date}}; $i++) {
+ &pgb_set_top_error_sample( $q,
+ $_pgb_error_info{$q}{date}[$i],
+ $_pgb_error_info{$q}{error}[$i],
+ $_pgb_error_info{$q}{db}[$i],
+ $_pgb_error_info{$q}{user}[$i],
+ $_pgb_error_info{$q}{remote}[$i]
+ );
+ }
+ }
+
### per_minute_info ###
foreach my $day (keys %_per_minute_info) {
}
}
+ ### pgbouncer per_minute_info ###
+
+ foreach my $day (keys %_pgb_per_minute_info) {
+ foreach my $hour (keys %{ $_pgb_per_minute_info{$day} }) {
+ foreach my $min (keys %{ $_pgb_per_minute_info{$day}{$hour} }) {
+ $pgb_per_minute_info{$day}{$hour}{$min}{connection}{count} +=
+ ($_pgb_per_minute_info{$day}{$hour}{$min}{connection}{count} || 0);
+ $pgb_per_minute_info{$day}{$hour}{$min}{session}{count} +=
+ ($_pgb_per_minute_info{$day}{$hour}{$min}{session}{count} || 0);
+ $pgb_per_minute_info{$day}{$hour}{$min}{t_req} +=
+ ($_pgb_per_minute_info{$day}{$hour}{$min}{t_req} || 0);
+ $pgb_per_minute_info{$day}{$hour}{$min}{t_inbytes} +=
+ ($_pgb_per_minute_info{$day}{$hour}{$min}{t_inbytes} || 0);
+ $pgb_per_minute_info{$day}{$hour}{$min}{t_outbytes} +=
+ ($_pgb_per_minute_info{$day}{$hour}{$min}{t_outbytes} || 0);
+ $pgb_per_minute_info{$day}{$hour}{$min}{t_avgduration} +=
+ ($_pgb_per_minute_info{$day}{$hour}{$min}{t_avgduration} || 0);
+ }
+ }
+ }
+
### lock_info ###
foreach my $lock (keys %_lock_info) {
$session_info{app}{$app}{duration} += $_session_info{app}{$app}{duration};
}
+ ### pgbouncer session_info ###
+
+ foreach my $db (keys %{ $_pgb_session_info{database}}) {
+ $pgb_session_info{database}{$db}{count} += $_pgb_session_info{database}{$db}{count};
+ $pgb_session_info{database}{$db}{duration} += $_pgb_session_info{database}{$db}{duration};
+ }
+
+ $pgb_session_info{count} += $_pgb_session_info{count};
+
+ foreach my $day (keys %{ $_pgb_session_info{chronos}}) {
+ foreach my $hour (keys %{ $_pgb_session_info{chronos}{$day}}) {
+ $pgb_session_info{chronos}{$day}{$hour}{count} += $_pgb_session_info{chronos}{$day}{$hour}{count};
+ $pgb_session_info{chronos}{$day}{$hour}{duration} += $_pgb_session_info{chronos}{$day}{$hour}{duration};
+ }
+ }
+
+ foreach my $user (keys %{ $_pgb_session_info{user}}) {
+ $pgb_session_info{user}{$user}{count} += $_pgb_session_info{user}{$user}{count};
+ $pgb_session_info{user}{$user}{duration} += $_pgb_session_info{user}{$user}{duration};
+ }
+
+ $pgb_session_info{duration} += $_pgb_session_info{duration};
+
+ foreach my $host (keys %{ $_pgb_session_info{host}}) {
+ $pgb_session_info{host}{$host}{count} += $_pgb_session_info{host}{$host}{count};
+ $pgb_session_info{host}{$host}{duration} += $_pgb_session_info{host}{$host}{duration};
+ }
+
### tempfile_info ###
$tempfile_info{count} += $_tempfile_info{count}
return;
}
+# Function used to dump all relevant objects in memory to a single binary file
sub dump_as_binary
{
my $lfh = shift();
store_fd({
'overall_stat' => \%overall_stat,
+ 'pgb_overall_stat' => \%pgb_overall_stat,
'overall_checkpoint' => \%overall_checkpoint,
'normalyzed_info' => \%normalyzed_info,
'error_info' => \%error_info,
+ 'pgb_error_info' => \%pgb_error_info,
'connection_info' => \%connection_info,
+ 'pgb_connection_info' => \%pgb_connection_info,
'database_info' => \%database_info,
'application_info' => \%application_info,
'user_info' => \%user_info,
'host_info' => \%host_info,
'checkpoint_info' => \%checkpoint_info,
'session_info' => \%session_info,
+ 'pgb_session_info' => \%pgb_session_info,
'tempfile_info' => \%tempfile_info,
- 'error_info' => \%error_info,
'logs_type' => \%logs_type,
'lock_info' => \%lock_info,
'per_minute_info' => \%per_minute_info,
+ 'pgb_per_minute_info' => \%pgb_per_minute_info,
'top_slowest' => \@top_slowest,
'nlines' => $nlines,
'log_files' => \@log_files,
{
my $json = encode_json({
'overall_stat' => \%overall_stat,
+ 'pgb_overall_stat' => \%pgb_overall_stat,
'overall_checkpoint' => \%overall_checkpoint,
'normalyzed_info' => \%normalyzed_info,
'error_info' => \%error_info,
+ 'pgb_error_info' => \%pgb_error_info,
'connection_info' => \%connection_info,
+ 'pgb_connection_info' => \%pgb_connection_info,
'database_info' => \%database_info,
'application_info' => \%application_info,
'user_info' => \%user_info,
'host_info' => \%host_info,
'checkpoint_info' => \%checkpoint_info,
'session_info' => \%session_info,
+ 'pgb_session_info' => \%pgb_session_info,
'tempfile_info' => \%tempfile_info,
- 'error_info' => \%error_info,
'logs_type' => \%logs_type,
'lock_info' => \%lock_info,
'per_minute_info' => \%per_minute_info,
+ 'pgb_per_minute_info' => \%pgb_per_minute_info,
'top_slowest' => \@top_slowest,
'nlines' => $nlines,
'log_files' => \@log_files,
$i++;
}
- foreach my $x (sort (keys %SYMBOLS)) {
+ foreach my $x (sort keys %SYMBOLS) {
$code =~ s/$x/\$\$PGBGYA\$\$$SYMBOLS{$x}\$\$PGBGYB\$\$/gs;
}
for (my $x = 0 ; $x <= $#KEYWORDS1 ; $x++) {
return 1;
}
+sub parse_pgbouncer
+{
+
+ my $t_pid = $prefix_vars{'t_pid'};
+ my $t_session_id = $prefix_vars{'t_session_id'} || $prefix_vars{'t_pid'};
+
+ my $date_part = "$prefix_vars{'t_year'}$prefix_vars{'t_month'}$prefix_vars{'t_day'}";
+ my $cur_last_log_timestamp = "$prefix_vars{'t_year'}-$prefix_vars{'t_month'}-$prefix_vars{'t_day'} " .
+ "$prefix_vars{t_hour}:$prefix_vars{t_min}:$prefix_vars{t_sec}";
+
+ # Do not parse lines that are not an error message when error only report is requested
+ if ($error_only && ($prefix_vars{'t_loglevel'} !~ $full_error_regex)) {
+ return;
+ }
+
+ # Do not parse lines that are an error-like message when error reports are not wanted
+ if ($disable_error && ($prefix_vars{'t_loglevel'} =~ $full_error_regex)) {
+ return;
+ }
+
+ # Replace syslog tabulation rewrite
+ if ($format =~ /syslog/) {
+ $prefix_vars{'t_query'} =~ s/#011/\t/g;
+ }
+
+ if ($prefix_vars{'t_loglevel'} eq 'STATS') {
+
+ $pgb_per_minute_info{$date_part}{$prefix_vars{'t_hour'}}{$prefix_vars{'t_min'}}{t_req} = $prefix_vars{'t_req/s'};
+ $pgb_overall_stat{'peak'}{$cur_last_log_timestamp}{t_req} = $prefix_vars{'t_req/s'};
+ $pgb_per_minute_info{$date_part}{$prefix_vars{'t_hour'}}{$prefix_vars{'t_min'}}{t_inbytes} = $prefix_vars{'t_inbytes/s'};
+ $pgb_overall_stat{'peak'}{$cur_last_log_timestamp}{t_inbytes} = $prefix_vars{'t_inbytes/s'};
+
+ $pgb_per_minute_info{$date_part}{$prefix_vars{'t_hour'}}{$prefix_vars{'t_min'}}{t_outbytes} = $prefix_vars{'t_outbytes/s'};
+ $pgb_overall_stat{'peak'}{$cur_last_log_timestamp}{t_outbytes} = $prefix_vars{'t_outbytes/s'};
+
+ # We need millisecond instead of microsecond from pgbouncer
+ my $duration = int($prefix_vars{'t_avgduration'}/1000);
+ $pgb_per_minute_info{$date_part}{$prefix_vars{'t_hour'}}{$prefix_vars{'t_min'}}{t_avgduration} = $duration;
+ $pgb_overall_stat{'peak'}{$cur_last_log_timestamp}{t_avgduration} = $duration;
+
+ return;
+
+ } elsif ($prefix_vars{'t_loglevel'} =~ $main_error_regex) {
+
+ if ($prefix_vars{'t_loglevel'} eq 'WARNING') {
+
+ if ($prefix_vars{'t_query'} =~ /dropping database '([^']+)' as it does not exist anymore/) {
+ $prefix_vars{t_dbname} = $1;
+ } elsif ($prefix_vars{'t_query'} =~ /^([^:]+): (.*?)\/(.*?)\@([^:]+):\d+ (.*)/) {
+ $prefix_vars{t_query} = $5;
+ $prefix_vars{t_dbname} = $2;
+ $prefix_vars{t_dbuser} = $3;
+ $prefix_vars{t_dbclient} = $4;
+ $prefix_vars{t_session_id} = $1;
+ }
+
+ # Add log level at beginning of the query and normalize it
+ $prefix_vars{'t_query'} = $prefix_vars{'t_loglevel'} . ": " . $prefix_vars{'t_query'};
+ my $normalized_error = &pgb_normalize_error($prefix_vars{'t_query'});
+
+ # Stores total and normalized error count
+ $pgb_overall_stat{'errors_number'}++;
+ $pgb_error_info{$normalized_error}{count}++;
+
+ # Stores normalized error count per time
+ my $cur_day_str = "$prefix_vars{t_year}$prefix_vars{t_month}$prefix_vars{t_day}";
+ my $cur_hour_str = "$prefix_vars{t_hour}";
+ $pgb_error_info{$normalized_error}{chronos}{"$cur_day_str"}{"$cur_hour_str"}{count}++;
+ $pgb_error_info{$normalized_error}{chronos}{"$cur_day_str"}{"$cur_hour_str"}{min}{$prefix_vars{t_min}}++;
+
+ # Stores normalized query samples
+ if ($sample > 0) {
+ &pgb_set_top_error_sample( $normalized_error,
+ $cur_last_log_timestamp,
+ $prefix_vars{t_query},
+ $prefix_vars{t_dbname},
+ $prefix_vars{t_dbuser},
+ $prefix_vars{t_client} || $prefix_vars{t_dbclient}
+ );
+ }
+
+ } else {
+ print STDERR "UNPARSED LOG LEVEL: $prefix_vars{'t_loglevel'} => $prefix_vars{'t_query'}\n";
+ }
+ } elsif ($prefix_vars{'t_loglevel'} ne 'LOG') {
+ print STDERR "UNRECOGNIZED LOG LEVEL: $prefix_vars{'t_loglevel'} => $prefix_vars{'t_query'}\n";
+ }
+
+ # Stores connection activity
+ if ( ($prefix_vars{'t_loglevel'} eq 'LOG')
+ && ($prefix_vars{'t_query'} =~ /login attempt: db=([^\s]+) user=([^\s]+)/))
+ {
+ return if ($disable_connection);
+
+ my $usr = $prefix_vars{'t_dbuser'} || $2;
+ my $db = $prefix_vars{'t_dbname'} || $1;
+ my $host= $prefix_vars{'t_client'} || '';
+ $host = _gethostbyaddr($host) if ($dns_resolv);
+ $pgb_overall_stat{'peak'}{$cur_last_log_timestamp}{connection}++;
+
+ $pgb_connection_info{count}++;
+ $pgb_connection_info{user}{$usr}++;
+ $pgb_connection_info{host}{$host}++;
+ $pgb_connection_info{database}{$db}++;
+ $pgb_connection_info{database_user}{$db}{$usr}++;
+ $pgb_connection_info{chronos}{$date_part}{$prefix_vars{'t_hour'}}{count}++;
+
+ if ($graph) {
+ $pgb_per_minute_info{$date_part}{$prefix_vars{'t_hour'}}{"$prefix_vars{'t_min'}"}{connection}{count}++;
+ $pgb_per_minute_info{$date_part}{$prefix_vars{'t_hour'}}{"$prefix_vars{'t_min'}"}{connection}{second}{$prefix_vars{'t_sec'}}++;
+ }
+
+ # set current session workload
+ if ( !$disable_session ) {
+ $pgb_current_sessions{$t_session_id} = $prefix_vars{'t_timestamp'};
+ my $sess_count = scalar keys %pgb_current_sessions;
+ $pgb_overall_stat{'peak'}{$cur_last_log_timestamp}{session} = $sess_count;
+ $pgb_per_minute_info{$date_part}{$prefix_vars{'t_hour'}}{"$prefix_vars{'t_min'}"}{session}{count} = $sess_count;
+ $pgb_per_minute_info{$date_part}{$prefix_vars{'t_hour'}}{"$prefix_vars{'t_min'}"}{session}{second}{$prefix_vars{'t_sec'}} = $sess_count;
+ }
+
+ return;
+ }
+
+ # Store session duration
+ if (($prefix_vars{'t_loglevel'} eq 'LOG')
+ && ($prefix_vars{'t_query'} =~ /\(age=(\d+)\)$/))
+ {
+ return if ($disable_session);
+
+ # Use millisecond for session duration
+ my $time = $1*1000;
+
+ my $usr = $prefix_vars{'t_dbuser'} || '(nousr)';
+ my $db = $prefix_vars{'t_dbname'} || '(nodb)';
+ my $host= $prefix_vars{'t_client'} || '';
+ $host = _gethostbyaddr($host) if ($dns_resolv && $host);
+
+ # Store time in milliseconds since the connection attempt
+ if ($pgb_current_sessions{$t_session_id} =~ /(\d+):(\d+):(\d+\.\d+)$/) {
+ my $time1 = ($3 * 1000) + ($2 * 60 * 1000) + ($1 * 60 * 60 * 1000);
+ $prefix_vars{'t_timestamp'} =~ /(\d+):(\d+):(\d+\.\d+)$/;
+ my $time2 = ($3 * 1000) + ($2 * 60 * 1000) + ($1 * 60 * 60 * 1000);
+ $time = $time2 - $time1;
+ }
+ $pgb_session_info{count}++;
+ $pgb_session_info{duration} += $time;
+ $pgb_session_info{chronos}{$date_part}{$prefix_vars{'t_hour'}}{count}++;
+ $pgb_session_info{chronos}{$date_part}{$prefix_vars{'t_hour'}}{duration} += $time;
+ $pgb_session_info{database}{$db}{count}++;
+ $pgb_session_info{database}{$db}{duration} += $time;
+ $pgb_session_info{user}{$usr}{count}++;
+ $pgb_session_info{user}{$usr}{duration} += $time;
+ $pgb_session_info{host}{$host}{count}++;
+ $pgb_session_info{host}{$host}{duration} += $time;
+ my $k = &get_hist_inbound($time, @histogram_session_time);
+ $pgb_overall_stat{histogram}{session_time}{$k}++;
+ $pgb_overall_stat{histogram}{session_total}++;
+
+ delete $pgb_current_sessions{$t_session_id};
+
+ return;
+ }
+
+ return 1;
+}
+
+# Remain current parsed information into memory for subsequent use
sub set_current_infos
{
return $orig_query;
}
+# Normalize pgbouncer error messages
+sub pgb_normalize_error
+{
+ my $orig_query = shift;
+
+ return if (!$orig_query);
+
+ # Replace changing parameter by ...
+ $orig_query =~ s/"[^"]*"/"..."/g;
+ $orig_query =~ s/'[^']*'/'...'/g;
+ $orig_query =~ s/\(.*\)/\(...\)/g;
+
+ # Need more normalization stuff here
+ return $orig_query;
+}
+
sub average_per_minutes
{
my $val = shift;
my $nline = 0;
my $fmt = '';
- localdie("FATAL: can't open file $file, $!\n") unless(open(TESTFILE, $file));
+ localdie("FATAL: when detecting binary file, can't open file $file, $!\n") unless(open(TESTFILE, $file));
my $fltf = <TESTFILE>;
close(TESTFILE);
# is file in binary format ?
{
$fmt = 'stderr';
$nfound++;
+ } elsif ($line =~ /^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\.\d+(?: [A-Z\+\-\d]{3,6})? (\d+) (LOG|ERROR) (.\-0x[0-9a-f\.]*):/) {
+ # pgbouncer log file can be parsed with other postgresql file
+ # so even if it is given at first position, set default format
+ # to stderr. Try to look for format at next file.
+ $fmt = autodetect_format($log_files[1]) if ($#log_files >= 1);
+ # If we just have one single pgbouncer file, force pgbouncer_only to 1
+ if ($#log_files == 0) {
+ $pgbouncer_only = 1;
+ $fmt = 'stderr';
+ }
+ $nfound++ if ($fmt);
}
last if (($nfound > 10) || ($nline > 5000));
}
my ($query, $remove_white_tokens) = @_;
my $re = qr{
- (
- (?:--)[\ \t\S]* # single line comments
- |
- (?:\-\|\-) # range operator "is adjacent to"
- |
- (?:\->>|\->|\#>>|\#>|\?\&|\?) # Json Operators
- |
- (?:\#<=|\#>=|\#<>|\#<|\#=) # compares tinterval and reltime
- |
- (?:>>=|<<=) # inet operators
- |
- (?:!!|\@\@\@) # deprecated factorial and full text search operators
- |
- (?:\|\|\/|\|\/) # square root and cube root
- |
- (?:\@\-\@|\@\@|\#\#|<\->|<<\||\|>>|\&<\||\&<|\|\&>|\&>|<\^|>\^|\?\#|\#|\?<\||\?\-\||\?\-|\?\|\||\?\||\@>|<\@|\~=)
- # Geometric Operators
- |
- (?:~<=~|~>=~|~>~|~<~) # string comparison for pattern matching operator families
- |
- (?:!~~|!~~\*|~~\*|~~) # LIKE operators
- |
- (?:!~\*|!~|~\*) # regular expression operators
- |
- (?:\*=|\*<>|\*<=|\*>=|\*<|\*>) # composite type comparison operators
- |
- (?:<>|<=>|>=|<=|==|!=|=|!|<<|>>|<|>|\|\||\||&&|&|-|\+|\*(?!/)|/(?!\*)|\%|~|\^|\?) # operators and tests
- |
- [\[\]\(\),;.] # punctuation (parenthesis, comma)
- |
- \'\'(?!\') # empty single quoted string
- |
- \"\"(?!\"") # empty double quoted string
- |
- "(?>(?:(?>[^"\\]+)|""|\\.)*)+" # anything inside double quotes, ungreedy
- |
- `(?>(?:(?>[^`\\]+)|``|\\.)*)+` # anything inside backticks quotes, ungreedy
- |
- '(?>(?:(?>[^'\\]+)|''|\\.)*)+' # anything inside single quotes, ungreedy.
- |
- /\*[\ \t\r\n\S]*?\*/ # C style comments
- |
- (?:[\w:@]+(?:\.(?:\w+|\*)?)*) # words, standard named placeholders, db.table.*, db.*
- |
- (?:\$\w+\$)
- |
- (?: \$_\$ | \$\d+ | \${1,2} | \$\w+\$ ) # dollar expressions - eg $_$ $3 $$ $BODY$
- |
- \n # newline
- |
- [\t\ ]+ # any kind of white spaces
- )
+ (
+ (?:--)[\ \t\S]* # single line comments
+ |
+ (?:\-\|\-) # range operator "is adjacent to"
+ |
+ (?:\->>|\->|\#>>|\#>|\?\&|\?) # Json Operators
+ |
+ (?:\#<=|\#>=|\#<>|\#<|\#=) # compares tinterval and reltime
+ |
+ (?:>>=|<<=) # inet operators
+ |
+ (?:!!|\@\@\@) # deprecated factorial and full text search operators
+ |
+ (?:\|\|\/|\|\/) # square root and cube root
+ |
+ (?:\@\-\@|\@\@|\#\#|<\->|<<\||\|>>|\&<\||\&<|\|\&>|\&>|<\^|>\^|\?\#|\#|\?<\||\?\-\||\?\-|\?\|\||\?\||\@>|<\@|\~=)
+ # Geometric Operators
+ |
+ (?:~<=~|~>=~|~>~|~<~) # string comparison for pattern matching operator families
+ |
+ (?:!~~|!~~\*|~~\*|~~) # LIKE operators
+ |
+ (?:!~\*|!~|~\*) # regular expression operators
+ |
+ (?:\*=|\*<>|\*<=|\*>=|\*<|\*>) # composite type comparison operators
+ |
+ (?:<>|<=>|>=|<=|==|!=|=|!|<<|>>|<|>|\|\||\||&&|&|-|\+|\*(?!/)|/(?!\*)|\%|~|\^|\?) # operators and tests
+ |
+ [\[\]\(\),;.] # punctuation (parenthesis, comma)
+ |
+ \'\'(?!\') # empty single quoted string
+ |
+ \"\"(?!\"") # empty double quoted string
+ |
+ "(?>(?:(?>[^"\\]+)|""|\\.)*)+" # anything inside double quotes, ungreedy
+ |
+ `(?>(?:(?>[^`\\]+)|``|\\.)*)+` # anything inside backticks quotes, ungreedy
+ |
+ '(?>(?:(?>[^'\\]+)|''|\\.)*)+' # anything inside single quotes, ungreedy.
+ |
+ /\*[\ \t\r\n\S]*?\*/ # C style comments
+ |
+ (?:[\w:@]+(?:\.(?:\w+|\*)?)*) # words, standard named placeholders, db.table.*, db.*
+ |
+ (?:\$\w+\$)
+ |
+ (?: \$_\$ | \$\d+ | \${1,2} | \$\w+\$ ) # dollar expressions - eg $_$ $3 $$ $BODY$
+ |
+ \n # newline
+ |
+ [\t\ ]+ # any kind of white spaces
+ )
}smx;
my @query = ();