This post provides information on a solution to provide audit logs and summary information on printer usage on a Windows print server. This will provide daily and monthly printer event logs, and summary results based on one or more log files.
Pre-requisites:
- A scheduled task that runs every day to collect and process the logs, it doesn’t have to be on the print server.
- 'Log Spooler Information Events' on the printer spooler in question, which will write Event ID 10 entries every time someone prints through the spooler
- A system event log big enough to capture at least one day's logs
Create a batch file that contains the following commands. Note that you will need to modify the variables or to reference paths as appropriate, eg for dumpel.exe and the VBScript, and the print/log dir.
Set PrintDir=c:\Print
Set LogDir=C:\logs
for /f "tokens=1-8 delims=/:. " %%i in ('echo %date%') do Set DateFlat=%%l%%k%%j
dumpel -s \\%print_server% -l System -e 10 -m Print -d 1 >> %logDir%\%server%_jobs_%DateFlat%.csv
for /f "tokens=3,4 delims=/ " %%i in ('echo %date%') do copy %server%_jobs_%%j%%i??.csv %PrintDir%\PrintJobs_%%j%%i.csv /y
cscript ProcessPrinterLogs.wsf /f:%LogDir%
If you create a scheduled task to run this batch file every day at the same time, these commands will:
- Dump the event logs for the past day to a daily log file with YYYYMMDD suffix.
- Collate the daily log files into a monthly log file, by appending each daily file. For each day in a month, this command will overwrite the previous monthly log, until it runs on the last day of the month.
- The script processes the dumpel log entries, providing different views in the form of per-printer, per-user, per-day totals of jobs/pages/bytes (useful for graphics), as well as summary totals of the information.
This has the following advantages:
- It’s simple and not very intensive. A SQL database with a recurring DTS job to import the logs and then using SQL Reporting Services would be a lot prettier, but really not that much more functional or useful.
- You will have a permanent set of log files, one for each month that you can store for historical purposes, while purging the daily log file directory every so often.
The ProcessPrinterLogs.vbs script that does all the work is listed below. To run:
cscript ProcessPrinterLogs.vbs /f:%logDir%
Const ForReading = 1, ForWriting = 2, ForAppending = 8 Set objFSO = CreateObject("Scripting.FileSystemObject") Set objShell = CreateObject("WScript.Shell") Main() Sub Main() If WScript.Arguments.Named.Exists("f") Then sSource = Wscript.Arguments.Named("f") Else Wscript.Arguments.ShowUsage() Wscript.Echo "Source file or directory must be supplied" Wscript.Quit(2) End If If Wscript.Arguments.Named.Exists("o") Then sOutputFile = Wscript.Arguments.Named("o") Else dNow = Now dLogDate = DatePart("yyyy", dNow) dLogDate = dLogDate & String(2 - Len(DatePart("m", dNow)),"0") & DatePart("m", dNow) dLogDate = dLogDate & String(2 - Len(DatePart("d", dNow)),"0") & DatePart("d", dNow) sOutputFile = objShell.ExpandEnvironmentStrings("%Temp%") sOutputFile = sOutputFile & "\" & Left(WScript.ScriptName, InStrRev(WScript.ScriptName,".vbs")-1) & "_" & dLogDate & ".csv" End If wscript.echo "Input file/dir: '" & sSource & "'" wscript.echo "Output file: '" & sOutputFile & "'" If objFSO.FileExists(sSource) Then sFileSet = sSource ' Process a single file wscript.echo "Single file specified - " & sFileSet ElseIf objFSO.FolderExists(sSource) Then wscript.echo "Source specified was a directory, reading files from '" & sSource & "'" sFileSet = "" Set oFolder = objFSO.GetFolder(sSource) ' Get the folder Set oFiles = oFolder.Files For Each oFile in oFiles ' For each file sFileset = sFileset & vbCRLF & oFile.Path ' Append to the fileset Next If Len(sFileSet) > Len(vbCRLF) Then sFileSet = Right(sFileSet, Len(sFileSet) - Len(vbCRLF)) ' Trim the leading CRLF End If Set dPrinters = CreateObject("Scripting.Dictionary") ' Create the dictionary objects Set dusers = CreateObject("Scripting.Dictionary") Set dDates = CreateObject("Scripting.Dictionary") Set dJobs = CreateObject("Scripting.Dictionary") For Each sFile in Split(sFileset, vbCRLF) ' For Each file wscript.echo "Processing '" & sFile & "'" sBuffer = "" Set objTextStream = objFSO.OpenTextFile(sFile, ForReading) sBuffer = objTextStream.ReadAll For Each sLine in Split(sBuffer, vbCRLF) ' For each line in this file Call ProcessLogEntry(sLine, dPrinters, dUsers, dDates, dJobs) ' Process the log entry Next Next Call ProduceOutput(sOutput, dPrinters, dUsers, dDates, dJobs) ' Produce the output Set objTextStream = objFSO.OpenTextFile(sOutputFile, ForWriting, True) objTextStream.Write sOutput wscript.echo "Output saved to '" & sOutputFile & "', " & Len(sOutput) & " characters." End Sub Function ProduceOutput(ByRef sOutput, ByRef dPrinters, ByRef dUsers, ByRef dDates, ByRef dJobs) Dim strPrinter, strPort, dtmDate, strUser, strserver, strDocumentName, intSize, intPages, strInformation, strTotal Dim strUserTotal, strPrinterTotal, strDateTotal, strJobTotal, aJobTotal sOutput = "" For Each strPrinter in dPrinters.Keys sOutput = sOutput & vbCRLF & strPrinter & "," & dPrinters.Item(strPrinter) Next sOutput = sOutput & vbCRLF For Each strUser in dUsers.Keys sOutput = sOutput & vbCRLF & strUser & "," & dUsers.Item(strUser) Next sOutput = sOutput & vbCRLF For Each dtmDate in dDates.Keys sOutput = sOutput & vbCRLF & dtmDate & "," & dDates.Item(dtmDate) Next sOutput = sOutput & vbCRLF For Each strTotal in dJobs.Keys strJobTotal = dJobs.Item(strTotal) aJobTotal = Split(strJobTotal, ",") sOutput = sOutput & vbCRLF & "Total Jobs," & aJobTotal(0) sOutput = sOutput & vbCRLF & "Total Pages," & aJobTotal(1) sOutput = sOutput & vbCRLF & "Total Size (MB)," & aJobTotal(2) Next sOutput = sOutput & vbCRLF strUserTotal = UBound(dUsers.Keys)+1 strPrinterTotal = UBound(dPrinters.Keys)+1 strDateTotal = UBound(dDates.Keys)+1 sOutput = sOutput & vbCRLF & "Printers," & strPrinterTotal sOutput = sOutput & vbCRLF & "Users," & strUserTotal sOutput = sOutput & vbCRLF & "Days," & strDateTotal aJobTotal = Split(strJobTotal, ",") sOutput = sOutput & vbCRLF sOutput = sOutput & vbCRLF & "Average jobs/person," & CInt(aJobTotal(0)/strUserTotal) sOutput = sOutput & vbCRLF & "Average pages/person," & CInt(aJobTotal(1)/strUserTotal) sOutput = sOutput & vbCRLF & "Average pages/person/day," & CInt(CInt(aJobTotal(1)/strUserTotal) / strDateTotal) sOutput = sOutput & vbCRLF & "Average pages/minute," & CInt(aJobTotal(1) / (strDateTotal * 8 * 60)) End Function Function ProcessLogEntry(ByRef sLine, ByRef dPrinters, ByRef dUsers, ByRef dDates, ByRef dJobs) Dim strPrinter, strPort, dtmDate, strUser, strserver, strDocumentName, intSize, intPages, strInformation Dim aPrintJob, intOffset, strTemp, aTemp aPrintJob = Split(sLine, vbTAB) If UBound(aPrintJob) = 9 Then dtmDate = aPrintJob(0) ' & " " & aPrintJob(1) aTemp = Split(dtmDate, "/") dtmDate = Right("00" & Trim(aTemp(1)), 2) & "/" & Right("00" & Trim(aTemp(0)), 2) & "/" & aTemp(2) ' Trim, pad and switch to dd/mm/yyyy instead of mm/dd/yyyy strServer = aPrintJob(8) strInformation = Trim(aPrintJob(9)) strInformation = Right(strInformation, Len(strInformation) - InStr(strInformation, " ")) ' Remove the job ID intOffset = InStrRev(strInformation, " ") intPages = Right(strInformation, Len(strInformation) - intOffset) ' Extract the number of pages from the end strInformation = Left(strInformation, intOffset-1) ' Trim the string intOffset = InStrRev(strInformation, " ") intSize = Right(strInformation, Len(strInformation) - intOffset) ' Extract the number of bytes from the end strInformation = Left(strInformation, intOffset-1) ' Trim the string intOffset = InStrRev(strInformation, " ") strPort = Right(strInformation, Len(strInformation) - intOffset) ' Extract the port from the end strInformation = Left(strInformation, intOffset-1) ' Trim the string intOffset = InStrRev(strInformation, " ") strPrinter = Right(strInformation, Len(strInformation) - intOffset) ' Extract the printer from the end strInformation = Left(strInformation, intOffset-1) ' Trim the string intOffset = InStrRev(strInformation, " ") strUser = Right(strInformation, Len(strInformation) - intOffset) ' Extract the user from the end strInformation = Left(strInformation, intOffset-1) ' Trim the string strDocumentName = strInformation If dPrinters.Exists(strPrinter) Then ' Does this printer already exist in the dictionary? aTemp = Split(dPrinters.Item(strPrinter), ",") ' Find the existing printer job/page count aTemp(0) = aTemp(0) + 1 ' Increment the job count aTemp(1) = aTemp(1) + CInt(intPages) ' Add to the page count aTemp(2) = aTemp(2) + CInt(intSize/1024/1024) ' Add to the byte count dPrinters.Item(strPrinter) = Join(aTemp, ",") ' Update the dictionary Else aTemp = Array(1, intPages, CInt(intsize /1024/1024)) ' Start the job/page count dPrinters.Add strPrinter, Join(aTemp, ",") ' Create this item End If If dUsers.Exists(strUser) Then ' Does this user already exist in the dictionary? aTemp = Split(dUsers.Item(strUser), ",") ' Find the existing user job/page count aTemp(0) = aTemp(0) + 1 ' Increment the job count aTemp(1) = aTemp(1) + CInt(intPages) ' Add to the page count aTemp(2) = aTemp(2) + CInt(intSize/1024/1024) ' Add to the byte count dUsers.Item(strUser) = Join(aTemp, ",") ' Update the dictionary Else aTemp = Array(1, intPages, CInt(intsize /1024/1024)) ' Start the job/page count dUsers.Add strUser, Join(aTemp, ",") ' Create this item End If If dDates.Exists(dtmDate) Then ' Does this date already exist in the dictionary? aTemp = Split(dDates.Item(dtmDate), ",") ' Find the existing date job/page count aTemp(0) = aTemp(0) + 1 ' Increment the job count aTemp(1) = aTemp(1) + CInt(intPages) ' Add to the page count aTemp(2) = aTemp(2) + CInt(intSize/1024/1024) ' Add to the byte count dDates.Item(dtmDate) = Join(aTemp, ",") ' Update the dictionary Else aTemp = Array(1, intPages, CInt(intsize /1024/1024)) ' Start the job/page count dDates.Add dtmDate, Join(aTemp, ",") ' Create this item End If If dJobs.Exists(JOB_TOTAL) Then ' Does the total already exist in the dictionary? aTemp = Split(dJobs.Item(JOB_TOTAL), ",") ' Find the existing total counts aTemp(0) = aTemp(0) + 1 ' Increment the job count aTemp(1) = aTemp(1) + CInt(intPages) ' Add to the page count aTemp(2) = aTemp(2) + CInt(intSize/1024/1024) ' Add to the byte count dJobs.Item(JOB_TOTAL) = Join(aTemp, ",") ' Update the dictionary Else aTemp = Array(1, intPages, CInt(intsize /1024/1024)) ' Start the job/page count dJobs.Add JOB_TOTAL, Join(aTemp, ",") ' Create this item End If Else wscript.echo "skipped '" & sLine & "'" End If End Function
No comments:
Post a Comment