Take the 2-minute tour ×
Ask Different is a question and answer site for power users of Apple hardware and software. It's 100% free, no registration required.

I want to be able to start an app or script, leave it running for a few hours, then come back and see a log of what processes started and stopped in that time. This is not the same as Activity Monitor or Top which will only show me a "live" view.

E.g. something like this:

<timestamp> <pid> Safari started
<timestamp> <pid> ssh started
<timestamp> <pid> ssh stopped
<timestamp> <pid> Safari stopped

Any recommendations?

Edit to clarify - I don't already know the names of the processes I want to monitor - I want to find out what processes are starting/stopping.

share|improve this question
    
I have edited my original answer to provide you with a dynamic solution. You will now be able to log all Applications/Processes even without knowing their names. –  E1Suave May 7 '12 at 18:33

3 Answers 3

up vote 3 down vote accepted

The following script builds an array of Applications/Processes dynamically. This is an advantage in the sense that you do not need to "know" every process prior to its launch. However, it is important to note that due to this design as the number of processes grow the longer it will take to add a new process to the array. To compensate for this the script keeps the contents of the array cached so as to use them later preventing the need to re-add them to the array.

ALSO: If the Application/Process has not been dynamically loaded into the array prior to the process stopping the logger will leave the "Command" column empty. This will become less and less likely as your dynamically built array builds upon its cached information. Adding to the previous point. At times a "Command" may not be shown in the column yet it has already been added to the array. This is likely caused by the fact that certain process's like "sort" have been removed as they are used in the script and would be constantly starting and stopping (adding false positives to the logger). Currently the data will be put into the file /command_logger. This of course can be changed as you see fit. The config file /tmp/command_logger.plist can also be renamed/moved but remember that it holds the data to the growing App/Process array.

Please feel free to use and modify the following script as you see fit. Please remember to utilize a testing environment prior to running on your primary environment. Enjoy.

Tested on:

•   10.5.x
•   10.6.x
•   10.7.4

The output will include the following:

  • STARTED/STOPPED
  • Epoch (timestamp)
  • Date/Time
  • PID
  • Application/Process

command_logger

NOTE: The following script includes an internal loop.

DYNAMIC APP/PROCESS LOGGER

#!/bin/bash

On="true"

TMP="/tmp/command_logger"
LOG_CONFIG="/command_logger.plist"
LOG="/command_logger"

if [[ ! -e ${TMP} ]]; then

    mkdir ${TMP}

    if [[ ! -e ${TMP}${LOG_CONFIG} ]]; then
        /usr/libexec/PlistBuddy -c "Add:Commands Dict" ${TMP}${LOG_CONFIG} > /dev/null 2>&1
    fi

elif [[ -e ${TMP} ]]; then

    if [[ ! -e ${TMP}${LOG_CONFIG} ]]; then
        /usr/libexec/PlistBuddy -c "Add:Commands Dict" ${TMP}${LOG_CONFIG} > /dev/null 2>&1
        find ${TMP} -type f ! -name '*.plist' -exec rm -f {} \;
    elif [[ -e ${TMP}${LOG_CONFIG} ]]; then
        find ${TMP} -type f ! -name '*.plist' -exec rm -f {} \;
    fi

fi

if [[ ! -e ${LOG} ]]; then
    echo -e "Status\tTimestamp\tDate/Time\t\tPID\tCommand\n" > ${LOG}
elif [[ -e ${LOG} ]]; then

    TITLE=$(head -1 ${LOG} | awk '{print $1}')

    if [[ ${TITLE} != "Status" ]]; then
        echo -e "Status\tTimestamp\tDate/Time\t\tPID\tCommand\n" > ${LOG}
    fi

fi


