Generating unique ID of an exception For classifying error reports, I need to generate an ID from an exception. That ID should be


(more or less) unique
a short alpha numeric string
equal for equivalent exceptions (even on different systems)


The following code seems to do the job:

    public static string GetExceptionID(this Exception ex)
    {
        if (ex == null) return "0000";

        int hash = 31;
        var innerException = ex;
        while (innerException != null)
        {
            hash ^= GetHash(innerException.GetType().ToString());

            hash ^= innerException.StackTrace
                                  .Split(new[] {Environment.NewLine}, StringSplitOptions.RemoveEmptyEntries)
                                  // If *.pdb file is provided, the file and line no. (e.g. "... in C:\Path...") is part of the stacktrace.
                                  // The next line cutting off that part
                                  .Select(line =&gt; line.Substring(0, line.IndexOf(" in ") &lt; 0 ? line.Length : line.IndexOf(" in ")))
                                  .Aggregate(31, (currentHash, val) =&gt; currentHash ^= val.GetHash());

            innerException = innerException.InnerException;
        }

        return Convert.ToBase64String(BitConverter.GetBytes(hash))
            .Replace('+', 'P')
            .Replace('-', 'M')
            .Replace("=", "")
            .ToUpper();
    }

    // https://en.wikipedia.org/wiki/Jenkins_hash_function
    private static int GetHash(this string str)
    {
        if (str == null) return 1;

        int hash = 31;
        foreach (char c in str)
        {
            unchecked
            {
                hash += c;
                hash += hash &lt;&lt; 10;
                hash ^= hash &gt;&gt; 6;
            }
        }

        hash += hash &lt;&lt; 3;
        hash ^= hash &gt;&gt; 11;
        hash += hash &lt;&lt; 15;

        return hash;
    }


Do you see any problems with the code?
Stack trace

I think you shouldn't work with the stack string but retrieve the stack and go through each frame yourself. I find it would make the analysis more reliable and you could get the information you need without extracting it from the string because you would have direct access to each part.

private static IEnumerable&lt;dynamic&gt; GetStackTrace(this Exception exception)
{
    var stackTrace = new StackTrace(exception, true);
    var stackFrames = stackTrace.GetFrames();
    var result = stackFrames?.Select(sf =&gt; new
    {
        Caller = (sf.GetMethod() as MethodInfo)?.ToShortString() ?? string.Empty,
        FileName = Path.GetFileName(sf.GetFileName()),
        LineNumber = sf.GetFileLineNumber(),
    });
}


This snippet comes from my Exception prettyfier.



Other suggestions

It would be a good idea to split this method into three or four methods.


the first one gets you the information you need from the stack - you could implement it either based on the string or on the raw stack trace
the second one would encode the data as Base64String
the third and last one would clean-up or encode the string generated by the second one
the fourth could be a helper encapsulating the three other methods


Example:

var ex.ToExceptionId()


where

public static string ToExceptionId(this Exception ex)
{
    return ex.GetStackTrace().ToBase64String().EncodeBase64String();
}


Now you can test and maintain each part of the exception id creation separately.