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. 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. This has the following advantages: The ProcessPrinterLogs.vbs script that does all the work is listed below. To run:
Pre-requisites:
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:
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
* Please don’t print this post :) *
Information regarding Windows Infrastructure, centred mostly around commandline automation and other useful bits of information.
6 comments:
Is there any way I can get the client name from which a print job originated?
regards
jan
Hey Wayne, great post! I used your scripts as a starting point for my print server auditing needs.
In addition to modifying the batch portion to loop through a list of servers, I've also added a section to the vbs script that outputs a list of the users printing to each printer. I also used this opportunity to learn VBS as I've not worked with it before.
I'm willing to share with you my additions and would like to know how you're using the output to generate reports, charts, etc. Please feel free to contact me at towerbe@gmail.com if you're interested.
bt
Hey Wayne,
I've gotten a number of emails from people asking me about my version of your script(s). Hope you don't mind if I use this post to direct them to my blog on the subject.
http://pyreanspring.blogspot.com/2008/09/auditing-windows-2003-print-servers.html
On a related note, I also have a post on a script I developed to unmap and remap printers, changing the print server used.
http://pyreanspring.blogspot.com/2008/10/more-fun-with-windows-printers.html
bt
Hi Wayne,
cool script.
It so happens that I just recently did a powershell version of a print monitor for the same scenario.
It has been uploaded to MS Script repository, check it out
http://gallery.technet.microsoft.com/ScriptCenter/en-us/44ba12ff-8c7f-41ad-8d7f-85421ba4198b
Regards Konráð Hall
Hi
I am looking for a script to work with windows 2008 print server!
Post a Comment