Populating Extended Attribute through checking errors in jamf.log

SeanA
Contributor III

My Mac OS X clients have different errors that I see through the email notifications that are sent to me. Rather than manually resolve each computer, I wanted to have the JSS place the computers with particular errors in specific smart groups so I can use those smart groups to scope specific solutions, e.g., run QuickAdd or jamf enroll at computers with "device signature errors".

I would appreciate JAMF Nation's feedback in either tightening the EA's script (I am sure it can be written more efficiently) and/or improving the script's logic. For example, the current iteration of the EA only checks the last 100 lines of the jamf.log for a particular error. My intent with the tail is to try to examine the few days or week's worth of entries so that the error is relatively fresh; I did not want to try to resolve an error that occurred once a few months ago.

Thanks in advance.

#!/bin/bash

$ERROR=""  #examples: "connection failure", "device signature error", etc.

MATCH=`tail -n 100 /var/log/jamf.log | grep '$ERROR' | awk 'NR==2{print $0}'`

if [[ "$MATCH" == "$ERROR" ]]; then
        RESULT="PROBLEM EXISTS"
    else
        RESULT="NO PROBLEM EXISTS"
fi

echo "<result>$RESULT</result>"

exit 0
2 ACCEPTED SOLUTIONS

mm2270
Legendary Contributor III

Seems like it should be fine, but just a couple of points.
One, I'm not sure how often policies are running on your Macs, but 100 lines of the jamf.log doesn't sound like enough to me to capture anything useful. I can see 100 new lines in my log file in the course of one day. Again, maybe for your Macs this isn't the case, but you may need to bump that number up a bit if you find its not catching anything.
Unfortunately, the date format in the jamf.log is not in anything that makes it easy for a script to parse out only the last -n days. It kind of annoys me its in the format like Thu May 15 12:00:00, etc, Not even a year in it, which is not very useful.

Two, consider using egrep instead of grep and you can place as many different regex terms you want separated by pipes and it will find anything that matches.
For example, use something like this

egrep -i "fail|error"

It will catch anything with either "fail" or "error" and the -i flag makes sure its using case insensitive matching, just incase one of the terms starts with a capital letter, like "Fail" e.g.

View solution in original post

sean
Valued Contributor

You can tail a file in reverse, this way you can find the last instance of a selected pattern match reading a line at a time.

I'm not currently in a Casper environment, but based on the above example 'Thu May 15 12:00:00' you can grab the current date and then work backwards through the file until you get to a previous date. The date format may need to alter slightly to match the jamf log.

I'd suggest writing a launchd to execute a script (below example weekly, but you could do daily or another duration) and use something like:

#!/bin/bash

TESTCOUNTER=0
HASERROR="PROBLEM EXISTS"
NOERROR="NO PROBLEM EXISTS"

while read line
do

# Create a launchd item to match the duration as set in the date function, eg: 7 days, -v-7d
    if [[ "$line" =~ `date -j -v-7d +"%a %b %e"` ]]
    then
        # Reached selected date with no error, so break loop
        break
    else
        case "$line" in

            *"connection failure"*)
                RESULT="$HASERROR"
                TESTCOUNTER=$(($TESTCOUNTER + 1))
                ;;
            *"device signature error"*)
                RESULT="$HASERROR"
                TESTCOUNTER=$(($TESTCOUNTER + 1))
                ;;
            *)
                RESULT="$NOERROR"
                ;;
        esac

        if [ $TESTCOUNTER = 1 ]
        then
            # Found the latest error, so break loop
            break
        fi
    fi
done < <(tail -r /var/log/jamf.log)

echo "<result>$RESULT</result>"

exit 0

You can use an OR in a case statement, but I left them separate in case you want to tailor the result depending on the error.

View solution in original post

5 REPLIES 5

mm2270
Legendary Contributor III

Seems like it should be fine, but just a couple of points.
One, I'm not sure how often policies are running on your Macs, but 100 lines of the jamf.log doesn't sound like enough to me to capture anything useful. I can see 100 new lines in my log file in the course of one day. Again, maybe for your Macs this isn't the case, but you may need to bump that number up a bit if you find its not catching anything.
Unfortunately, the date format in the jamf.log is not in anything that makes it easy for a script to parse out only the last -n days. It kind of annoys me its in the format like Thu May 15 12:00:00, etc, Not even a year in it, which is not very useful.

Two, consider using egrep instead of grep and you can place as many different regex terms you want separated by pipes and it will find anything that matches.
For example, use something like this

