A document layout language, lout can also be used to generate graphs in PostScript.
Most documentation tools can also make graphs. This article will show you how to use lout, a document layout language, to create graphs. At the end we will have a script which creates a history graph of system activity.
First of all, lout is a document layout program which generates PostScript. It was written by Jeffrey Kingston at the Basser Department of Computer Science, the University of Sydney, Australia. It is available on Red Hat distributions such as Caldera’s, and many Linux ftp sites. Also, the original sources are available at ftp.cs.su.oz.au under the “jeff” directory.
In many ways, lout is a preprocessor for PostScript, since many lout statements turn into PostScript instructions. In other ways, lout is a language for writing structured documents—we describe lists, sections, chapters, books and reports with lout commands, and our text becomes the arguments to those commands. In this article, we will focus on the commands in lout’s graph package, and ignore its other capabilities for documents.
In general, lout commands start with a commercial-at sign, @. Arguments to a command appear to its right, enclosed in braces, and option settings can appear between the command and its argument:
@Command options { argument }
Usually the argument can contain lout commands also, so we’ll get nested groups of arguments with their braces helping to sort everything out.
Let’s jump right in and take a look at a simple lout document for a graph:
@SysInclude {graph} @SysInclude {doc} @Doc @Text @Begin @Graph { @Data pairs {solid} { 1 1 2 10 3 5 } } @End @Text
Put this into a file called graph.l, and run lout like this:
lout -s -o ps graph.l
lout reads the input file, and generates PostScript, writing it to the output file, here called ps. The -s tells lout that we have a stand-alone document, and we don’t want it to make its cross-reference files.
When you look at the resulting ps file with Ghostview (or print it to a printer), you’ll see a nice little graph at the top of the page. lout has automatically generated a box, figured out some appropriate scales, plotted the lines and displayed the graph. The graph package in lout has many options to modify the characteristics of this graph; we’ll see some of them later.
For now, the important part is the @Graph command:
@Graph { @Data options { data-values } }
This tells lout to make a graph from a data set in its argument list. We gave the @Data command the option “pairs {solid}”, which tells lout that the data set has pairs of x-y coordinate values, and that we want the data points connected with solid lines.
As for the rest of the example, the @SysInclude commands tell lout which packages we’ll be using in the document. The @Doc command says that the body of the document follows, which is delimited by the @Text @Begin and @End @Text statements. And that’s about as difficult as lout gets.
System Activity Graph
Suppose we want to make a system activity graph. We’ll want three curves, one each for the user, system and idle percentages. The user percentage curve will be a solid line, with filled-in circles to indicate each data point. The system and idle percentage curves won’t have symbols at each data point, but the system percent curve will be dashed, and the idle percent curve will be dotted.
Moving from the data sets to the graph itself, we want the graph to fill the page, in a landscape orientation. There will be a title at the top, a brief description below the graph, and a y-axis label to the left. Instead of a legend box to identify the curves, we want an appropriate label to the right of the last point in each curve on the graph.
Implementation
So that’s what we want. How do we get there? We will use awk as an interface between vmstat and lout. The overall flow will look like:
vmstat | awk | lout
vmstat generates numbers for the system activity over a period of time, and passes them on to awk. awk’s role is to take those numbers, collect the ones it needs, and generate @Graph and @Data statements for lout. Finally, lout takes its document and converts it to PostScript. We can take the PostScript output and view it, print it, or embed it elsewhere. If the system is up all the time, we can even use a crontab entry to automatically print out daily activity reports at the end of each day.
Listing 1. SysAct
# # Make a System Activity Graph # # This will be a little confusing to read... # it contains quotes of 'lout' code embedded within an 'awk' script; # they both use lots of braces. # # # Write the 'lout' statements to setup the graph # ( echo "@SysInclude {graph} @SysInclude {doc} @Document @InitialFont {Helvetica Base 10p} @PageHeaders {None} // @Text @Begin @Display 90d @Rotate @Graph width {8 in} height {5.5 in} abovecaption {System Activity Graph: User, System, and Idle Percentages} belowcaption {Ten minute period starting on `date`} leftcaption {90d @Rotate {Percent of System Used}} hidecaptions {no} xticks { 0@ 1@ 2@ 3@ 4@ 5@ 6@ 7@ 8@ 9@ 10@ } yextra {0} xextra {0}" # # run "vmstat" to get system data # read the data lines; store selected data in arrays # at end of the data, generate the remainder of the 'lout' statements # vmstat -n 10 60 | awk ' BEGIN { n = 0 # initialize data-point counter } { if ( $0 ~ /[a-zA-Z]/ ) # skip over header lines next # save selected data values user[n] = $13 # user % syst[n] = $14 # system % idle[n] = $15 # idle % n++ } # # we have all the values # generate data statements for lout # function gen_data(array, dot, line) { printf("@Data ") # start a data group if ( dot != "" ) printf("points { %s } ", dot) # set data-point style printf("pairs { %s } {\n", line) # x/y pairs, line-style for ( i = 0; i < n; i++ ) { # write the data values printf("%f %d\n", i/6.0, array[i]) } print "}" # end this data group } END { # print the identifier at the end of each plot print "objects {" printf(" @E at {%d %d} {User}\n", (n+2)/6.0, user[n-1]) printf(" @E at {%d %d} {System}\n", (n+2)/6.0, syst[n-1]) printf(" @E at {%d %d} {Idle}\n", (n+2)/6.0, idle[n-1]) print "}" # now generate the data body print "{ " gen_data(user, "filledcircle", "solid") gen_data(syst, "", "dashed") gen_data(idle, "", "dotted"); print "}" # and finish-up the document print "@End @Text" }' ) | lout -s -
The complete script is shown in Listing 1. Let’s step through it together. The echo command sends the lout setup code; we include the graph and doc packages, set the default font for the document, and turn off the page headers to avoid getting page numbers.
After the @Text @Begin statements, we show the rotated graph, or, as expressed in lout:
@Display 90d @Rotate @Graph { ... }
Here the @Rotate command takes two arguments: on its left, the number of degrees to rotate, and on its right, the lout object to rotate. In this case, the object we rotate is our entire graph.
The @Graph statement is followed by several graph options. We set the graph size, and we set some captions. We add a caption above the graph with abovecaption { title text }. Similarly, we add a comment below the graph, and a y-axis label to the left of the graph. Note that we rotated the leftcaption so that it would be parallel to the y-axis of the graph. After the captions, we specify the tick marks on the x-axis. By default, lout generated ticks only for every other minute. We can specify where we want ticks and what to label each tick with the xticks and yticks options. In this case, the y-axis ticks are fine, but we want a tick and label on the horizontal axis for each minute. The last two graph options tell lout to not leave any free space between the graph curves and the axes of the graph. This way, the bottom line of the graph box becomes the 0% line of the chart.
Now that all the setup is done, we can start collecting data. We call vmstat, and tell it that we want 60 samples taken 10 seconds apart, to collect data over a 10-minute period. We send these measurements to awk.
Inside the awk script, we collect the user, system, and idle percentages in separate arrays, taking care to skip over the header lines. When vmstat exits, we’ll use these saved values to generate data sets for lout.
Inside the END routine of the awk script, we first generate the lout code to put the curve identifiers at the right side of the graph. In the code, these appear in an objects { … } option to the @Graph command. This is a powerful feature which lets us put nearly anything anywhere on the graph. After the objects are written, we can generate the data sets. An awk function generates the lout statements for us—we pass the array name, the point-style, and the line-style to the routine called gen_data. It takes care of generating a well-formatted data set for lout.
Finally, the awk script finishes the lout document by declaring the end of the text with @End @Text.
The combined output of echo and awk are piped into lout, which sends the PostScript for the system activity graph to its standard output. This script could serve as the basis for many other graphs:
- We could pass in the options to vmstat to get better control over the sampling interval and duration.
- Instead of rotating the graph to take up the full page, we could leave it on a portrait-aligned page, and put additional graphs below it (for swap, free and buffer memory, perhaps).
- We could instruct the awk script to highlight severe system conditions by changing the line-style and point-style in separate data sets when the data values become too high or too low.
- We could generate annotations for the graph automatically, showing the time and percentage values when the system percentage crosses some threshold.
The principle is the same for any other data. Need a disk-usage graph? Run du through an interface script and generate some lout code to graphically show the disk-hogs on the system. Modify the script a little to get a directory usage graph for a single user.
I hope this brief introduction has piqued your interest in the graphing abilities of lout. For more information about lout, see the excellent user and reference manuals that come with it. They cover everything you’ll need to know to use lout.
Availability
Scripts for the system activity graph and a disk-usage graph can be found at: http://www.balr.com.