5 Memcached - A Plugin to monitor Memcached Servers (Multigraph)
7 =head1 MUNIN CONFIGURATION
10 env.host 127.0.0.1 *default*
11 env.port 11211 *default*
12 env.timescale 3 *default*
14 =head2 MUNIN ENVIRONMENT CONFIGURATION EXPLANATION
16 host = host we are going to monitor
17 port = port we are connecting to, in order to gather stats
18 timescale = what time frame do we want to format our graphs too
20 =head1 NODE CONFIGURATION
22 Please make sure you can telnet to your memcache servers and issue the
23 following commands: stats, stats settings, stats items and stats slabs.
25 Available Graphs contained in this Plugin
27 bytes => This graphs the current network traffic in and out
29 commands => I<MULTIGRAPH> This graphs the current commands being issued to the memcache machine. B<Multigraph breaks this down to per slab.>
31 conns => This graphs the current, max connections as well as avg conns per sec avg conns per sec is derived from total_conns / uptime.
33 evictions => I<MULTIGRAPH> This graphs the current evictions on the node. B<Multigraph breaks this down to per slab.>
35 items => I<MULTIGRAPH> This graphs the current items and total items in the memcached node. B<Multigraph breaks this down to per slab.>
37 memory => I<MULTIGRAPH> This graphs the current and max memory allocation B<Multigraph breaks this down to per slab.>
39 The following example holds true for all graphing options in this plugin.
40 Example: ln -s /usr/share/munin/plugins/memcached_multi_ /etc/munin/plugins/memcached_multi_bytes
42 =head1 ADDITIONAL INFORMATION
44 You will find that some of the graphs have LEI on them. This was done in order to save room
45 on space for text and stands for B<Last Evicted Item>.
47 The B<Timescale> variable formats certain graphs based on the following guidelines.
50 3 => Hours B<*Default*>
53 =head1 ACKNOWLEDGEMENTS
55 The core of this plugin is based on the mysql_ plugin maintained by Kjell-Magne Ãierud
57 Thanks to dormando as well for putting up with me ;)
61 Matt West < https://code.google.com/p/memcached-munin-plugin/ >
70 #%# capabilities=autoconf suggest
80 my $host = $ENV{host} || "127.0.0.1";
81 my $port = $ENV{port} || 11211;
84 # This hash contains the information contained in two memcache commands
85 # stats and stats settings.
88 # This gives us eviction rates and other hit stats per slab
89 # We track this so we can see if something was evicted earlier than necessary
92 # This gives us the memory size and usage per slab
93 # We track this so we can see what slab is being used the most and has no free chunks
94 # so we can re-tune memcached to allocate more pages for the specified chunk size
96 my $timescale = $ENV{timescale} || 3;
97 # This gives us the ability to control the timescale our graphs are displaying.
98 # The default it set to divide by hours, if you want to get seconds set it to 1.
99 # Options: 1 = seconds, 2 = minutes, 3 = hours, 4 = days
101 # So I was trying to figure out how to build this up, and looking at some good examples
102 # I decided to use the format, or for the most part, the format from the mysql_ munin plugin
103 # for Innodb by Kjell-Magne Ãierud, it just spoke ease of flexibility especially with multigraphs
106 # %graphs is a container for all of the graph definition information. In here is where you'll
107 # find the configuration information for munin's graphing procedure.
110 # $graph{graph_name} => {
112 # # You'll find keys and values stored here for graph manipulation
115 # # Name: name given to data value
116 # # Attr: Attribute for given value
117 # { name => 'Name', (Attr) },
125 args => '--base 1000 --lower-limit 0',
126 vlabel => 'Items in Memcached',
127 category => 'memcached',
129 info => 'This graph shows the number of items in use by memcached',
132 { name => 'curr_items', label => 'Current Items', min => '0' },
133 { name => 'total_items', label => 'New Items', min => '0', type => 'DERIVE' },
139 args => '--base 1024 --lower-limit 0',
140 vlabel => 'Bytes Used',
141 category => 'memcached',
142 title => 'Memory Usage',
143 info => 'This graph shows the memory consumption of memcached',
146 { name => 'limit_maxbytes', draw => 'AREA', label => 'Maximum Bytes Allocated', min => '0' },
147 { name => 'bytes', draw => 'AREA', label => 'Current Bytes Used', min => '0' },
153 args => '--base 1000',
154 vlabel => 'bits in (-) / out (+)',
155 title => 'Network Traffic',
156 category => 'memcached',
157 info => 'This graph shows the network traffic in (-) / out (+) of the machine',
158 order => 'bytes_read bytes_written',
161 { name => 'bytes_read', type => 'DERIVE', label => 'Network Traffic coming in (-)', graph => 'no', cdef => 'bytes_read,8,*', min => '0' },
162 { name => 'bytes_written', type => 'DERIVE', label => 'Traffic in (-) / out (+)', negative => 'bytes_read', cdef => 'bytes_written,8,*', min => '0' },
168 args => '--base 1000 --lower-limit 0',
169 vlabel => 'Connections per ${graph_period}',
170 category => 'memcached',
171 title => 'Connections',
172 info => 'This graph shows the number of connections being handled by memcached',
173 order => 'max_conns curr_conns avg_conns',
176 { name => 'curr_conns', label => 'Current Connections', min => '0' },
177 { name => 'max_conns', label => 'Max Connections', min => '0' },
178 { name => 'avg_conns' , label => 'Avg Connections', min => '0' },
182 $graphs{commands} = {
184 args => '--base 1000 --lower-limit 0',
185 vlabel => 'Commands per ${graph_period}',
186 category => 'memcached',
188 info => 'This graph shows the number of commands being handled by memcached',
191 { name => 'cmd_get', type => 'DERIVE', label => 'Gets', info => 'Cumulative number of retrieval reqs', min => '0' },
192 { name => 'cmd_set', type => 'DERIVE', label => 'Sets', info => 'Cumulative number of storage reqs', min => '0' },
193 { name => 'get_hits', type => 'DERIVE', label => 'Get Hits', info => 'Number of keys that were requested and found', min => '0' },
194 { name => 'get_misses', type => 'DERIVE', label => 'Get Misses', info => 'Number of keys there were requested and not found', min => '0' },
195 { name => 'delete_hits', type => 'DERIVE', label => 'Delete Hits', info => 'Number of delete requests that resulted in a deletion of a key', min => '0' },
196 { name => 'delete_misses', type => 'DERIVE', label => 'Delete Misses', info => 'Number of delete requests for missing key', min => '0' },
197 { name => 'incr_hits', type => 'DERIVE', label => 'Increment Hits', info => 'Number of successful increment requests', min => '0' },
198 { name => 'incr_misses', type => 'DERIVE', label => 'Increment Misses', info => 'Number of unsuccessful increment requests', min => '0' },
199 { name => 'decr_hits', type => 'DERIVE', label => 'Decrement Hits', info => 'Number of successful decrement requests', min => '0' },
200 { name => 'decr_misses', type => 'DERIVE', label => 'Decrement Misses', info => 'Number of unsuccessful decrement requests', min => '0' },
204 $graphs{evictions} = {
206 args => '--base 1000 --lower-limit 0',
207 vlabel => 'Evictions per ${graph_period}',
208 category => 'memcached',
209 title => 'Evictions',
210 info => 'This graph shows the number of evictions per second',
213 { name => 'evictions', label => 'Evictions', info => 'Cumulative Evictions Across All Slabs', type => 'DERIVE', min => '0' },
214 { name => 'evicted_nonzero', label => 'Evictions prior to Expire', info => 'Cumulative Evictions forced to expire prior to expiration', type => 'DERIVE', min => '0' },
215 { name => 'reclaimed', label => 'Reclaimed Items', info => 'Cumulative Reclaimed Item Entries Across All Slabs', type => 'DERIVE', min => '0' },
219 $graphs{slabchnks} = {
221 args => '--base 1000 --lower-limit 0',
222 vlabel => 'Available Chunks for this Slab',
223 category => 'memcached',
224 title => 'Chunk Usage for Slab: ',
225 info => 'This graph shows you the chunk usage for this memory slab.',
228 { name => 'total_chunks', label => 'Total Chunks Available', min => '0' },
229 { name => 'used_chunks', label => 'Total Chunks in Use', min => '0' },
230 { name => 'free_chunks', label => 'Total Chunks Not in Use (Free)', min => '0' },
234 $graphs{slabhits} = {
236 args => '--base 1000 --lower-limit 0',
237 vlabel => 'Hits per Slab per ${graph_period}',
238 category => 'memcached',
239 title => 'Hits for Slab: ',
240 info => 'This graph shows you the successful hit rate for this memory slab.',
243 { name => 'get_hits', label => 'Get Requests', type => 'DERIVE', min => '0' },
244 { name => 'cmd_set', label => 'Set Requests', type => 'DERIVE', min => '0' },
245 { name => 'delete_hits', label => 'Delete Requests', type => 'DERIVE', min => '0' },
246 { name => 'incr_hits', label => 'Increment Requests', type => 'DERIVE', min => '0' },
247 { name => 'decr_hits', label => 'Decrement Requests', type => 'DERIVE', min => '0' },
251 $graphs{slabevics} = {
253 args => '--base 1000 --lower-limit 0',
254 vlabel => 'Evictions per Slab per ${graph_period}',
255 category => 'memcached',
256 title => 'Evictions for Slab: ',
257 info => 'This graph shows you the eviction rate for this memory slab.',
260 { name => 'evicted', label => 'Total Evictions', type => 'DERIVE', min => '0' },
261 { name => 'evicted_nonzero', label => 'Evictions from LRU Prior to Expire', type => 'DERIVE', min => '0' },
262 { name => 'reclaimed', label => 'Reclaimed Expired Items', info => 'This is number of times items were stored in expired entry memory space', type => 'DERIVE', min => '0' },
266 $graphs{slabevictime} = {
268 args => '--base 1000 --lower-limit 0',
269 vlabel => ' since Request for LEI',
270 category => 'memcached',
271 title => 'Eviction Request Time for Slab: ',
272 info => 'This graph shows you the time since we requested the last evicted item',
275 { name => 'evicted_time', label => 'Eviction Time (LEI)', info => 'Time Since Request for Last Evicted Item', min => '0' },
279 $graphs{slabitems} = {
281 args => '--base 1000 --lower-limit 0',
282 vlabel => 'Items per Slab',
283 category => 'memcached',
284 title => 'Items in Slab: ',
285 info => 'This graph shows you the number of items and reclaimed items per slab.',
288 { name => 'number', label => 'Items', info => 'This is the amount of items stored in this slab', min => '0' },
292 $graphs{slabitemtime} = {
294 args => '--base 1000 --lower-limit 0',
295 vlabel => ' since item was stored',
296 category => 'memcached',
297 title => 'Age of Eldest Item in Slab: ',
298 info => 'This graph shows you the time of the eldest item in this slab',
301 { name => 'age', label => 'Eldest Item\'s Age', min => '0' },
306 #### Config Check ####
309 if (defined $ARGV[0] && $ARGV[0] eq 'config') {
311 $0 =~ /memcached_multi_(.+)*/;
314 die 'Unknown Plugin Specified: ' . ($plugin ? $plugin : '') unless $graphs{$plugin};
316 # We need to fetch the stats before we do any config, cause its needed for multigraph
319 # Now lets go ahead and print out our config.
325 #### Autoconf Check ####
328 if (defined $ARGV[0] && $ARGV[0] eq 'autoconf') {
330 my $s = IO::Socket::INET->new(
340 print "no (unable to connect to $host\[:$port\])\n";
346 #### Suggest Check ####
349 if (defined $ARGV[0] && $ARGV[0] eq 'suggest') {
351 my $s = IO::Socket::INET->new(
358 my @rootplugins = ('bytes','conns','commands','evictions','items','memory');
359 foreach my $plugin (@rootplugins) {
364 print "no (unable to connect to $host\[:$port\])\n";
370 #### Well We aren't running (auto)config/suggest so lets print some stats ####
376 #### Subroutines for printing info gathered from memcached ####
380 #### This subroutine performs the bulk processing for printing statistics.
385 $0 =~ /memcached_multi_(.+)*/;
388 die 'Unknown Plugin Specified: ' . ($plugin ? $plugin : '') unless $graphs{$plugin};
390 # Well we need to actually fetch the stats before we do anything to them.
393 # Now lets go ahead and print out our output.
395 if ($plugin eq 'memory') {
396 @subgraphs = ('slabchnks');
397 foreach my $slabid(sort{$a <=> $b} keys %chnks) {
398 print_submulti_output($slabid,$plugin,@subgraphs);
400 print_rootmulti_output($plugin);
401 } elsif ($plugin eq 'commands') {
402 @subgraphs = ('slabhits');
403 foreach my $slabid(sort{$a <=> $b} keys %chnks) {
404 print_submulti_output($slabid,$plugin,@subgraphs);
406 print_rootmulti_output($plugin);
407 } elsif ($plugin eq 'evictions') {
408 @subgraphs = ('slabevics','slabevictime');
409 foreach my $slabid (sort{$a <=> $b} keys %items) {
410 print_submulti_output($slabid,$plugin,@subgraphs);
412 print_rootmulti_output($plugin);
413 } elsif ($plugin eq 'items') {
414 @subgraphs = ('slabitems','slabitemtime');
415 foreach my $slabid (sort{$a <=> $b} keys %items) {
416 print_submulti_output($slabid,$plugin,@subgraphs);
418 print_rootmulti_output($plugin);
420 print_root_output($plugin);
427 #### This subroutine is for the root non-multigraph graphs which render on the main node page ####
430 sub print_root_output {
433 my $graph = $graphs{$plugin};
435 print "graph memcached_$plugin\n";
437 if ($plugin ne 'conns') {
438 foreach my $dsrc (@{$graph->{datasrc}}) {
439 my %datasrc = %$dsrc;
440 while ( my ($key, $value) = each(%datasrc)) {
441 next if ($key ne 'name');
442 my $output = $stats{$value};
443 print "$dsrc->{name}.value $output\n";
448 foreach my $dsrc (@{$graph->{datasrc}}) {
449 my %datasrc = %$dsrc;
450 while ( my ($key, $value) = each(%datasrc)) {
451 if ($value eq 'max_conns') {
452 $output = $stats{maxconns};
453 } elsif ($value eq 'curr_conns') {
454 $output = $stats{curr_connections};
455 } elsif ($value eq 'avg_conns') {
456 $output = sprintf("%02d", $stats{total_connections} / $stats{uptime});
460 print "$dsrc->{name}.value $output\n";
469 #### This subroutine is for the root multigraph graphs which render on the main node page ####
472 sub print_rootmulti_output {
475 my $graph = $graphs{$plugin};
477 print "multigraph memcached_$plugin\n";
479 foreach my $dsrc (@{$graph->{datasrc}}) {
481 my %datasrc = %$dsrc;
482 while ( my ($key, $value) = each(%datasrc)) {
483 next if ($key ne 'name');
484 if (($plugin eq 'evictions') && ($value eq 'evicted_nonzero')) {
485 foreach my $slabid (sort{$a <=> $b} keys %items) {
486 $output += $items{$slabid}->{evicted_nonzero};
489 $output = $stats{$value};
491 print "$dsrc->{name}.value $output\n";
499 #### This subroutine is for the sub multigraph graphs created via the multigraph plugin ####
502 sub print_submulti_output {
503 my ($slabid,$plugin,@subgraphs) = (@_);
504 my $currslab = undef;
506 foreach my $sgraph (@subgraphs) {
508 my $graph = $graphs{$sgraph};
510 print "multigraph memcached_$plugin.$sgraph\_$slabid\n";
512 if ($plugin eq 'evictions') {
513 $currslab = $items{$slabid};
514 } elsif ($plugin eq 'memory') {
515 $currslab = $chnks{$slabid};
516 } elsif ($plugin eq 'commands') {
517 $currslab = $chnks{$slabid};
518 } elsif ($plugin eq 'items') {
519 $currslab = $items{$slabid};
524 foreach my $dsrc (@{$graph->{datasrc}}) {
525 my %datasrc = %$dsrc;
526 while ( my ($key, $value) = each(%datasrc)) {
527 next if ($key ne 'name');
528 my $output = $currslab->{$value};
529 if (($sgraph eq 'slabevictime') || ($sgraph eq 'slabitemtime')) {
530 $output = time_scale('data',$output); ;
532 print "$dsrc->{name}.value $output\n";
541 #### Subroutines for printing out config information for graphs ####
545 #### This subroutine does the bulk printing the config info per graph ####
551 if ($plugin eq 'memory') {
552 @subgraphs = ('slabchnks');
553 foreach my $slabid (sort{$a <=> $b} keys %chnks) {
554 print_submulti_config($slabid,$plugin,@subgraphs);
556 print_rootmulti_config($plugin);
557 } elsif ($plugin eq 'commands') {
558 @subgraphs = ('slabhits');
559 foreach my $slabid (sort{$a <=> $b} keys %chnks) {
560 print_submulti_config($slabid,$plugin,@subgraphs);
562 print_rootmulti_config($plugin);
563 } elsif ($plugin eq 'evictions') {
564 @subgraphs = ('slabevics','slabevictime');
565 foreach my $slabid (sort{$a <=> $b} keys %items) {
566 print_submulti_config($slabid,$plugin,@subgraphs);
568 print_rootmulti_config($plugin);
569 } elsif ($plugin eq 'items') {
570 @subgraphs = ('slabitems','slabitemtime');
571 foreach my $slabid (sort{$a <=> $b} keys %items) {
572 print_submulti_config($slabid,$plugin,@subgraphs);
574 print_rootmulti_config($plugin);
576 print_root_config($plugin);
583 #### This subroutine is for the config info for sub multigraph graphs created via the multigraph plugin ####
586 sub print_submulti_config {
587 my ($slabid,$plugin,@subgraphs) = (@_);
588 my ($slabitems,$slabchnks) = undef;
590 foreach my $sgraph (@subgraphs) {
592 my $graph = $graphs{$sgraph};
594 my %graphconf = %{$graph->{config}};
596 print "multigraph memcached_$plugin.$sgraph\_$slabid\n";
598 while ( my ($key, $value) = each(%graphconf)) {
599 if ($key eq 'title') {
600 print "graph_$key $value" . "$slabid" . " ($chnks{$slabid}->{chunk_size} Bytes)\n";
601 } elsif (($key eq 'vlabel') && (($sgraph eq 'slabevictime') || ($sgraph eq 'slabitemtime'))) {
602 $value = time_scale('config',$value);
603 print "graph_$key $value\n";
605 print "graph_$key $value\n";
609 foreach my $dsrc (@{$graph->{datasrc}}) {
610 my %datasrc = %$dsrc;
611 while ( my ($key, $value) = each(%datasrc)) {
612 next if ($key eq 'name');
613 print "$dsrc->{name}.$key $value\n";
622 #### This subroutine is for the config info for root multigraph graphs which render on the main node page ####
625 sub print_rootmulti_config {
628 die 'Unknown Plugin Specified: ' . ($plugin ? $plugin : '') unless $graphs{$plugin};
630 my $graph = $graphs{$plugin};
632 my %graphconf = %{$graph->{config}};
634 print "multigraph memcached_$plugin\n";
636 while ( my ($key, $value) = each(%graphconf)) {
637 print "graph_$key $value\n";
640 foreach my $dsrc (@{$graph->{datasrc}}) {
641 my %datasrc = %$dsrc;
642 while ( my ($key, $value) = each(%datasrc)) {
643 next if ($key eq 'name');
644 print "$dsrc->{name}.$key $value\n";
652 #### This subroutine is for the config info for non multigraph graphs which render on the main node page ####
655 sub print_root_config {
658 die 'Unknown Plugin Specified: ' . ($plugin ? $plugin : '') unless $graphs{$plugin};
660 my $graph = $graphs{$plugin};
662 my %graphconf = %{$graph->{config}};
664 print "graph memcached_$plugin\n";
666 while ( my ($key, $value) = each(%graphconf)) {
667 print "graph_$key $value\n";
670 foreach my $dsrc (@{$graph->{datasrc}}) {
671 my %datasrc = %$dsrc;
672 while ( my ($key, $value) = each(%datasrc)) {
673 next if ($key eq 'name');
674 print "$dsrc->{name}.$key $value\n";
682 #### This subroutine actually performs the data fetch for us ####
683 #### These commands do not lock up Memcache at all ####
687 my $s = IO::Socket::INET->new(
693 die "Error: Unable to Connect to $host\[:$port\]\n" unless $s;
695 print $s "stats\r\n";
697 while (my $line = <$s>) {
698 if ($line =~ /STAT\s(.+?)\s(\d+)/) {
699 my ($skey,$svalue) = ($1,$2);
700 $stats{$skey} = $svalue;
702 last if $line =~ /^END/;
705 print $s "stats settings\r\n";
707 while (my $line = <$s>) {
708 if ($line =~ /STAT\s(.+?)\s(\d+)/) {
709 my ($skey,$svalue) = ($1,$2);
710 $stats{$skey} = $svalue;
712 last if $line =~ /^END/;
715 print $s "stats slabs\r\n";
717 while (my $line = <$s>) {
718 if ($line =~ /STAT\s(\d+):(.+)\s(\d+)/) {
719 my ($slabid,$slabkey,$slabvalue) = ($1,$2,$3);
720 $chnks{$slabid}->{$slabkey} = $slabvalue;
722 last if $line =~ /^END/;
725 print $s "stats items\r\n";
727 while (my $line = <$s>) {
728 if ($line =~ /STAT\sitems:(\d+):(.+?)\s(\d+)/) {
729 my ($itemid,$itemkey,$itemvalue) = ($1,$2,$3);
730 $items{$itemid}->{$itemkey} = $itemvalue;
732 last if $line =~ /^END/;
737 #### This subroutine is to help manage the time_scale settings for the graph
741 my ($configopt,$origvalue) = (@_);
744 if ($configopt eq 'config') {
745 if ($timescale == 1) {
746 $value = "Seconds" . $origvalue;
747 } elsif ($timescale == 2) {
748 $value = "Minutes" . $origvalue;
749 } elsif (($timescale == 3) || ($timescale > 4) || (!defined($timescale))) {
750 $value = "Hours" . $origvalue;
751 } elsif ($timescale == 4) {
752 $value = "Days" . $origvalue;
754 } elsif ($configopt eq 'data') {
755 if ($timescale == 1) {
756 $value = sprintf("%02.2f", $origvalue / 1);
757 } elsif ($timescale == 2) {
758 $value = sprintf("%02.2f", $origvalue / 60);
759 } elsif (($timescale == 3) || ($timescale > 4) || (!defined($timescale))) {
760 $value = sprintf("%02.2f", $origvalue / 3600);
761 } elsif ($timescale == 4) {
762 $value = sprintf("%02.2f", $origvalue / 86400);
765 die "Unknown time_scale option given: either [config/data]\n";