Friday, July 20, 2007

4238.aspx

A Rolling File Appender in VBScript

I use log4j and log4net in most of my projects but I did not have a decent alternative for VBScript so I implemented my own. The script below implements the most important features of the log4j/log4net rolling file appender. It lets you configure:



  • file to log to

  • size of each file

  • number of backups/old copies to keep

The code below uses test.txt as the log file with 4 backups. Test.txt contains the last logged data. When it is full, it is renamed to the backup file test.txt.1. The older logs are kept in the files test.txt.2, test.txt.3, … test.txt.N


Note that the size calculation is not accurate as the file is kept open. It is slower, but the size will be correct, if you open and close the file each time you log something. I did not care about accuracy so I chose the faster implementation below.


option explicit


'== CONFIG
const LOG_FILE = "C:\temp\test.txt"
const LOG_FILE_BACKUPS = 4
const LOG_FILE_SIZE_KB = 1000
const LOG_TO_CONSOLE = false



const LOG_FATAL = 0
const LOG_ERROR = 1
const LOG_WARN = 2
const LOG_INFO = 3
const LOG_DEBUG = 4


const LOG_FILE_LEVEL = 4



dim currentLogStream
set currentLogStream = nothing



'==== Test ONLY. Delete this section in production code
'Main tester
WScript.echo "Press CTRL+C to exit"
do while true
 traceLog LOG_DEBUG, "Debug level test " & String(1000,"d")
 traceLog LOG_INFO, "Info level test" & String(1000,"i")
 traceLog LOG_WARN, "Warn level test" & String(1000,"w")
 traceLog LOG_ERROR, "Error level test" & String(1000,"e")
 traceLog LOG_FATAL, "Fatal level test" & String(1000,"f")
 
 Wscript.sleep (1)
Loop
'==== End Test



public sub traceLog (level, message)
dim prefix


 prefix = "[UNKNOWN]"
 if level <= LOG_FILE_LEVEL then
  select case level
   case LOG_FATAL
    prefix = "[FATAL]"
   case LOG_ERROR
    prefix = "[ERROR]"
   case LOG_WARN
    prefix = "[WARN]"
   case LOG_INFO
    prefix = "[INFO]"
   case LOG_DEBUG
    prefix = "[DEBUG]"
  end select


  rollingAppend prefix & vbTab & now() & vbTab & message  
 end if
end sub


sub rollingAppend (message)
'Special case the first time (file not opened)
dim oFS
dim oFile
dim fileNum
 on error resume next 'NEVER bomb while logging
 set oFS = CreateObject("Scripting.FileSystemObject")
 if currentLogStream is nothing then
  'Open stream for APPEND, creating the file if it does not exist
  set currentLogStream = oFS.OpenTextFile(LOG_FILE, 8, true)
 end if


 'Log
 currentLogStream.WriteLine (message) 
 if LOG_TO_CONSOLE then WScript.echo message
 
 'Roll over if we should
 set oFile = oFS.getFile(LOG_FILE)
 if oFile.size >= LOG_FILE_SIZE_KB * 1024 then
  currentLogStream.close()
  set currentLogStream = nothing
  
  'Delete the last backup   
  if oFS.FileExists(LOG_FILE  & "." &  LOG_FILE_BACKUPS) then
   oFS.DeleteFile LOG_FILE  & "." &  LOG_FILE_BACKUPS
  end if
  
  'Rename all the old log files so log_file.1 -> 2, 2->3, etc.
  for fileNum = LOG_FILE_BACKUPS - 1 to 1 step - 1
   if oFS.FileExists (LOG_FILE  & "." &  fileNum) then
    oFS.MoveFile LOG_FILE  & "." &  fileNum, LOG_FILE  & "." &  fileNum + 1
   end if
  next
  
  'Move current file to .1
  oFS.MoveFile LOG_FILE, LOG_FILE & "." & 1  
 end if
end sub

1 comment: