Command logging allows an application to create a detailed audit log of every command executed, including those from within Procs, paragraphs, menus, etc. The QM command processor passes the command and its origin to an optional user supplied logging subroutine, leaving the application designer free to determine what information is actually logged.
The logging is performed by a QMBasic class module globally catalogued as !CLOG. This has a public subroutine named LOG that two arguments; the command and its origin.
The origin is one of the following:
Where a command includes an inline prompt construct, the command is logged a second time after expansion of the inline prompt.
Logging may be enabled in two mutually exclusive manners. If the CMDLOG configuration parameter is not null, it specifies the pathname of the directory in which logging data is to be recorded. Logging is enabled across all processes and merged into a single log file per day in the specified directory using a name formed from the date in yymmdd format with a .log suffix.
If the CMDLOG configuration parameter is null, logging may be enabled in individual processes by use of the COMMAND.LOGGING ON command. In this mode, the logged data is written to a file named clog.nnn.log in the QM temporary directory where nnn is the QM user number.
The default command logger shown below is catalogued as part of QM and is also issued in source form in the BP file of the QMSYS account. Users may adapt this to meet their own needs.
private log.f, log.date, log.dir
public subroutine create.object
log.date = 0
log.dir = config('CMDLOG')
public subroutine log(cmd, origin)
tm = epoch()
if not(fileinfo(log.f, FL$OPEN)) then
if log.dir = '' then ;* Single process logging
log.path = system(SYS$TEMP) : @ds : 'clog.' : @user.no : '.log'
dt = mvdate(tm)
if log.date # dt then ;* New date - switch log files
closeseq log.f ;* No effect if file was not open
log.date = dt
log.path = log.dir : @ds : oconv(log.date, 'D2YMD["",""]') : '.log'
openseq log.path shared to log.f else return
* Construct and write log data
log.rec = tm
log.rec<-1> = @user.no
log.rec<-1> = @logname
log.rec<-1> = origin
log.rec<-1> = cmd
writeseq change(log.rec, @fm, char(9)) to log.f else null
This example logger constructs a dynamic array that it then converts to be tab delimited. The logged data is the epoch value (allowing consistent logging across time zones), the QM user number, user name, command origin and the command itself.