.\" Automatically generated by Pod::Man 4.14 (Pod::Simple 3.42) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" ======================================================================== .\" .IX Title "RRDTool::OO 3pm" .TH RRDTool::OO 3pm "2022-06-17" "perl v5.34.0" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" RRDTool::OO \- Object\-oriented interface to RRDTool .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 1 \& use RRDTool::OO; \& \& # Constructor \& my $rrd = RRDTool::OO\->new( \& file => "myrrdfile.rrd" ); \& \& # Create a round\-robin database \& $rrd\->create( \& step => 1, # one\-second intervals \& data_source => { name => "mydatasource", \& type => "GAUGE" }, \& archive => { rows => 5 }); \& \& # Update RRD with sample values, use current time. \& for(1..5) { \& $rrd\->update($_); \& sleep(1); \& } \& \& # Start fetching values from one day back, \& # but skip undefined ones first \& $rrd\->fetch_start(); \& $rrd\->fetch_skip_undef(); \& \& # Fetch stored values \& while(my($time, $value) = $rrd\->fetch_next()) { \& print "$time: ", \& defined $value ? $value : "[undef]", "\en"; \& } \& \& # Draw a graph in a PNG image \& $rrd\->graph( \& image => "mygraph.png", \& vertical_label => \*(AqMy Salary\*(Aq, \& start => time() \- 10, \& draw => { \& type => "area", \& color => \*(Aq0000FF\*(Aq, \& legend => "Salary over Time", \& } \& ); \& \& # Same using rrdtool\*(Aqs graphv \& $rrd\->graphv( \& image => "mygraph.png", \& [...] \& }; .Ve .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\f(CW\*(C`RRDTool::OO\*(C'\fR is an object-oriented interface to Tobi Oetiker's round robin database tool \fIrrdtool\fR. It uses \fIrrdtool\fR's \&\f(CW\*(C`RRDs\*(C'\fR module to get access to \fIrrdtool\fR's shared library. .PP \&\f(CW\*(C`RRDTool::OO\*(C'\fR tries to marry \fIrrdtool\fR's database engine with the dwimminess and whipuptitude Perl programmers take for granted. Using \&\f(CW\*(C`RRDTool::OO\*(C'\fR abstracts away implementation details of the \s-1RRD\s0 engine, uses easy to memorize named parameters and sets meaningful defaults for parameters not needed in simple cases. For the experienced user, however, it provides full access to \&\fIrrdtool\fR's \s-1API\s0 (if you find a feature that's not implemented, let me know). .SS "\s-1FUNCTIONS\s0" .IX Subsection "FUNCTIONS" .IP "\fImy \f(CI$rrd\fI = RRDTool::OO\->new( file => \f(CI$file\fI )\fR" 4 .IX Item "my $rrd = RRDTool::OO->new( file => $file )" The constructor hooks up with an existing \s-1RRD\s0 database file \f(CW$file\fR, but doesn't create a new one if none exists. That's what the \f(CW\*(C`create()\*(C'\fR methode is for. Returns a \f(CW\*(C`RRDTool::OO\*(C'\fR object, which can be used to get access to the following methods. .IP "\fI\f(CI$rrd\fI\->create( ... )\fR" 4 .IX Item "$rrd->create( ... )" Creates a new round robin database (\s-1RRD\s0). A \s-1RRD\s0 consists of one or more data sources and one or more archives: .Sp .Vb 5 \& $rrd\->create( \& step => 60, \& data_source => { name => "mydatasource", \& type => "GAUGE" }, \& archive => { rows => 5 }); .Ve .Sp This defines a \s-1RRD\s0 database with a step rate of 60 seconds in between primary data points. Additionally, the \s-1RRD\s0 start time can be specified by specifying a \f(CW\*(C`start\*(C'\fR parameter. .Sp It also sets up one data source named \f(CW\*(C`my_data_source\*(C'\fR of type \f(CW\*(C`GAUGE\*(C'\fR, telling \fIrrdtool\fR to use values of data samples as-is, without additional trickery. .Sp And it creates a single archive with a 1:1 mapping between primary data points and archive points, with a capacity to hold five data points. .Sp The \s-1RRD\s0's \f(CW\*(C`step\*(C'\fR parameter is optional, and will be set to 300 seconds by \fIrrdtool\fR by default. .Sp In addition to the mandatory settings for \f(CW\*(C`name\*(C'\fR and \f(CW\*(C`type\*(C'\fR, \&\f(CW\*(C`data_source\*(C'\fR parameter takes the following optional parameters: \&\f(CW\*(C`min\*(C'\fR (minimum input, defaults to \f(CW\*(C`U\*(C'\fR), \&\f(CW\*(C`max\*(C'\fR (maximum input, defaults to \f(CW\*(C`U\*(C'\fR), \&\f(CW\*(C`heartbeat\*(C'\fR (defaults to twice the \s-1RRD\s0's step rate). .Sp Archives expect at least one parameter, \f(CW\*(C`rows\*(C'\fR indicating the number of data points the archive is configured to hold. If nothing else is set, \fIrrdtool\fR will store primary data points 1:1 in the archive. .Sp If you want to combine several primary data points into one archive point, specify values for \&\f(CW\*(C`cpoints\*(C'\fR (the number of points to combine) and \f(CW\*(C`cfunc\*(C'\fR (the consolidation function) explicitly: .Sp .Vb 8 \& $rrd\->create( \& step => 60, \& data_source => { name => "mydatasource", \& type => "GAUGE" }, \& archive => { rows => 5, \& cpoints => 10, \& cfunc => \*(AqAVERAGE\*(Aq, \& }); .Ve .Sp This will collect 10 data points to form one archive point, using the calculated average, as indicated by the parameter \f(CW\*(C`cfunc\*(C'\fR (Consolidation Function, \s-1CF\s0). Other options for \f(CW\*(C`cfunc\*(C'\fR are \&\f(CW\*(C`MIN\*(C'\fR, \f(CW\*(C`MAX\*(C'\fR, and \f(CW\*(C`LAST\*(C'\fR. .Sp If you're defining multiple data sources or multiple archives, just provide them in this manner: .Sp .Vb 10 \& # Define the RRD \& my $rc = $rrd\->create( \& step => 60, \& data_source => { name => \*(Aqload1\*(Aq, \& type => \*(AqGAUGE\*(Aq, \& }, \& data_source => { name => \*(Aqload2\*(Aq, \& type => \*(AqGAUGE\*(Aq, \& }, \& archive => { rows => 5, \& cpoints => 10, \& cfunc => \*(AqAVERAGE\*(Aq, \& }, \& archive => { rows => 5, \& cpoints => 10, \& cfunc => \*(AqMAX\*(Aq, \& }, \& ); .Ve .IP "\fI\f(CI$rrd\fI\->update( ... ) \fR" 4 .IX Item "$rrd->update( ... ) " Update the round robin database with a new data sample, consisting of a value and an optional time stamp. If called with a single parameter, like in .Sp .Vb 1 \& $rrd\->update($value); .Ve .Sp then the current timestamp and the defined \f(CW$value\fR will be used. If \f(CW\*(C`update\*(C'\fR is called with a named parameter list like in .Sp .Vb 1 \& $rrd\->update(time => $time, value => $value); .Ve .Sp then the given timestamp \f(CW$time\fR is used along with the given value \&\f(CW$value\fR. .Sp When updating multiple data sources, use the \f(CW\*(C`values\*(C'\fR parameter (instead of \f(CW\*(C`value\*(C'\fR) and pass an arrayref: .Sp .Vb 1 \& $rrd\->update(time => $time, values => [$val1, $val2, ...]); .Ve .Sp This way, \fIrrdtool\fR expects you to pass in the data values in exactly the same order as the data sources were defined in the \&\f(CW\*(C`create\*(C'\fR method. If that's not the case, then the \f(CW\*(C`values\*(C'\fR parameter also accepts a hashref, mapping data source names to values: .Sp .Vb 3 \& $rrd\->update(time => $time, \& values => { $dsname1 => $val1, \& $dsname2 => $val2, ...}); .Ve .Sp \&\f(CW\*(C`RRDTool::OO\*(C'\fR will transform this automagically into \f(CW\*(C`RRDTool\*(Aqs\*(C'\fR \fItemplate\fR syntax. .IP "\fI\f(CI$rrd\fI\->updatev( ... )\fR" 4 .IX Item "$rrd->updatev( ... )" This is identical to \f(CW\*(C`update\*(C'\fR, but uses rrdtool's updatev function internally. The only difference is when using the \f(CW\*(C`print_results\*(C'\fR method described below, which then contains additional information. .IP "\fI\f(CI$rrd\fI\->fetch_start( ... )\fR" 4 .IX Item "$rrd->fetch_start( ... )" Initializes the iterator to fetch data from the \s-1RRD.\s0 This works nicely without any parameters if your archives are using a single consolidation function (e.g. \f(CW\*(C`MAX\*(C'\fR). If there's several archives in the \s-1RRD\s0 using different consolidation functions, you have to specify which one you want: .Sp .Vb 1 \& $rrd\->fetch_start(cfunc => "MAX"); .Ve .Sp Other options for \f(CW\*(C`cfunc\*(C'\fR are \f(CW\*(C`MIN\*(C'\fR, \f(CW\*(C`AVERAGE\*(C'\fR, and \f(CW\*(C`LAST\*(C'\fR. .Sp \&\f(CW\*(C`fetch_start\*(C'\fR features a number of optional parameters: \&\f(CW\*(C`start\*(C'\fR, \f(CW\*(C`end\*(C'\fR and \f(CW\*(C`resolution\*(C'\fR. .Sp If the \f(CW\*(C`start\*(C'\fR time parameter is omitted, the fetch starts 24 hours before the end of the archive. Also, an \f(CW\*(C`end\*(C'\fR time can be specified: .Sp .Vb 2 \& $rrd\->fetch_start(start => time()\-10*60, \& end => time()); .Ve .Sp The third optional parameter, \&\f(CW\*(C`resolution\*(C'\fR defaults to the highest resolution available and can be set to a value in seconds, specifying the time interval between the data samples extracted from the \s-1RRD.\s0 See the \f(CW\*(C`rrdtool fetch\*(C'\fR manual page for details. .Sp Development note: The current implementation fetches \fIall\fR values from the \s-1RRA\s0 in one swoop and caches them in memory. This might change in the future, to cache only the last timestamp and keep fetching from the \s-1RRD\s0 with every \f(CW\*(C`fetch_next()\*(C'\fR call. .IP "\fI\f(CI$rrd\fI\->\f(BIfetch_skip_undef()\fI\fR" 4 .IX Item "$rrd->fetch_skip_undef()" \&\fIrrdtool\fR doesn't remember the time the first data sample went into the archive. So if you run a \fIrrdtool fetch\fR with a start time of 24 hours ago and you've only submitted a couple of samples to the archive, you'll see many \f(CW\*(C`undef\*(C'\fR values. .Sp Starting from the current iterator position (or at the specified \f(CW\*(C`start\*(C'\fR time immediately after a \f(CW\*(C`fetch_start()\*(C'\fR), \f(CW\*(C`fetch_skip_undef()\*(C'\fR will skip all \f(CW\*(C`undef\*(C'\fR values in the \s-1RRA\s0 and positions the iterator right before the first defined value. If all values in the \s-1RRA\s0 are undefined, the a following \f(CW\*(C`$rrd\->fetch_next()\*(C'\fR will return \f(CW\*(C`undef\*(C'\fR. .IP "\fI($time, \f(CI$value\fI, ...) = \f(CI$rrd\fI\->\f(BIfetch_next()\fI\fR" 4 .IX Item "($time, $value, ...) = $rrd->fetch_next()" Gets the next row from the \s-1RRD\s0 iterator, initialized by a previous call to \f(CW\*(C`$rrd\->fetch_start()\*(C'\fR. Returns the time of the archive point along with all values as a list. .Sp Note that there might be more than one value coming back from \f(CW\*(C`fetch_next\*(C'\fR if the \s-1RRA\s0 defines more than one datasource): .Sp .Vb 1 \& I<($time, @values_of_all_ds) = $rrd\-Efetch_next()> .Ve .Sp It is not possible to fetch only a specific datasource, as rrdtool doesn't provide this. .IP "\fI($time, \f(CI$value\fI, ...) = \f(CI$rrd\fI\->\f(BIfetch_next()\fI\fR" 4 .IX Item "($time, $value, ...) = $rrd->fetch_next()" .PD 0 .IP "\fI\f(CI$rrd\fI\->graph( ... )\fR" 4 .IX Item "$rrd->graph( ... )" .PD If there's only one data source in the \s-1RRD,\s0 drawing a nice graph in an image file on disk is as easy as .Sp .Vb 8 \& $rrd\->graph( \& image => $image_file_name, \& vertical_label => \*(AqMy Salary\*(Aq, \& draw => { thickness => 2, \& color => \*(AqFF0000\*(Aq, \& legend => \*(AqSalary over Time\*(Aq, \& }, \& ); .Ve .Sp This will assume a start time of 24 hours before now and an end time of now. Specify \f(CW\*(C`start\*(C'\fR and \f(CW\*(C`end\*(C'\fR explicitly to be clear: .Sp .Vb 10 \& $rrd\->graph( \& image => $image_file_name, \& vertical_label => \*(AqMy Salary\*(Aq, \& start => time() \- 24*3600, \& end => time(), \& draw => { thickness => 2, \& color => \*(AqFF0000\*(Aq, \& legend => \*(AqSalary over Time\*(Aq, \& }, \& ); .Ve .Sp As always, \f(CW\*(C`RRDTool::OO\*(C'\fR will pick reasonable defaults for parameters not specified. The values for data source and consolidation function default to the first values it finds in the \s-1RRD.\s0 If there are multiple datasources in the \s-1RRD\s0 or multiple archives with different values for \f(CW\*(C`cfunc\*(C'\fR, just specify explicitly which one to draw: .Sp .Vb 9 \& $rrd\->graph( \& image => $image_file_name, \& vertical_label => \*(AqMy Salary\*(Aq, \& draw => { \& thickness => 2, \& color => \*(AqFF0000\*(Aq, \& dsname => "load", \& cfunc => \*(AqMAX\*(Aq}, \& ); .Ve .Sp If \f(CW\*(C`draw\*(C'\fR doesn't define a \f(CW\*(C`type\*(C'\fR, it defaults to \f(CW"line"\fR. If you don't want to define a type (because the graph shouldn't be drawn), use \f(CW\*(C`type => "hidden"\*(C'\fR. Other values are \f(CW"area"\fR for solid colored areas. The \f(CW"stack"\fR type (for graphical values stacked on top of each other) has been deprecated sind rrdtool\-1.2, but RRDTool::OO still supports it by transforming it into an 'area' type with a 'stack' option. .Sp And you can certainly have more than one graph in the picture: .Sp .Vb 10 \& $rrd\->graph( \& image => $image_file_name, \& vertical_label => \*(AqMy Salary\*(Aq, \& draw => { \& type => \*(Aqarea\*(Aq, \& color => \*(AqFF0000\*(Aq, # red area \& dsname => "load", \& cfunc => \*(AqMAX\*(Aq}, \& draw => { \& type => \*(Aqarea\*(Aq, \& stack => 1, \& color => \*(Aq00FF00\*(Aq, # a green area stacked on top of the red one \& dsname => "load", \& cfunc => \*(AqAVERAGE\*(Aq}, \& ); .Ve .Sp Graphs may assemble data from different \s-1RRD\s0 files. Just specify which file you want to draw the data from, using \f(CW\*(C`draw\*(C'\fR: .Sp .Vb 10 \& $rrd\->graph( \& image => $image_file_name, \& vertical_label => \*(AqNetwork Traffic\*(Aq, \& draw => { \& file => "file1.rrd", \& legend => "First Source", \& }, \& draw => { \& file => "file2.rrd", \& type => \*(Aqarea\*(Aq, \& stack => 1, \& color => \*(Aq00FF00\*(Aq, # a green area stacked on top of the red one \& dsname => "load", \& legend => "Second Source", \& cfunc => \*(AqAVERAGE\*(Aq \& }, \& ); .Ve .Sp If a \f(CW\*(C`file\*(C'\fR parameter is specified per \f(CW\*(C`draw\*(C'\fR, the defaults for \f(CW\*(C`dsname\*(C'\fR and \f(CW\*(C`cfunc\*(C'\fR are fetched from this file, not from the file that's attached to the \f(CW\*(C`RRDTool::OO\*(C'\fR object \f(CW$rrd\fR used. .Sp Graphs may also consist of algebraic calculations of previously defined graphs. In this case, graphs derived from real data sources need to be named, so that subsequent \f(CW\*(C`cdef\*(C'\fR definitions can refer to them and calculate new graphs, based on the previously defined graph: .Sp .Vb 10 \& $rrd\->graph( \& image => $image_file_name, \& vertical_label => \*(AqNetwork Traffic\*(Aq, \& draw => { \& type => \*(Aqline\*(Aq, \& color => \*(AqFF0000\*(Aq, # red line \& dsname => \*(Aqload\*(Aq, \& name => \*(Aqfirstgraph\*(Aq, \& legend => \*(AqUnmodified Load\*(Aq, \& }, \& draw => { \& type => \*(Aqline\*(Aq, \& color => \*(Aq00FF00\*(Aq, # green line \& cdef => "firstgraph,2,*", \& legend => \*(AqLoad Doubled Up\*(Aq, \& }, \& ); .Ve .Sp Note that the second \f(CW\*(C`draw\*(C'\fR doesn't refer to a datasource \f(CW\*(C`dsname\*(C'\fR (nor does it fall back to the default data source), but defines a \f(CW\*(C`cdef\*(C'\fR, performing calculations on a previously defined draw named \f(CW\*(C`firstgraph\*(C'\fR. The calculation is specified using RRDTool's reverse polish notation, where instructions are separated by commas (\f(CW"firstgraph,2,*"\fR simply multiplies \f(CW\*(C`firstgraph\*(C'\fR's values by 2). .Sp On a global level, in addition to the \f(CW\*(C`vertical_label\*(C'\fR parameter shown in the examples above, \f(CW\*(C`graph\*(C'\fR offers a plethora of parameters: .Sp \&\f(CW\*(C`vertical_label\*(C'\fR, \&\f(CW\*(C`title\*(C'\fR, \&\f(CW\*(C`start\*(C'\fR, \&\f(CW\*(C`end\*(C'\fR, \&\f(CW\*(C`x_grid\*(C'\fR, \&\f(CW\*(C`y_grid\*(C'\fR, \&\f(CW\*(C`alt_y_grid\*(C'\fR, \&\f(CW\*(C`no_minor\*(C'\fR, \&\f(CW\*(C`alt_y_mrtg\*(C'\fR, \&\f(CW\*(C`alt_autoscale\*(C'\fR, \&\f(CW\*(C`alt_autoscale_max\*(C'\fR, \&\f(CW\*(C`base\*(C'\fR, \&\f(CW\*(C`units_exponent\*(C'\fR, \&\f(CW\*(C`units_length\*(C'\fR, \&\f(CW\*(C`width\*(C'\fR, \&\f(CW\*(C`height\*(C'\fR, \&\f(CW\*(C`interlaced\*(C'\fR, \&\f(CW\*(C`imginfo\*(C'\fR, \&\f(CW\*(C`imgformat\*(C'\fR, \&\f(CW\*(C`overlay\*(C'\fR, \&\f(CW\*(C`unit\*(C'\fR, \&\f(CW\*(C`lazy\*(C'\fR, \&\f(CW\*(C`rigid\*(C'\fR, \&\f(CW\*(C`lower_limit\*(C'\fR, \&\f(CW\*(C`upper_limit\*(C'\fR, \&\f(CW\*(C`logarithmic\*(C'\fR, \&\f(CW\*(C`color\*(C'\fR, \&\f(CW\*(C`no_legend\*(C'\fR, \&\f(CW\*(C`only_graph\*(C'\fR, \&\f(CW\*(C`force_rules_legend\*(C'\fR, \&\f(CW\*(C`title\*(C'\fR, \&\f(CW\*(C`step\*(C'\fR. .Sp Some options (e.g. \f(CW\*(C`alt_y_grid\*(C'\fR) don't expect values, they need to be specified like .Sp .Vb 1 \& alt_y_grid => undef .Ve .Sp in order to be passed properly to RRDTool. .Sp The \f(CW\*(C`color\*(C'\fR option expects a reference to a hash with various settings for the different graph areas: \&\f(CW\*(C`back\*(C'\fR (background), \&\f(CW\*(C`canvas\*(C'\fR, \&\f(CW\*(C`shadea\*(C'\fR (left/top border), \&\f(CW\*(C`shadeb\*(C'\fR (right/bottom border), \&\f(CW\*(C`grid\*(C'\fR, \f(CW\*(C`mgrid\*(C'\fR major grid, \&\f(CW\*(C`font\*(C'\fR, \&\f(CW\*(C`frame\*(C'\fR and \f(CW\*(C`arrow\*(C'\fR: .Sp .Vb 8 \& $rrd\->graph( \& ... \& color => { back => \*(Aq#0e0e0e\*(Aq, \& arrow => \*(Aq#ff0000\*(Aq, \& canvas => \*(Aq#eebbbb\*(Aq, \& }, \& ... \& ); .Ve .Sp Fonts for various graph elements may be specified in \f(CW\*(C`font\*(C'\fR blocks, which must either name a TrueType font file or a PDF/Postscript font name. You may optionally specify a size and element name (defaults to \s-1DEFAULT,\s0 which to \s-1RRD\s0 means "use this font for everything). Example: .Sp .Vb 5 \& font => { \& name => "/usr/openwin/lib/X11/fonts/TrueType/GillSans.ttf", \& size => 16, \& element => "title" \& } .Ve .Sp Please check the RRDTool documentation for a detailed description on what each option is used for: .Sp .Vb 1 \& http://people.ee.ethz.ch/~oetiker/webtools/rrdtool/manual/rrdgraph.html .Ve .Sp Sometimes it's useful to print max, min or average values of a given graph at the bottom of the chart or to \s-1STDOUT.\s0 That's what \&\f(CW\*(C`gprint\*(C'\fR and \f(CW\*(C`print\*(C'\fR options are for. They are printing variables which are defined as \f(CW\*(C`vdef\*(C'\fRs somewhere else: .Sp .Vb 8 \& $rrd\->graph( \& image => $image_file_name, \& # Real graph \& draw => { \& name => "first_draw", \& dsname => "load", \& cfunc => \*(AqMAX\*(Aq \& }, \& \& # vdef for calculating average of real graph \& draw => { \& type => "hidden", \& name => "average_of_first_draw", \& vdef => "first_draw,AVERAGE" \& }, \& \& gprint => { \& draw => \*(Aqaverage_of_first_draw\*(Aq, \& format => \*(AqAverage=%lf\*(Aq, \& }, \& ); .Ve .Sp The \f(CW\*(C`vdef\*(C'\fR performs a calculation, specified in \s-1RPN\s0 notation, on a real graph, which it refers to. It uses a hidden graph for this. .Sp The \f(CW\*(C`gprint\*(C'\fR option then refers to the \f(CW\*(C`vdef\*(C'\fR virtual graph and prints \&\*(L"Average=x.xx\*(R" at the bottom of the graph, showing what the average value of graph \f(CW\*(C`first_draw\*(C'\fR is. .Sp To write comments to the graph (like gprints, but with no associated \&\s-1RRD\s0 data source) use \f(CW\*(C`comment\*(C'\fR, like this: .Sp .Vb 8 \& $rrd\->graph( \& image => $image_file_name, \& draw => { \& name => "first_draw", \& dsname => "load", \& cfunc => \*(AqMAX\*(Aq}, \& comment => "Remember, 83% of all statistics are made up", \& ); .Ve .Sp Multiple comment lines can be specified in a single comment specification like this: .Sp .Vb 3 \& comment => [ "All the king\*(Aqs horses and all the king\*(Aqs men\e\en", \& "couldn\*(Aqt put Humpty together again.\e\en", \& ], .Ve .Sp Vertical rules (lines) may be placed into the graph by using a \f(CW\*(C`vrule\*(C'\fR block like so: .Sp .Vb 1 \& vrule => { time => time()\-3600, } .Ve .Sp These can be useful for indicating when the most recent day on the graph started, for example. .Sp vrules can have a color specification (they default to black) and also an optional legend string specified: .Sp .Vb 4 \& vrule => { time => $first_thing_today, \& color => "#0000ff", \& legend => "When we crossed midnight" \& }, .Ve .Sp hrules can have a color specification (they default to black) and also an optional legend string specified: .Sp .Vb 4 \& hrule => { value => $numeric_value, \& color => "#0000ff", \& legend => "a static line at your value" \& }, .Ve .Sp Horizontal rules can be added by using a \f(CW\*(C`line\*(C'\fR block like in .Sp .Vb 7 \& line => { \& value => "fixed num value or draw name", \& color => "#0000ff", \& legend => "a blue horizontal line", \& width => 120, \& stack => 1, \& } .Ve .Sp If instead of a horizontal line, a rectangular area is supposed to be added to the graph, use an \f(CW\*(C`area\*(C'\fR block: .Sp .Vb 6 \& area => { \& value => "fixed num value or draw name", \& color => "#0000ff", \& legend => "a blue horizontal line", \& stack => 1, \& } .Ve .Sp The \f(CW\*(C`graph\*(C'\fR method can also generate tickmarks (vertical lines) for every defined value, using the \f(CW\*(C`tick\*(C'\fR option: .Sp .Vb 6 \& tick => { \& draw => "drawname", \& color => "#0000ff", \& legend => "a blue horizontal line", \& stack => 1, \& } .Ve .Sp The graph may be shifted relative to the time axis: .Sp .Vb 4 \& shift => { \& draw => "drawname", \& offset => $offset, \& } .Ve .IP "\fI\f(CI$rrd\fI\->graphv( ... )\fR" 4 .IX Item "$rrd->graphv( ... )" This is identical to \f(CW\*(C`graph\*(C'\fR, but uses rrdtool's graphv function internally. The only difference is when using the \f(CW\*(C`print_results\*(C'\fR method described below, which then contains additional information. Be aware that rrdtool 1.3 is required for \f(CW\*(C`graphv\*(C'\fR to work. .IP "\fI\f(CI$rrd\fI\->\f(BIdump()\fI\fR" 4 .IX Item "$rrd->dump()" \&\fIAvailable as of rrdtool 1.0.49\fR. .Sp Dumps the \s-1RRD\s0 in \s-1XML\s0 format to \s-1STDOUT.\s0 If you want to dump it into a file instead, do this: .Sp .Vb 1 \& my $pid; \& \& unless ($pid = open DUMP, "\-|") { \& die "Can\*(Aqt fork: $!" unless defined $pid; \& $rrd\->dump(); \& exit 0; \& } \& \& waitpid($pid, 0); \& \& open OUT, ">out"; \& print OUT $_ for ; \& close OUT; .Ve .IP "\fImy \f(CI$hashref\fI = \f(CI$rrd\fI\->xport(...)\fR" 4 .IX Item "my $hashref = $rrd->xport(...)" Feed a perl structure with \s-1RRA\s0 data (Cf. rrdxport man page). .Sp .Vb 10 \& my $results = $rrd\->xport( \& start => $start_time, \& end => $end_time , \& step => $step, \& def => [{ \& vname => "load1_vname", \& file => "foo", \& dsname => "load1", \& cfunc => "MAX", \& }, \& { \& vname => "load2_vname", \& file => "foo", \& dsname => "load2", \& cfunc => "MIN", \& }], \& \& cdef => [{ \& vname => "load2_vname_multiply", \& rpn => "load2_vname,2,*", \& }], \& \& xport => [{ \& vname => "load1_vname", \& legend => "it_s_gonna_be_legend_", \& }, \& { \& vname => "load2_vname", \& legend => "wait_for_it", \& }, \& { \& vname => "load2_vname_multiply", \& legend => "_\|_\|_dary", \& }], \& ); \& \& my $data = $results\->{data}; \& my $metadata = $results\->{meta}; \& \& print "### METADATA ###\en"; \& print "StartTime: $metadata\->{start}\en"; \& print "EndTime: $metadata\->{end}\en"; \& print "Step: $metadata\->{step}\en"; \& print "Number of data columns: $metadata\->{columns}\en"; \& print "Number of data rows: $metadata\->{rows}\en"; \& print "Legend: ", join(", ", @{$metadata\->{legend}}), "\en"; \& \& print "\en### DATA ###\en"; \& foreach my $entry (@$data) { \& my $entry_timestamp = shift(@$entry); \& print "[$entry_timestamp] ", join(" ", @$entry), "\en"; \& } .Ve .IP "\fImy \f(CI$hashref\fI = \f(CI$rrd\fI\->\f(BIinfo()\fI\fR" 4 .IX Item "my $hashref = $rrd->info()" Grabs the \s-1RRD\s0's meta data and returns it as a hashref, holding a map of parameter names and their values. .IP "\fImy \f(CI$time\fI = \f(CI$rrd\fI\->\f(BIfirst()\fI\fR" 4 .IX Item "my $time = $rrd->first()" Return the \s-1RRD\s0's first update time. .IP "\fImy \f(CI$time\fI = \f(CI$rrd\fI\->\f(BIlast()\fI\fR" 4 .IX Item "my $time = $rrd->last()" Return the \s-1RRD\s0's last update time. .ie n .IP "\fI\f(CI$rrd\fI\->restore(xml => ""file.xml"")\fR" 4 .el .IP "\fI\f(CI$rrd\fI\->restore(xml => ``file.xml'')\fR" 4 .IX Item "$rrd->restore(xml => file.xml)" \&\fIAvailable as of rrdtool 1.0.49\fR. .Sp Restore a \s-1RRD\s0 from a \f(CW\*(C`dump\*(C'\fR. The \f(CW\*(C`xml\*(C'\fR parameter specifies the name of the \s-1XML\s0 file containing the dump. If the optional flag \f(CW\*(C`range_check\*(C'\fR is set to a true value, \f(CW\*(C`restore\*(C'\fR will make sure the values in the RRAs do not exceed the limits defined for the different datasources: .Sp .Vb 1 \& $rrd\->restore(xml => "file.xml", range_check => 1); .Ve .IP "\fI\f(CI$rrd\fI\->tune( ... )\fR" 4 .IX Item "$rrd->tune( ... )" Alter a \s-1RRD\s0's data source configuration values: .Sp .Vb 2 \& # Set the heartbeat of the RRD\*(Aqs only datasource to 100 \& $rrd\->tune(heartbeat => 100); \& \& # Set the minimum of DS \*(Aqload\*(Aq to 1 \& $rrd\->tune(dsname => \*(Aqload\*(Aq, minimum => 1); \& \& # Set the maximum of DS \*(Aqload\*(Aq to 10 \& $rrd\->tune(dsname => \*(Aqload\*(Aq, maximum => 10); \& \& # Set the type of DS \*(Aqload\*(Aq to AVERAGE \& $rrd\->tune(dsname => \*(Aqload\*(Aq, type => \*(AqAVERAGE\*(Aq); \& \& # Set the name of DS \*(Aqload\*(Aq to \*(Aqload2\*(Aq \& $rrd\->tune(dsname => \*(Aqload\*(Aq, name => \*(Aqload2\*(Aq); .Ve .IP "\fI\f(CI$rrd\fI\->\f(BIerror_message()\fI\fR" 4 .IX Item "$rrd->error_message()" Return the message of the last error that occurred while interacting with \f(CW\*(C`RRDTool::OO\*(C'\fR. .SS "Aberrant behavior detection" .IX Subsection "Aberrant behavior detection" RRDTool supports aberrant behavior detection (\s-1ABD\s0), which takes a data source, stuffs its values into a special \s-1RRA,\s0 smoothes the data stream, tries to predict future values and triggers an alert if actual values are way off the predicted values. .PP Using a fairly elaborate algorithm not only allows it to find out if a data source produces a value that exceeds a certain fixed threshold. The algorithm constantly adapts its parameters to the input data and acts dynamically on slowly changing values. .PP The \f(CW\*(C`alpha\*(C'\fR parameter specifies the baseline and lies between 0 and 1. Values close to 1 specify that most recent values have the most weight on the prediction, whereas values close to 0 indicate that past values carry higher weight. .PP On top of that, \s-1ABD\s0 can deal with data input that displays continuously rising values (slope). The \f(CW\*(C`beta\*(C'\fR parameters, again between 0 and 1, specifies whether past values or more recent values carry the most weight. .PP And, furthermore, it deals with seasonal cycles, so it won't freak out if there's a daily peak at noon. The \f(CW\*(C`gamma\*(C'\fR parameter indicates this, if you don't specify it, it defaults to the value of \f(CW\*(C`alpha\*(C'\fR. .PP In the easiest case, an \s-1RRA\s0 with aberrant behavior detection can be created like .PP .Vb 8 \& # Create a round\-robin database \& $rrd\->create( \& step => 1, # one\-second intervals \& data_source => { name => "mydatasource", \& type => "GAUGE" }, \& hwpredict => { rows => 3600, \& }, \& ); .Ve .PP where \f(CW\*(C`alpha\*(C'\fR and \f(CW\*(C`beta\*(C'\fR default to 0.5, and the \f(CW\*(C`seasonal_period\*(C'\fR defaults to 1/5 of the rows number. .PP \&\f(CW\*(C`rows\*(C'\fR is the number of primary data points that are stored in the \s-1RRA\s0 before a wrap-around happens. Note that with \s-1ABD\s0 enabled, RRDTool won't consolidate the data from a data source before stuffing it into the \s-1HWPREDICT\s0 RRAs, as the whole point of \s-1ABD\s0 is to smooth unfiltered data and predict future values. .PP A violation happens if a new measured value falls outside of the prediction. If \f(CW\*(C`threshold\*(C'\fR or more violations happen within \&\f(CW\*(C`window_length\*(C'\fR, an error is reported to the \s-1FAILURES RRA.\s0 \&\f(CW\*(C`threshold\*(C'\fR defaults to 7, \f(CW\*(C`window_length\*(C'\fR to 9. .PP A more elaborate \s-1RRD\s0 could be defined as .PP .Vb 10 \& # Create a round\-robin database \& $rrd\->create( \& step => 1, # one\-second intervals \& data_source => { name => "mydatasource", \& type => "GAUGE" }, \& hwpredict => { rows => 3600, \& alpha => 0.1, \& beta => 0.1, \& gamma => 0.1, \& threshold => 7, \& window_length => 9, \& }, \& ); .Ve .PP If you want to peek under the hood (not that you need to, just for your entertainment), with the specification above, RRDTool::OO will create the following five RRAs according to the RRDtool specification and fill in these values: .PP .Vb 5 \& * RRA:HWPREDICT:rows:alpha:beta:seasonal_period:rra\-num \& * RRA:SEASONAL:seasonal period:gamma:rra\-num \& * RRA:DEVSEASONAL:seasonal period:gamma:rra\-num \& * RRA:DEVPREDICT:rows:rra\-num \& * RRA:FAILURES:rows:threshold:window_length:rra\-num .Ve .PP The \f(CW\*(C`rra\-num\*(C'\fR argument is an internal index referencing other RRAs (for example, \s-1HWPREDICT\s0 references \s-1SEASONAL\s0), but this will be taken care of automatically by RRDTool::OO with no user interaction required whatsoever. .SS "Development Status" .IX Subsection "Development Status" The following methods are not yet implemented: .PP \&\f(CW\*(C`rrdresize\*(C'\fR, \f(CW\*(C`xport\*(C'\fR, \f(CW\*(C`rrdcgi\*(C'\fR. .SS "Print Output" .IX Subsection "Print Output" The \f(CW\*(C`graph\*(C'\fR method can be configured to have RRDTool's \f(CW\*(C`graph\*(C'\fR function to print data. Calling rrdtool on the command line, this data ends up on \s-1STDOUT,\s0 but calling something like .PP .Vb 3 \& $rrd\->graph( \& image => "mygraph.png", \& start => $start_time, \& \& # ... \& \& draw => { \& type => "hidden", \& name => "in95precent", \& vdef => "firstdraw,95,PERCENT" \& }, \& \& print => { \& draw => \*(Aqin95percent\*(Aq, \& format => "95 Percent Result = %3.2lf", \& }, \& \& # ... .Ve .PP captures the print data internally. To get access to a reference to the array containing the different pieces of data written in this way, call .PP .Vb 1 \& my $array_ref = $rrd\->print_results(); .Ve .PP If no print output is available, the array referenced by \f(CW$array_ref\fR is empty. .PP If the \f(CW\*(C`graphv\*(C'\fR function is used instead of \f(CW\*(C`graph\*(C'\fR, the return value of print_results is a hashref containing the same information in the \f(CW\*(C`print\*(C'\fR keys, along with additional keys containing detailed information on the graph. See \f(CW\*(C`rrdtool\*(C'\fR documentation for more detail. Here is an example: .PP .Vb 1 \& use Data::Dumper; \& \& $rrd \-> graphv ( \& image => "\-", \& start => $start_time, \& \& # ... \& \& my $hash_ref = $rrd\->print_results(); \& \& print Dumper $hash_ref; \& $VAR1 = { \& \*(Aqprint[2]\*(Aq => \*(Aq1600.00\*(Aq, \& \*(Aqvalue_min\*(Aq => \*(Aq200\*(Aq, \& \*(Aqimage_height\*(Aq => 64, \& \*(Aqgraph_height\*(Aq => 10, \& \*(Aqprint[1]\*(Aq => \*(Aq3010.18\*(Aq, \& \*(Aqgraph_end\*(Aq => 1249391462, \& \*(Aqprint[3]\*(Aq => \*(Aq1600.00\*(Aq, \& \*(Aqgraph_left\*(Aq => 51, \& \*(Aqprint[4]\*(Aq => \*(Aq2337.29\*(Aq, \& \*(Aqprint[0]\*(Aq => \*(Aq305.13\*(Aq, \& \*(Aqvalue_max\*(Aq => \*(Aq10000\*(Aq, \& \*(Aqgraph_width\*(Aq => 10, \& \*(Aqimage_width\*(Aq => 91, \& \*(Aqgraph_top\*(Aq => 22, \& \*(Aqimage\*(Aq => \*(Aq#PNG \& [...lots of binary rubbish your terminal won\*(Aqt like...] \& \*(Aq, \& \*(Aqgraph_start\*(Aq => 1217855462 \& }; .Ve .PP In this case, the option (image => \*(L"\-\*(R") has been used to create the hash key with the same name, the value of which actually contains the \s-1BLOB\s0 of the image itself. This is useful when image needs to be passed to other modules (e.g. Image::Magick), instead of writing it to disk. Be aware that rrdtool 1.3 is required for \f(CW\*(C`graphv\*(C'\fR to work. .SS "Error Handling" .IX Subsection "Error Handling" By default, \f(CW\*(C`RRDTool::OO\*(C'\fR's methods will throw fatal errors (as in: they're calling \f(CW\*(C`die\*(C'\fR) if the underlying \f(CW\*(C`RRDs::*\*(C'\fR commands indicate failure. .PP This behaviour can be overridden by calling the constructor with the \f(CW\*(C`raise_error\*(C'\fR flag set to false: .PP .Vb 4 \& my $rrd = RRDTool::OO\->new( \& file => "myrrdfile.rrd", \& raise_error => 0, \& ); .Ve .PP In this mode, RRDTool's methods will just pass back values returned from the underlying \f(CW\*(C`RRDs\*(C'\fR functions if an error happens (usually 1 if successful and \f(CW\*(C`undef\*(C'\fR if an error occurs). .SS "Debugging" .IX Subsection "Debugging" \&\f(CW\*(C`RRDTool::OO\*(C'\fR is \f(CW\*(C`Log::Log4perl\*(C'\fR enabled, so if you want to know what's going on under the hood, just turn it on: .PP .Vb 1 \& use Log::Log4perl qw(:easy); \& \& Log::Log4perl\->easy_init({ \& level => $DEBUG \& }); .Ve .PP If you're interested particularly in \fIrrdtool\fR commands issued by \f(CW\*(C`RRDTool::OO\*(C'\fR while you're operating it, just enable the category \f(CW"rrdtool"\fR: .PP .Vb 5 \& Log::Log4perl\->easy_init({ \& level => $INFO, \& category => \*(Aqrrdtool\*(Aq, \& layout => \*(Aq%m%n\*(Aq, \& }); .Ve .PP This will display all \f(CW\*(C`rrdtool\*(C'\fR commands that \f(CW\*(C`RRDTool::OO\*(C'\fR submits to the shared library. Let's turn it on for the code snippet in the \&\s-1SYNOPSIS\s0 section of this manual page and watch the output: .PP .Vb 6 \& rrdtool create myrrdfile.rrd \-\-step 1 \e \& DS:mydatasource:GAUGE:2:U:U RRA:MAX:0.5:1:5 \& rrdtool update myrrdfile.rrd N:1 \& rrdtool update myrrdfile.rrd N:2 \& rrdtool update myrrdfile.rrd N:3 \& rrdtool fetch myrrdfile.rrd MAX .Ve .PP Often handy for cut-and-paste. .SS "Allow New rrdtool Parameters" .IX Subsection "Allow New rrdtool Parameters" \&\f(CW\*(C`RRDTool::OO\*(C'\fR tracks rrdtool's progress loosely, so it might happen that at a given point in time, rrdtool introduces a new option that \&\f(CW\*(C`RRDTool::OO\*(C'\fR doesn't know about yet. .PP This might lead to problems, since default, \f(CW\*(C`RRDTool::OO\*(C'\fR has its \&\f(CW\*(C`strict\*(C'\fR mode enabled, rejecting all unknown options. This mode is usually helpful, because it catches typos (like \f(CW"verical_label"\fR), but if you want to use a new rrdtool option, it's in the way. .PP To work around this problem until a new version of \f(CW\*(C`RRDTool::OO\*(C'\fR supports the new parameter, you can use .PP .Vb 1 \& $rrd\->option_add("graph", "frobnication_level"); .Ve .PP to add it to the optional parameter list of the \f(CW\*(C`graph\*(C'\fR (or whatever) rrd function. Note that some functions in \f(CW\*(C`RRDTool::OO\*(C'\fR have sub-methods, which you can specify with the dash notation. The \f(CW\*(C`graph\*(C'\fR method with its various \*(L"graph/draw\*(R", \*(L"graph/color\*(R", \&\*(L"graph/font\*(R" are notable examples. .PP And, as a band-aid, you can disable strict mode in these situation by setting the \f(CW\*(C`strict\*(C'\fR parameter to 0 in \f(CW\*(C`RRDTool::OO\*(C'\fR's constructor call: .PP .Vb 4 \& my $rrd = RRDTool::OO\->new( \& strict => 0, \& file => "myrrdfile.rrd", \& ); .Ve .PP Note that \f(CW\*(C`RRDTool::OO\*(C'\fR follows the convention that parameters names do not contain dashes, but underscores instead. So, you need to say \f(CW"vertical_label"\fR, not \f(CW"vertical\-label"\fR. The underlying rrdtool layer, however, expects dashes, not underscores, which is why \&\f(CW\*(C`RRDTool::OO\*(C'\fR converts them automatically, e.g. transforming \&\f(CW"vertical_label"\fR to \f(CW"\-\-vertical\-label"\fR before the underlying rrdtool call happens. .SS "Dry Run Mode" .IX Subsection "Dry Run Mode" If you want to use \f(CW\*(C`RRDTool::OO\*(C'\fR to create \s-1RRD\s0 commands without executing them directly, thanks to Jacquelin Charbonnel, there's the \&\fIdry run\fR mode. Here's how it works: .PP .Vb 4 \& my $rrd = RRDTool::OO\->new( \& file => "myrrdfile.rrd", \& dry_run => 1 \& ); .Ve .PP With \fIdry_run\fR set to a true value, you can run commands like .PP .Vb 5 \& $rrd\->create( \& step => 60, \& data_source => { name => "mydatasource", \& type => "GAUGE" }, \& archive => { rows => 5 }); .Ve .PP but since \fIdry_mode\fR is on, they won't be handed through to the rrdtool layer anymore. Instead, RRDTool::OO allows you to retrieve a reference to the RRDs function it was about to call including its arguments: .PP .Vb 1 \& my ($subref, $args) = $rrd\->get_exec_env(); .Ve .PP You can now examine or modify the subroutine reference \f(CW$subref\fR or the arguments in the array reference \f(CW$args\fR. Later, simply call .PP .Vb 1 \& $subref\->(@$args); .Ve .PP to execute the RRDs function with the modified argument list later. In this case, @$args would contain the following items: .PP .Vb 2 \& ("myrrdfile.rrd", "\-\-step", "60", \& "DS:mydatasource:GAUGE:120:U:U", "RRA:MAX:0.5:1:5") .Ve .PP If you're interested in the \s-1RRD\s0 function name to be executed, retrieve the third parameter of \f(CW\*(C`get_exec_env\*(C'\fR: .PP .Vb 1 \& my ($subref, $args, $funcname) = $rrd\->get_exec_env(); .Ve .SH "INSTALLATION" .IX Header "INSTALLATION" \&\f(CW\*(C`RRDTool::OO\*(C'\fR requires a \fIrrdtool\fR installation with the \&\f(CW\*(C`RRDs\*(C'\fR Perl module, that comes with the \f(CW\*(C`rrdtool\*(C'\fR distribution. .PP Download the tarball from .PP .Vb 1 \& http://oss.oetiker.ch/rrdtool/pub/rrdtool.tar.gz .Ve .PP and then unpack, compile and install: .PP .Vb 6 \& tar zxfv rrdtool.tar.gz \& cd rrdtool\-1.2.26 \& ./configure \-\-enable\-perl\-site\-install \-\-prefix=/usr \e \& \-\-disable\-tcl \-\-disable\-rrdcgi \& make \& make install \& \& cd bindings/perl\-shared \& perl Makefile.PL \& ./configure \& make \& make test \& make install .Ve .SH "SEE ALSO" .IX Header "SEE ALSO" .IP "\(bu" 4 Tobi Oetiker's RRDTool homepage at .Sp .Vb 1 \& http://rrdtool.org .Ve .Sp especially the manual page at .Sp .Vb 1 \& http://people.ee.ethz.ch/~oetiker/webtools/rrdtool/manual/index.html .Ve .IP "\(bu" 4 My articles on rrdtool in \&\*(L"Linux Magazine\*(R" (\s-1UK\s0) and \&\*(L"Linux Magazin\*(R" (Germany): .Sp .Vb 4 \& (English) \& http://www.linux\-magazine.com/issue/44/Perl_RDDtool.pdf \& (German) \& http://www.linux\-magazin.de/Artikel/ausgabe/2004/06/perl/perl.html .Ve .SH "AUTHOR" .IX Header "AUTHOR" Mike Schilli, .SH "COPYRIGHT AND LICENSE" .IX Header "COPYRIGHT AND LICENSE" Copyright (C) 2004\-2009 by Mike Schilli .PP This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.3 or, at your option, any later version of Perl 5 you may have available.