egrep -i "fail|error"

It will catch anything with either "fail" or "error" and the -i flag makes sure its using case insensitive matching, just incase one of the terms starts with a capital letter, like "Fail" e.g.

sean
Valued Contributor

You can tail a file in reverse, this way you can find the last instance of a selected pattern match reading a line at a time.

I'm not currently in a Casper environment, but based on the above example 'Thu May 15 12:00:00' you can grab the current date and then work backwards through the file until you get to a previous date. The date format may need to alter slightly to match the jamf log.

I'd suggest writing a launchd to execute a script (below example weekly, but you could do daily or another duration) and use something like:

#!/bin/bash

TESTCOUNTER=0
HASERROR="PROBLEM EXISTS"
NOERROR="NO PROBLEM EXISTS"

while read line
do

# Create a launchd item to match the duration as set in the date function, eg: 7 days, -v-7d
    if [[ "$line" =~ `date -j -v-7d +"%a %b %e"` ]]
    then
        # Reached selected date with no error, so break loop
        break
    else
        case "$line" in

            *"connection failure"*)
                RESULT="$HASERROR"
                TESTCOUNTER=$(($TESTCOUNTER + 1))
                ;;
            *"device signature error"*)
                RESULT="$HASERROR"
                TESTCOUNTER=$(($TESTCOUNTER + 1))
                ;;
            *)
                RESULT="$NOERROR"
                ;;
        esac

        if [ $TESTCOUNTER = 1 ]
        then
            # Found the latest error, so break loop
            break
        fi
    fi
done < <(tail -r /var/log/jamf.log)

echo "<result>$RESULT</result>"

exit 0

You can use an OR in a case statement, but I left them separate in case you want to tailor the result depending on the error.

SeanA
Contributor III

@mm2270… I agree with both your points. I was concerned with the 100 lines variable as well. I was not sure if that would be enough to capture anything useful, but also, I was concerned about capturing too much. It I am using this EA to scope the membership of a smart group, then I am thinking I will also need to capture something that will DROP a computer out of the group after the problem is resolved. After all, the log will still keep the error message of the problem, even after it has been resolved.

Thanks for the egrep. I will definitely put that in my toolbox.

The script that sean is doing seems to logically resolve that concern. Just have to test, test, and more test.

Thanks both of you!

ctangora
Contributor III

I started down this road once. I still have it in my JSS8 environment, but am not bringing it to the JSS9 environment. Ran into the same issues you did, reading only the last xxx lines, and any kind of "error" found would populate the EA. The machines would fall out of the group when the tail of the log no longer had the error. Other things came up and dev on this had to stop and never started up again, but here is what i had.

The other two issues I noticed that should be mentioned. This script only find the first error and does not do well with multiple errors. Also you want to make sure your EA does not have the word "ERROR" in it. I think JAMF fixed this later on, but when I was playing with this it was causing a false-error by erring on the error in the name.

If you get a working EA, let me know. If I ever find free time at work I'll try updating this again.

#!/bin/bash

### Script to read (tail) the jamf log, looking for errors related to MCX.
## Then report back if the error is a known error or an unknown error.
## Then make a few smart computer groups to handle these errors.

## Tweak the number here to change how far back the search/tail goes.
# 100 lines would catch an entire days worth of 'every15' checks.
TailLength=360

## Now, get XX lines of the jamf.log and grep for any errors.
ErrorsInLog=$(tail -n $TailLength /var/log/jamf.log | grep "Error")

## This is here for testing purposes only (if you need to tweak it later) -cT
# ErrorsInLog=$(tail -n $TailLength /var/log/jamf.log | grep "Chris" | sed -e 's_..__')

## Then we check if any errors are returned and searches to see if it is a known error 
## and returns a result that labels it as that error.

if [ -n "$ErrorsInLog" ]
then
    case $ErrorsInLog in
        *"Managed Account Password could not be reset"* )
        echo "<result>Managed Account Password</result>"
        ;;
        *"Import of Managed Preference Setting"* )
        echo "<result>Managed Preferences</result>"
        ;;
        *"Could not connect to the JSS"* )
        echo "<result>No JSS Connection</result>"
        ;;
        * )
        echo "<result>Unknown or new error</result>"
        ;;
    esac
else
    echo "<result>None</result>"
fi

sean
Valued Contributor

If you take the script I provided, use launchd as suggested to run the script periodically and echo the result into a local file you can then just create an EA to read that file's contents.