Skip to content

Commit 6ef1303

Browse files
HNOONa-0grooverdan
authored andcommitted
MDEV-34954 Add JSON flag for mysqldumpslow.sh output
By using the perl JSON module, we've added an additional flag -j/--json to output the dumped data in JSON format.
1 parent 891108e commit 6ef1303

File tree

1 file changed

+104
-22
lines changed

1 file changed

+104
-22
lines changed

scripts/mysqldumpslow.sh

Lines changed: 104 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,22 @@ GetOptions(\%opt,
5050
'h=s', # hostname/basename of db server for *-slow.log filename (can be wildcard)
5151
'i=s', # name of server instance (if using mysql.server startup script)
5252
'l!', # don't subtract lock time from total time
53+
'json|j!', # print as a JSON-formatted string
5354
) or usage("bad option");
5455

5556
$opt{'help'} and usage();
5657

58+
# check if JSON module is available
59+
if ($opt{json}) {
60+
eval {
61+
require JSON;
62+
JSON->import();
63+
1;
64+
} or do {
65+
die "JSON module not found. Please install the JSON module to use --json option.\n";
66+
};
67+
}
68+
5769
unless (@ARGV) {
5870
my $defaults = `my_print_defaults --mysqld`;
5971

@@ -99,14 +111,16 @@ warn "\nReading mysql slow query log from @ARGV\n";
99111
my @pending;
100112
my %stmt;
101113
$/ = ";\n#"; # read entire statements using paragraph mode
102-
while ( defined($_ = shift @pending) or defined($_ = <>) ) {
114+
while (<>) {
103115
warn "[[$_]]\n" if $opt{d}; # show raw paragraph being read
104116

105-
my @chunks = split /^\/.*Version.*started with[\000-\377]*?Time.*Id.*Command.*Argument.*\n/m;
106-
if (@chunks > 1) {
107-
unshift @pending, map { length($_) ? $_ : () } @chunks;
108-
warn "<<".join(">>\n<<",@chunks).">>" if $opt{d};
109-
next;
117+
# remove fluff that mysqld writes to log when it (re)starts:
118+
s!^.*Version.*started with:.*\n!!mg;
119+
s!^Tcp port: \d+ Unix socket: \S+\n!!mg;
120+
s!^Time.*Id.*Command.*Argument.*\n!!mg;
121+
# if there is only header info, skip
122+
if ($_ eq '') {
123+
next;
110124
}
111125

112126
s/^#? Time: \d{6}\s+\d+:\d+:\d+.*\n//;
@@ -120,18 +134,13 @@ while ( defined($_ = shift @pending) or defined($_ = <>) ) {
120134

121135
$t -= $l unless $opt{l};
122136

123-
# remove fluff that mysqld writes to log when it (re)starts:
124-
s!^/.*Version.*started with:.*\n!!mg;
125-
s!^Tcp port: \d+ Unix socket: \S+\n!!mg;
126-
s!^Time.*Id.*Command.*Argument.*\n!!mg;
127-
128137
# Remove optimizer info
129138
s!^# QC_Hit: \S+\s+Full_scan: \S+\s+Full_join: \S+\s+Tmp_table: \S+\s+Tmp_table_on_disk: \S+[^\n]+\n!!mg;
130139
s!^# Filesort: \S+\s+Filesort_on_disk: \S+[^\n]+\n!!mg;
131140
s!^# Full_scan: \S+\s+Full_join: \S+[^\n]+\n!!mg;
132141

133-
s/^use \w+;\n//; # not consistently added
134-
s/^SET timestamp=\d+;\n//;
142+
s!^SET timestamp=\d+;\n!!m; # remove the redundant timestamp that is always added to each query
143+
s!^use \w+;\n!!m; # not consistently added
135144

136145
s/^[ ]*\n//mg; # delete blank lines
137146
s/^[ ]*/ /mg; # normalize leading whitespace
@@ -181,15 +190,86 @@ my @sorted = sort { $stmt{$b}->{$opt{s}} <=> $stmt{$a}->{$opt{s}} } keys %stmt;
181190
@sorted = @sorted[0 .. $opt{t}-1] if $opt{t};
182191
@sorted = reverse @sorted if $opt{r};
183192
184-
foreach (@sorted) {
185-
my $v = $stmt{$_} || die;
186-
my ($c, $t, $at, $l, $al, $r, $ar, $e, $ae, $a, $aa) = @{ $v }{qw(c t at l al r ar e ae a aa)};
187-
my @users = keys %{$v->{users}};
188-
my $user = (@users==1) ? $users[0] : sprintf "%dusers",scalar @users;
189-
my @hosts = keys %{$v->{hosts}};
190-
my $host = (@hosts==1) ? $hosts[0] : sprintf "%dhosts",scalar @hosts;
191-
printf "Count: %d Time=%.2fs (%ds) Lock=%.2fs (%ds) Rows_sent=%.1f (%d), Rows_examined=%.1f (%d), Rows_affected=%.1f (%d), $user\@$host\n%s\n\n",
192-
$c, $at,$t, $al,$l, $ar,$r, $ae, $e, $aa, $a, $_;
193+
if(!$opt{json}) {
194+
foreach (@sorted) {
195+
my $v = $stmt{$_} || die;
196+
my ($c, $t, $at, $l, $al, $r, $ar, $e, $ae, $a, $aa) = @{ $v }{qw(c t at l al r ar e ae a aa)};
197+
my @users = keys %{$v->{users}};
198+
my $user = (@users==1) ? $users[0] : sprintf "%dusers",scalar @users;
199+
my @hosts = keys %{$v->{hosts}};
200+
my $host = (@hosts==1) ? $hosts[0] : sprintf "%dhosts",scalar @hosts;
201+
printf "Count: %d Time=%.2fs (%ds) Lock=%.2fs (%ds) Rows_sent=%.1f (%d), Rows_examined=%.1f (%d), Rows_affected=%.1f (%d), $user\@$host\n%s\n\n",
202+
$c, $at,$t, $al,$l, $ar,$r, $ae, $e, $aa, $a, $_;
203+
}
204+
} else {
205+
my @json_output;
206+
foreach (@sorted) {
207+
my $v = $stmt{$_} || die;
208+
my ($c, $t, $at, $l, $al, $r, $ar, $e, $ae, $a, $aa) = @{ $v }{qw(c t at l al r ar e ae a aa)};
209+
my @users = keys %{$v->{users}};
210+
my $user = (@users==1) ? $users[0] : sprintf "%dusers",scalar @users;
211+
my @hosts = keys %{$v->{hosts}};
212+
my $host = (@hosts==1) ? $hosts[0] : sprintf "%dhosts",scalar @hosts;
213+
214+
# parse the engine data
215+
my %engine;
216+
if ($_ =~ /^\s*#\s*Pages_accessed:\s*(\S+)\s+Pages_read:\s*(\S+)\s+Pages_prefetched:\s*(\S+)\s+Pages_updated:\s*(\S+)\s+Old_rows_read:\s*(\S+)/m) {
217+
@engine{qw(Pages_accessed Pages_read Pages_prefetched Pages_updated Old_rows_read)} = ($1, $2, $3, $4, $5);
218+
}
219+
if ($_ =~ /^\s*#\s*Pages_read_time:\s*(\S+)\s+Engine_time:\s*(\S+)/m) {
220+
@engine{qw(Pages_read_time Engine_time)} = ($1, $2);
221+
}
222+
# convert engine data to numbers
223+
map { $engine{$_} += 0 } keys %engine if $opt{a};
224+
225+
# build a structured explain output
226+
my @explain_lines = ($_ =~ /^\s*# explain: (.+)$/mg);
227+
my $explain;
228+
if (@explain_lines >= 2) {
229+
my @headers = split /\s+/, shift @explain_lines;
230+
$explain = [
231+
map {
232+
my @values = split /\s+/, $_;
233+
my %row;
234+
@row{@headers} = @values;
235+
\%row;
236+
} @explain_lines
237+
];
238+
# normalize the explain data
239+
foreach my $row (@$explain) {
240+
foreach my $key (keys %$row) {
241+
my $val = $row->{$key};
242+
$row->{$key} = undef if $val eq 'NULL';
243+
$row->{$key} = $val + 0 if $opt{a} and $val =~ /^\d+(?:\.\d+)?$/;
244+
}
245+
}
246+
}
247+
248+
# get the query string
249+
(my $query = $_) =~ s/^\s*#.*\n//mg;
250+
$query =~ s/^\s+|\s+$//g; # trim leading/trailing whitespace
251+
252+
# output the data as JSON
253+
push @json_output, {
254+
count => $c,
255+
avg_time => $at,
256+
total_time => $t,
257+
avg_lock => $al,
258+
total_lock => $l,
259+
avg_rows_sent => $ar,
260+
total_rows_sent => $r,
261+
avg_examined => $ae,
262+
total_examined => $e,
263+
avg_affected => $aa,
264+
total_affected => $a,
265+
user => $user,
266+
host => $host,
267+
query => $query,
268+
engine => (%engine ? \%engine : undef),
269+
explain => ($explain ? $explain : undef),
270+
};
271+
}
272+
print JSON->new->canonical(1)->pretty->encode(\@json_output);
193273
}
194274
195275
sub usage {
@@ -202,6 +282,7 @@ Parse and summarize the MySQL slow query log. Options are
202282
--verbose verbose
203283
--debug debug
204284
--help write this text to standard output
285+
--json print as a JSON-formatted string
205286
206287
-v verbose
207288
-d debug
@@ -226,6 +307,7 @@ Parse and summarize the MySQL slow query log. Options are
226307
default is '*', i.e. match all
227308
-i NAME name of server instance (if using mysql.server startup script)
228309
-l don't subtract lock time from total time
310+
-j print as a JSON-formatted string
229311
230312
HERE
231313
if ($str) {

0 commit comments

Comments
 (0)