October 23, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

A Picture is Worth a Thousand Lines of Code...

  • August 25, 2005
  • By Brad Lhotsky
  • Send Email »
  • More Articles »

Drawing Pretty Graphs

With your data securely stored in the RRDs, you can draw pretty pictures. I use two functions to draw the graphs. The first actually calls the RRDs::graph() function with the colors and layout. The second calls the first function to draw the graphs for each host and time period.

Setting up the graphs

After spending time setting up the databases, it's always rewarding to spend some time laying out the graph's visual elements. A detailed explanation of the options to pass to the graph function are covered in the excellent documentation accompanying the RRDtool distribution. See the man page for the RRDgraph tool for details.

my $IMGDIR = '/var/rrd/myapp/img';
sub graph {
   my ($host,$type,$start,$rrd) = @_;
   my @opts = (
      '--color', 'BACK#CCCCCC',      # Background Color
      '--color', 'SHADEA#FFFFFF',    # Left and Top Border Color
      '--color', 'CANVAS#000000',    # Canvas (Grid Background)
      '--color', 'GRID#333333',      # Grid Line Color
      '--color', 'MGRID#CCCCCC',     # Major Grid Line Color
      '--color', 'FONT#000000',      # Font Color
      '--color', 'ARROW#FF0000',     # Arrow Color for X/Y Axis
      '--color', 'FRAME#000000',     # Canvas Frame Color
      #
      # Set the labels
      '--title', "traffic for $host [$type]",    # Top
      '--vertical-label', 'bytes',               # Y-Axis Label
      #
      # Tell the graphing function how far back to go
      '--start', $start,
      '--step', 150,
      #
      # Extract data from the RRD
      "DEF:in=$rrd:in:AVERAGE",
      "DEF:out=$rrd:out:AVERAGE",
      "CDEF:rin=in,-1,*",
      "AREA:out#6666ff:outbound",
      "AREA:rin#99ccff:inbound",
      "HRULE:0#0000FF"
   );
   my $image = "$IMGDIR/$host-$type.gif";
   RRDs::graph $image, @opts;
   my $err = RRDs::error;
   warn "graphing $host ($type) failed: $errn" if $err;
}

An interesting problem that MRTG solves by using a combination of "AREA" and "LINE" plots occurs when two data points utilizing the "AREA" plotting end up swapping places. In other words, when the foreground "AREA" ends up with data points that trounce the background "AREA," it becomes impossible to read that background "AREA." A long time ago, I saw another excellent solution to the problem in a University born, Netflow graphing utility. By inflecting the "inbound" traffic under the X-Axis, you can use two "AREA" plots without worrying about overlap with some very interesting visual effects to boot!

How do you accomplish that? Well, obviously, you multiply the inbound traffic by -1. But what about all the data that's been collecting in your RRD with POSITIVE values for inbound traffic? Relax; RRDtool can use "CDEF"s to manipulate the data using "Reverse Polish Notation"—RPN—so you don't have to modify anything!

The "DEF" lines in the RRDgraph options allow you to pull data out of the RRD and use an aggregation function on those data points. This is used when you attempt to draw a graph where 1 pixel needs to represent more than 1 finite data point in the RRD. To modify the data, you have to extract it using a "DEF" line.

"DEF:in=$rrd:in:AVERAGE"

What this is doing is creating a variable called "in" that equals the average value of the "in" DS (data source) in the RRD "$rrd" (this variable is interpolated by perl to the filename). You do the same thing for the outbound traffic measurement. To inflect the in data point over the X-Axis, you have to multiply its value by -1. This is easily accomplished by using the "CDEF" and some RPN.

RPN

RPN works like a stack. You move from left to right, you get two values, then an operator, and that becomes a single value. Here's an example:

1,2,+,4,* = 12
Step By Step
Read Value Operation Stack After Operation
1 put 1 onto the stack 1
2 put 2 onto the stack 2
1
+ Operator. Add the values on the stack 3
4 put 4 onto the stack 4
3
* Operator. Multiply the values on the stack 12

It's actually rather simple after you train your brain to slow down. So, you use a "CDEF" and some RPN to inflect the inbound traffic on the X-Axis and in so doing, create a new variable "rin". You'll use the "rin" variable later in your "AREA" definition as the source of its values.

"CDEF:rin=in,-1,*"

You use your variable "in" that you defined as coming from the the "in" data source for a particular RRD. Then, you multiply its value at any given point by -1 by pushing a -1 onto the stack, immediately followed by a '*'.

The only thing left to decide is the type of graph element you want to use. For two simple data points using the inflection method for one of the values, it's fairly straightforward and visually appealing to use "AREA" definitions. RRDtool provides a host of other graph elements and excellent documentation on them. You may have to play with the aggregate functions for the "DEF"s and the order of the graph element declarations to get easy-to-read, non-overlapping visual data representations. Tuning the graphs is half the fun!

Once you're done, you've got the function set up to draw all your graphs!





Page 3 of 4



Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Sitemap | Contact Us

Rocket Fuel