Adrian Nier Code

Back to overview

Log CPU usage

Last modification: Thursday, April 30, 2009 06:04 pm

Meant to be run as a Stay Open AppleScript Application this script will sample the CPU usage and write the results to a log file.

Implementation

property pSAMPLE_INTERVAL : 5 -- Interval between samples in seconds
property pLOG_FILE_PATH : (path to home folder from user domain as string) & "Library:Logs:cpu_history.log"

global gFIRST_RUN
global gOUTPUT_PARSEABLE

on run
   set gFIRST_RUN to true
   main()
end run

on idle
   main()
   return pSAMPLE_INTERVAL
end idle

on main()
   
   try
      -- Sample with the top command three times and get the values of the last one
      try
         set _values to words of paragraph -1 of (do shell script "top -l 3 | grep 'CPU usage'")
         set _timeStamp to timeStamp(current date, 1)
      on error _eMsg number _eNum
         error "Sampling failed. " & _eMsg number _eNum
      end try
      
      -- Convert the values from the sample
      copy parseValues(_values) to {_loadAverage, _userCPU, _sysCPU, _allCPU, _allCPUInt}
      
      -- Format the values
      set _logMessage to _timeStamp & "   " & ¬
         paddedString(_loadAverage as string, " ", 5) & " " & ¬
         paddedString((_userCPU as string) & "%", " ", 7) & " " & ¬
         paddedString((_sysCPU as string) & "%", " ", 7) & " " & ¬
         paddedString((_allCPU as string) & "%", " ", 7) & "   [" & ¬
         paddedString("|", "-", _allCPUInt - 1) & paddedString("]", "-", 51 - _allCPUInt)
      set _logMessage to searchAndReplace(_logMessage, ",", ".")
      
      -- Write values to file
      logMessage(_logMessage, false)
      
   on error _eMsg number _eNum
      logMessage(_eMsg & " (" & (_eNum as string) & ")", true)
      if _eNum is 70000 then quit
   end try
end main

on logMessage(_message, _addTimeStamp)
   try
      if _addTimeStamp then
         try
            set _message to timeStamp(current date, 1) & "   " & _message
         end try
      end if
      set _message to quoted form of _message
      do shell script "echo " & _message & " >> " & quoted form of (POSIX path of (pLOG_FILE_PATH))
   end try
end logMessage

on parseValues(_values)
   
   try
      
      -- Check the output of the top command on first run
      if gFIRST_RUN then
         set gFIRST_RUN to false
         
         if (count of _values) is not 13 then error "Count of values should be 13, but is " & ((count of values) as string) & "."
         if item 6 of _values is not "CPU" then "Value 6 should be \"CPU\", but is \"" & (item 6 of _values) & "\"."
         if item 9 of _values is not "user" then "Value 9 should be \"CPU\", but is \"" & (item 9 of _values) & "\"."
         if item 11 of _values is not "sys" then "Value 11 should be \"CPU\", but is \"" & (item 11 of _values) & "\"."
         
      end if
      
      -- Parse the sampled values
      set _loadAverage to item 3 of _values as string
      
      try
         set _userCPU to item 8 of _values as real
      on error
         -- Strings like "12.3" cannot be converted to real numbers
         -- on systems with German number formats
         set _userCPU to (searchAndReplace(item 8 of _values, ".", ",")) as real
      end try
      
      try
         set _sysCPU to item 10 of _values as real
      on error
         -- Strings like "12.3" cannot be converted to real numbers
         -- on systems with German number formats
         set _sysCPU to (searchAndReplace(item 10 of _values, ".", ",")) as real
      end try
      
      
      set _allCPU to _userCPU + _sysCPU
      set _allCPUInt to (_allCPU / 2) as integer
      
      return {_loadAverage, _userCPU, _sysCPU, _allCPU, _allCPUInt}
      
   on error _eMsg number _eNum
      error "Could not parse output. " & _eMsg & " (" & (_eNum as string) & ")" number 70000
   end try
   
end parseValues

on paddedString(_string, _padding, _length)
   
   repeat _length - (count of _string) times
      set _string to _padding & _string
   end repeat
   
   return _string
   
end paddedString

on timeStamp(_date, _format)
   
   set _month to month of _date as integer
   set _day to day of _date
   set _year to year of _date as string
   
   set _time to (time of _date)
   set _hour to _time div 60 div 60
   set _minute to (_time div 60) - (_hour * 60)
   set _second to _time - (_minute * 60) - (_hour * 60 * 60)
   
   if _month is less than 10 then set _month to "0" & _month
   if _day is less than 10 then set _day to "0" & _day
   set _yearShort to characters -2 thru -1 of _year
   
   if _hour is less than 10 then set _hour to "0" & _hour
   if _minute is less than 10 then set _minute to "0" & _minute
   if _second is less than 10 then set _second to "0" & _second
   
   if _format is 1 then
      return ((_year & "-" & _month & "-" & _day & " " & _hour & ":" & _minute & ":" & _second) as string)
   else if _format is 2 then
      return ((_year & "-" & _month & "-" & _day & "_" & _hour & "-" & _minute & "-" & _second) as string)
   else
      return ""
   end if
   
end timeStamp

on searchAndReplace(_string, _search, _replace)
   
   if _string does not contain _search then return _string
   
   set _prvDlmt to AppleScript's text item delimiters
   try
      set AppleScript's text item delimiters to _search
      set _stringItems to text items of _string
      set AppleScript's text item delimiters to _replace
      set _string to _stringItems as string
   end try
   set AppleScript's text item delimiters to _prvDlmt
   
   return _string
   
end searchAndReplace