Write-Error is fine, but there's no way to capture it in redirect.  This means that a script peppered with 'Write-Error' will show nothing if redirected to a file.  The only way to capture it is to Start-Transcript.

Some people use $error only for what it says it is: PowerShell errors.  That's fine.  Those of you who do this (and only this), may as well close this window because I'm about to drive your BP up. 

Me, I'm starting to think of $error much like one does in STDERR in Unix: the 'other' filehandle, one that can also be easily manipulated.

function Log-Problem {
    param ( [string]$errMsg = $null );
    if (!$errMsg) {
        $errMsg = "Undefined call to Log-Problem: {0}" -f  ($MyInvocation.PositionMessage -replace '\n\+' -replace '^\s+');
    Write-Warning "$errMsg`n";
    Write-Error -ErrorAction SilentlyContinue $errMsg;

All this does is tee the error message to both Write-Warning for the user to see, and $error for programmatic access.  Now, $error objects are not just strings.  They have other properties that can be set. 

man Write-Error

will show the other options available. 

With this, if we snapshot $error.count before running the script, we don't have to rely on $? (which may return the last return code of an insignificant operation).  We can just compare $error.count -eq $errorCount.  If they differ, then we added new data to $error.

As for the $myInvocation.PositionMessage bit, that's so I know where I called Log-Problem without a message.  Hm, maybe I should toss that in the $error object as well.