Today I had a look on my 404 log to check if there were any broken links or orphaned slugs on my site. The log was more than a couple of days old and … rather long. Since I made some “experiments” with my site recently, it was likely that the majority of the 404s have been triggered by myself, that is, from my Mac, behind a dynamic IP address.
So, how to filter out these not-so-interesting entries?
While it is no problem to get the current external (WAN) IP address, it is indeed a problem to identify the IP addresses attributed to the router over the last weeks. (Unless you have a static IP, of course.) As far as I know there just isn’t a log file that tracks the WAN IP history anywhere on my Mac.
So, what we want is a little script that runs in the background, tracks the external IP address and writes it to a log file.
The Script
You love AppleScript, don’t you? Very useful here, since it integrates so well with the OS services, for example the Notification Center.
You can copy the script text from below or download it. Either way, save it as TrackWAN-IP.scpt
into the Scripts folder in your Home directory (~/Library/Scripts/
).
## Name: TrackWAN-IP
## Version: 2015-02-13
### BEGIN Settings ###
-- Location of the log file (delimiter: ":")
property IPLogFile : ((path to home folder) & "Desktop:wan_ip.log") as text
-- Shell queries to retrieve WAN IP; the fastest / most reliable should be first
property IPinfoQueries : {"dig +short myip.opendns.com @resolver1.opendns.com", "curl ip-addr.es", "curl ifconfig.me", "curl -s http://checkip.dyndns.org/ | grep -o \"[[:digit:].]\\+\""}
-- The format of the timestamp in the log file (for a complete timestamp: "date +%FT%T%z")
property dateCmd : "date +%y%m%dT%H%M" --This is a minimal timestamp
### END Settings ###
global wanIP, hasChanged, hasError
### Script ###
getWANIP(IPinfoQueries)
-- Don’t write if IP hasn’t changed
checkLastEntry(IPLogFile, wanIP)
if hasChanged then writeLog(wanIP)
### Handlers ###
on getWANIP(theQueries)
set wanIP to ""
set hasError to 0
set storedDelimiters to AppleScript's text item delimiters
set AppleScript's text item delimiters to "."
repeat with i in theQueries
try
set wanIP to do shell script i
end try
if (count of wanIP's text items) is equal to 4 then exit repeat
set hasError to 1
end repeat
if (count of wanIP's text items) is not equal to 4 then set hasError to 2
set AppleScript's text item delimiters to storedDelimiters
errorCheck(hasError)
return
end getWANIP
on writeLog(theIP)
try
close access file IPLogFile
end try
set the openIPLogFile to open for access file IPLogFile with write permission
set timeStamp to do shell script dateCmd
set newEntry to timeStamp & ": " & theIP & "\n"
write newEntry to the openIPLogFile starting at eof
close access the openIPLogFile
if hasError = 0 then display notification "New IP address written to log file." with title "✔︎ TrackWAN-IP: New IP Detected" subtitle theIP
end writeLog
on errorCheck(errorType)
tell application "System Events"
if errorType = 0 then
return
else if errorType = 1 then
display notification "IP address retrieved – but one of the sources didn’t work. Please check the URLs." with title "✘ TrackWAN-IP: Fallback URL Used"
else if errorType = 2 then
display alert "TrackWAN-IP: Error\n\nNo IP address retrieved. Check your network connection."
-- Less obstrusive:
--display notification "Check your network connection." with title "✘✘ TrackWAN-IP: Error" subtitle "No IP address retrieved."
else
display alert "TrackWAN-IP:\n\nFatal error occurred."
error number -128
end if
end tell
return
end errorCheck
on checkLastEntry(theFile, theEntry)
set storedDelimiters to AppleScript's text item delimiters
set AppleScript's text item delimiters to " "
try
set lastEntry to text item 2 of paragraph -2 of (read file theFile)
on error
--In case there is no previous entry
set lastEntry to ""
end try
set AppleScript's text item delimiters to storedDelimiters
if theEntry is equal to lastEntry then
-- Give feedback that the LaunchAgent is still launching the script
if hasError = 0 then display notification "IP address checked, no change." with title "✔︎ TrackWAN-IP: No IP Change"
-- We could exit here with '-128' but this would register an error with launchd
set hasChanged to false
else
set hasChanged to true
end if
return
end checkLastEntry
What the script basically does:
- It queries the external IP address of your connection.
- It writes the IP address to a log file on your Mac.
In addition it does some comfort stuff:
- If the IP query server fails it falls back to another server. Currently it uses these services:
opendns.com
(default)ip-addr.es
(fallback 1)ifconfig.me
(fallback 2)dyndns.org
(fallback 3)
- It doesn’t write anything to the log file if the IP address hasn’t changed.
- It gives you different notifications for these events:
- It has detected an IP address change
- It has launched and the IP hasn’t changed (you may disable this)
- The default query server is down and it uses one of the fallback URLs
- There is no connection at all
Configuration
As you may have seen, at the beginning of the script there are some properties you can/should configure:
- Location and name of your log file.
- The URLs of the servers to query. The default order works fine for me but that may be different at your location. Test the URLs individually in the Terminal and make sure that the fastest is on first position. The others serve only as fallback in case the first one is down.
- You can set the timestamp format to your liking. I’ve preset a compact form, to keep file size as small as possible.
Scheduling the Script
No special software needed for this. We’ll just create a launch agent for the system’s launchd. It works like this:
- Copy the text below und save it as net.dflect.TrackWAN-IP.plist into the directory
~/Library/LaunchAgents/
. (That’s in your Home folder.)Make sure that the line
<string>/Users/[home directory]/Library/Scripts/TrackWAN-IP.scpt</string>
corresponds to the actual location and name of the script file. (Of course, replace the[home directory]
with the name of your Home directory.)<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Disabled</key> <false/> <key>Label</key> <string>net.dflect.TrackWAN-IP</string> <key>ProgramArguments</key> <array> <string>/usr/bin/osascript</string> <string>/Users/[home directory]/Library/Scripts/TrackWAN-IP.scpt</string> </array> <key>RunAtLoad</key> <true/> <key>StartInterval</key> <integer>10800</integer> </dict> </plist>
- Set the
StartInterval
in the plist according to your needs (seconds). Depending on how frequently your external IP address changes, something between 1 hour (3600s) and 24 hours (86400s) should be fine. (You can always manually launch the script from the Script menu, too.) - Load and start the the script with…
launchctl load -w ~/Library/LaunchAgents/net.dflect.TrackWAN-IP.plist
The script should run immediatly, and automatically load at login, too.1
By the way, if you don’t want to bother with launch agents in the Terminal there is a very nice GUI app for all this: LaunchControl. It lets you set all options, indicates errors and can even create the plist file for you. Highly recommended.
Here’s a screenshot of what it looks like in LaunchControl if the script is correctly registered with launchd:
Sources, Credits, More Info
If you need IPv6 addresses: ip.appspot.com, icanhazip.com, ifcfg.me