while [[ ${On} == "true" ]]; do
    IFS=""

    Live_Array=$(ps -Ac | sed 's/  /|/g' | sed 's/ /_/g' | sed 's/[[:digit:]]_.*[[:digit:]]_/ /g' | sed 's/:/!/' | sed 's/$/.comm/' | sed 's/^_//g' | sed 's/^|//g' | sed 's/^_//g' | sed 's/^|.*//g' | sed 's/ $//g' | sed 's/_$//g' | sed 's/|$//g' | sed 's/PID_TTY||.*//g' | awk '{print $2}' | sed 's/.*-sh.*//' | sed 's/.*CMD.*//' | sed 's/.*PID.*//' | sort -u | grep "[[:graph:]]" | tr -s "[\n]" "[,]")
    Live_Array=$(echo ${Live_Array%\,})
    IFS=","
    Live_Array_2=( $Live_Array )
    livarray=${#Live_Array_2[@]}    
    for (( liv=0; liv<${livarray}; liv++ ));
    do  


        /usr/libexec/PlistBuddy -c "Add:Commands:${Live_Array_2[$liv]} bool true" ${TMP}${LOG_CONFIG} 2> /dev/null

        Config_Array=$(/usr/libexec/PlistBuddy -c "Print:Commands" ${TMP}${LOG_CONFIG} | grep "=" | sed 's/=.*//' | sed 's/  //g' | sed 's/^.comm//' | sed 's/ $//g' | grep "[[:graph:]]" | tr -s "[\n]" "[,]")
        Config_Array_2=( $Config_Array )
        conarray=${#Config_Array_2[@]}  


        for (( con=0; con<${conarray}; con++ ));
        do  

            STRING=$(ps -Ac | sed 's/  /|/g' | sed 's/ /_/g' | sed 's/[[:digit:]]_.*[[:digit:]]_/ /g' | sed 's/:/!/'  | sed 's/$/.comm/' | sed 's/^_//g' | sed 's/^|//g' | sed 's/^_//g' | sed 's/^|.*//g' | sed 's/|_.comm/.comm/g' | sed 's/PID_TTY||.*//g' | awk '{print $2,$1}' | sed 's/.*-sh.*//' | sed 's/.*CMD.*//' | sed 's/.*PID.*//' | sort -u)
            Launched_Command=$(echo ${STRING} | awk '{print $1}' | sort -u | awk "/${Config_Array_2[$con]}/")
            PID=$(echo ${STRING} | sort -u | awk '{print $2,$1}' | awk "/${Config_Array_2[$con]}/" | sed 's/ .*//')

            if [[ ${Launched_Command} != "" ]] && [[ ${PID} != "" ]]; then

                DATE=$(date "+%m-%d-%Y %T")
                EPOCH=$(date "+%s") 

                if [[ ${Launched_Command} == ${Config_Array_2[$con]} ]] && [[ ! -e ${TMP}/${Config_Array_2[$con]}-RUNNING ]]; then          
                    echo -e "STARTED\t${EPOCH}\t${DATE}\t${PID}\t${Config_Array_2[$con]}" | sed 's/.comm$//g' | sed 's/_/ /g' | sed 's/.*sort.*//g' | sed 's/.*sed.*//g' | sed 's/awk//g'| awk '/STARTED/' >> ${LOG}
                    rm -f ${TMP}/${Config_Array_2[$con]}-STOPPED
                    touch ${TMP}/${Config_Array_2[$con]}-RUNNING
                elif [[ ${Launched_Command} == ${Config_Array_2[$con]} ]] && [[ -e ${TMP}/${Config_Array_2[$con]}-RUNNING ]]; then
                    :       
                elif [[ ${Launched_Command} == ${Config_Array_2[$con]} ]] && [[ -e ${TMP}/${Config_Array_2[$con]}-RUNNING ]]; then

                    if [[ -e ${TMP}/${Config_Array_2[$con]}-STOPPED ]]; then
                        :
                    elif [[ ! -e ${TMP}/${Config_Array_2[$con]}-STOPPED ]]; then
                        echo -e "STOPPED\t${EPOCH}\t${DATE}\t${PID}\t${Config_Array_2[$con]}" | sed 's/.comm$//g' | sed 's/_/ /g' | sed 's/.*sort.*//g' | sed 's/.*sed.*//g' | sed 's/awk//g' | awk '/STOPPED/' >> ${LOG}
                        rm -f ${TMP}/${Config_Array_2[$con]}-RUNNING
                        touch ${TMP}/${Config_Array_2[$con]}-STOPPED                    
                    fi  

                fi

            elif [[ ${Launched_Command} == "" ]] && [[ -e ${TMP}/${Config_Array_2[$con]}-RUNNING ]]; then

                if [[ -e ${TMP}/${Config_Array_2[$con]}-STOPPED ]]; then
                    :
                elif [[ ! -e ${TMP}/${Config_Array_2[$con]}-STOPPED ]]; then
                    echo -e "STOPPED\t${EPOCH}\t${DATE}\t${PID}\t${Config_Array_2[$con]}" | sed 's/.comm$//g' | sed 's/_/ /g' | sed 's/.*sort.*//g' | sed 's/.*sed.*//g' | sed 's/awk//g' | awk '/STOPPED/' >> ${LOG}
                    rm -f ${TMP}/${Config_Array_2[$con]}-RUNNING
                    touch ${TMP}/${Config_Array_2[$con]}-STOPPED                    
                fi              
            fi      
        done
    done    
done
share|improve this answer
    
Hi that's great and it's given me some ideas. But I want the script to tell me about any processes that start, I don't already know the process names to monitor. –  snowcrash09 May 3 '12 at 9:23
    
@snowcrash09 Yeah, that is a bit different. I suppose the above script would still work if you wanted to include 10, 20, 30 different processes/Applications in the array. I do understand your point about not "knowing" the exact specific processes/Applications but as long as your intensions weren't to monitor completely unknown processes/Applications it may still do what you need. –  E1Suave May 3 '12 at 9:39
    
@snowcrash09 I have edited my original answer to provide you with a dynamic solution. You will now be able to log all Applications/Processes even without knowing their names. –  E1Suave May 7 '12 at 20:15
    
@snowcrash09 did you have any luck with the new script? –  E1Suave May 10 '12 at 15:34
    
yes thank you! it seems to incorrectly log existing processes as just starting, but I can take it from here. –  snowcrash09 May 11 '12 at 14:14

Have you had a look at fseventer?

It allows you to see a graphical representation of file system activity and, whilst it does a lot more than you might need, it can also list application launch and exit (which you should then be able to filter on to remove other events you don't need).

share|improve this answer

Effectively what top is doing is sampling the process manager. The process manager has C APIs on the Unix level, Obj-C APIs on the Cocoa level, and of course the commands PS and TOP.

I think what you are looking for is going to require a little more programming that a script. I also don't believe that if such a beast exists (which I'm not sure of) that you're not likely to find a Mac Application for free.

You maybe able to find one in the open source community (Google Code, Git, etc) if you poke around for a "Process Logger."

If you're thinking of spinning one of your own; you might want to ask around StackOverflow rather than here.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.