Most of the time when you see more than expected number of rows, that is because your join conditions are not granular enough. It sounds like that is not the problem in your case.
As you may already know, your query would still return multiple rows if you used the DISTINCT keyword ( as in SELECT DISTINCT a.AuditLogToken,....). This is because a row is considered a duplicate of another if and only if ALL the columns have the same values. Had that not been the case, all you would have to do is to add the DISTINCT keyword as I described.
In your case it sounds like for a given AuditLogToken, you might have more than one value of other columns - for example, a given AuditLogToken might have two rows, each with a different RecordDate. If that is the case, you have to decide which of those rows two values of RecordDate you want to see in your output.
One way to get one row per AuditLogToken is to use the row_number function as shown below:
;WITH cte AS
SELECT a.AuditLogToken ,
PARTITION BY a.AuditLogToken
ORDER BY (SELECT NULL)
) AS RN
FROM AuditLog a
JOIN custaddr c ON LEFT(a.KeyValue, 7) = LEFT(c.cust_num, 7)
WHERE a.MessageDesc LIKE 'cust%'
AND a.RecordDate > '2016-04-11'
AND a.NewValue IS NOT NULL
SELECT * FROM cte WHERE RN = 1;
That just picks one random row from among the duplicate AuditLogTokens. You have some control over which row to pick if you change the "(SELECT NULL)" to list columns you want to order by such that the row you want comes on top.