Cover V10, I05
Article

may2001.tar


Application Usage Accounting

Francisco Mancardi

In the UNIX environment, the software vendors use a license daemon (LD) to limit the number of simultaneous instances of an application or features accessible to the users. These different license daemons have the potential to gather data about usage (starting and ending time, user, host, feature, etc.). Some of this data can be stored in an ASCII file, but more information is stored using a binary format only readable with a tool sold by each license daemon vendor (LDV).

However, this creates a couple of problems:

  • If you choose to pay for the tools, how do you integrate the data from different vendors?
  • If you try to use the ASCII files, how many different formats do you have to parse?
  • What happens if the vendor changes the format?
  • Is the vendor providing documentation about file structure?
  • Is the information that you need recorded in these files?

At my job, we have faced these problems and decided to develop a system. We considered the use of the Resource Accounting System that was already present in our platforms (Solaris 2.6), but we found it very difficult to process that data to obtain what we wanted.

We then switched to a solution using the same principle used by the LDs. We need to generate a record every time a user launches an application and another record when the application is closed. The LDs have this information because the applications are compiled using libraries provided by the LDV, who contact the LD to request and to release a license. We have no access to the application code and little ability to persuade different software vendors to include code developed by our company.

Generally, every software application is launched by calling a script that sets the right environment (PATH, special paths, variables, etc.) and then calls the application. We then add a call to a start_recording script just before calling the application, and to the stop_recording script when the application returns. By using this mechanism, you can record usage information for any script you want.

We wanted a simple but powerful system capable of gathering the data in a centralized location, and we decided to use the standard method to report in UNIX syslogd through the command logger. Because we have to deal with historical information, we decided it's not a good solution to process and reprocess the generated text files every time we want a report. Thus, we decided to import the data into a database system.

We wanted to start with a freeware database engine, but maintain the possibility of migrating to other database engines. Another important condition was to avoid a massive rewrite during the switch between engines. We choose Perl because of the wide availabity of modules (we need date/time arithmetic, graphics, etc.).

Our first database engine was msql, but we switched to MySQL because it supports aggregate functions (AVG, SUM) in the queries, and there is a graphical client to make queries for various operating systems (Windows, Linux, Solaris).

The process is simple -- via syslog, we register the start and stop of every instance of an application in a text file located in the syslog host. During the night we stop syslogd, backup the logfile, re-start syslogd, and process it to insert the data into the database.

System Requirements

1. Perl 5.005_03 or later.

2. Time-modules -- We use Time::JulianDay.pm (Julian Day conversions).

3. DBI -- The Perl Database Interface by Tim Bunce.

4. The msql-mysql-modules -- DBD::mSQL/DBD::mysql and mSQL and mysql drivers for the Perl 5 Database Interface (DBI).

5. Additional software -- gd-chart and the gd lib if you want to generate nice charts.

Details

The system has been developed following a modular approach:

al_main.pl:   the main script
al_defs.pl:   constants DEFinitions
al_calc.pl:   functions to calculate application usage.
al_fct.pl :   functions that deals with date and time aritmethic.
cut_liclog:   the cron job
A sample text file generated using syslogd looks like the following:

1. Nov 20 07:47:40 wks12 LIC_ACC: app1 START 20001120-0747 fiore PID: 599
2. Nov 20 08:11:14 wks12 LIC_ACC: app1 STOP 20001120-0811 fiore PID: 599
3. Nov 20 08:11:57 wks12 LIC_ACC: app1 START 20001120-0811 fiore PID: 810
4. Nov 20 08:12:27 wks3 LIC_ACC: app2 START 20001120-0812 burma PID: 732
5. Nov 20 08:12:50 wks3 LIC_ACC: app3.sh START 20001120-0812 burma PID: 761
6. Nov 20 08:23:58 wks10 LIC_ACC: app4 START 20001120-0823 ckent PID: 755
7. Nov 20 08:32:20 wks14 LIC_ACC: app5 START 20001120-0832 norbi PID: 942
8. Nov 20 08:37:08 wks1 LIC_ACC: run_app6 START 20001120-0837 orre PID: 437
9. Nov 20 09:09:04 wks8 LIC_ACC: app5 START 20001120-0909 zuinni PID: 13998 
10. Nov 20 09:09:25 wks5 LIC_ACC: app7 START 20001120-0909 brink PID: 454 
11. Nov 20 09:12:18 wks5 LIC_ACC: app7 START 20001120-0912 brink PID: 688 
12. Nov 20 09:12:53 wks5 LIC_ACC: app5plus START 20001120-0912 brink PID: 717
13. Nov 20 09:13:04 wks5 LIC_ACC: app5plus STOP 20001120-0913 brink PID: 717
14. Nov 20 13:34:24 wks12 LIC_ACC: app1 STOP 20001120-1334 fiore PID: 810 
We have included the process i.d. (PID) to account for the simultaneous use of the same application in the same host by the same user. We consider each line composed by fields separated by a blank. For example, field #6 is the application name and field #8 is a time stamp in an easier format to process.

We calculate the usage by looking for the start tag (for example, see line 3) and then for the stop tag having the same hostname, application, user, and PID (see line 14).

The database, named DB_license_log, has two tables:

1. syslog_line -- Where we store the data imported from the file generated using the logger command:

CREATE TABLE syslog_line (
aaaa char(4),
mm char(2),
dd char(2),
hh char(2),
mmin char(2),
app char(20),
user char(8),
pid char(6),
hostname char(20),
status char(5),
procesado char(1) );
We have added the field procesado (processed) that we use to resolve the problem of applications running overnight. Remember that we "cut" our license log every night, so it is possible to have a start tag without the corresponding stop tag. We compute the elapsed time when we get the stop, or maybe never (if the application doesn't finish in the normal way).

2. uso_diario (usage) -- Where we insert the results from processing the data contained in syslog_line:

CREATE TABLE uso_diario (
user char(8),
hostname char(20),
app char(20),
aaaa char(4),
mm char(2),
dd char(2),
hh_start char(2),
mm_start char(2),
pid char(6),
uso_en_min int(11),
day_of_week char(2) );
hh_start and mm_start are the starting time, and uso_en_min is usage in minutes during the period started at hh_start and mm_start.

We have defined prime and regular time classes and, during the processing, we generate several records for the usage corresponding to each time class in the table uso_diario. We also have chosen to record the usage separated by date (i.e., if an application is launched on the day 20001123 and is stopped on 20001125, we will have three records for the prime time corresponding to 20001123, 20001124, and 20001125, and records corresponding to the regular time for the same days).

With this system, we can detect licenses that are "locked" during the night by the users. This can be a sign of some problems like users not logging out at the end of the day or insufficient number of licenses.

Getting Useful Data (Reports)

An important issue before trying a report is to understand some internal conventions. We have divided the day in the following slices:

regular - Overnight activity : 00:00 till 07:59 
Prime Time : 08:00 till 19:00 
regular - overtime : 19:00 till 23:59
We can detect overnight usage doing sql queries on the table uso_diario using an "as where" clause something like hh_start="00" and mm_start="00". We extract the most useful data from the table uso_diario. We can construct a monthly ranking of the most used applications with an sql statement like:

select app, aaaa, mm, SUM(uso_en_min) as total_usage  
from uso_diario 
where (day_of_week !="do" and day_of_week !="sa")
group by app, aaaa, mm order by aaaa,mm, total_usage
Sample output:

# app aaaa mm total_usage (minutes) 
'app1' ,'2000' ,'06' ,313 
'app3' ,'2000' ,'06' ,456  
'app12' ,'2000' ,'06' ,890
An important issue in the installation is knowing if there is the right quantity of licenses. To answer this question, you can compute the average number of used licenses with the following equation:

AveLic=Average Number of Licenses
AveLic = Total App usage / Available Working Time
Available Working Time = 
   WorkingHoursbyDay * WorkingDays_in_a_month
We get this figure with the following sql statement:

select app,aaaa, mm, (((SUM(uso_en_min)/60)/6)/22) as num_of_lic from uso_diario 
where (day_of_week !="do" and day_of_week !="sa") 
group by app,aaaa,mm order by aaaa,mm, num_of_lic
Where the numbers mean:

60 -> Minutes in an hour  
6 -> WorkingHoursbyDay 
22 -> WorkingDays_in_a_month
Sample output:

# app aaaa mm num_of_lic
'app1' ,'2000' ,'06' ,0.751389  'app3' 
,'2000' ,'06' ,1.361995  'xsxmenu' ,'2000' 
,'06' ,1.452020  'd3dapp' ,'2000' ,'06' 
,1.662247  'qTool.sh' ,'2000' ,'06' ,1.744318  
'app2d' ,'2000' ,'06' ,2.003283  'app3d' 
,'2000' ,'06' ,2.097601  'MAP' ,'2000' 
,'06' ,3.398359
Another similar figure can be computed with the following equation:

AppUsage(%) = Total App usage / Available License Time
Available License Time = number of licenses * Available Working Time
By adding additional tables to the database (e.g., relation between users and projects, relation between App and App Type), you can compute usage by project or usage by App Type.

Conclusion

Thus, we've got a simple and practical tool to record application usage data, with the spirit of UNIX tools (i.e., combining different tools such as Perl, PerlDBI, syslogd, and MySQL). We have proof that it is possible in a database application of low complexity like this to change your database engine (from msql, to MySQL, to Oracle) with minimum (re-coding) effort thanks to PerlDBI. We have a vendor-independent solution, and the freedom to add different data if we need it. By using the MySQL GUI Client, we have a good user interface with zero development effort. We "rediscover" the power of using syslogd as a standard method for applications and scripts log generation. Further development is needed to provide a standard set of predefined reports as an integral piece to this tool. Furthermore, work is in progress to access reports through the Web.

Acknowledgements

Thanks to my wife, Gabriela, and my twins, Annabella and Sofia. Special thanks to Carolina Leon for her suggestions and testing.

Francisco Mancardi is an associate in U&R Consultores, Buenos Aires, Argentina, a consulting company focused in network management and data security. He can be reached at: fman@uyr.com.ar.