func (s stack) Fingerprint() string {
	hash := crc32.NewIEEE()
	for _, frame := range s {
		fmt.Fprintf(hash, "%s%s%d", frame.Filename, frame.Method, frame.Line)
	}
	return fmt.Sprintf("%x", hash.Sum32())
}
Fingerprint creates a fingerprint that uniqely identify a given message. We use the full
callstack, including file names. That ensure that there are no false duplicates
but also means that after changing the code (adding/removing lines), the
fingerprints will change. It's a trade-off.
package rollbar
import (
	"fmt"
	"hash/crc32"
	"os"
	"runtime"
	"strings"
)
var (
	knownFilePathPatterns = []string{
		"github.com/",
		"code.google.com/",
		"bitbucket.org/",
		"launchpad.net/",
	}
)
type frame struct {
	Filename string `json:"filename"`
	Method string `json:"method"`
	Line int `json:"lineno"`
}
type stack []frame
func buildStack(frames []runtime.Frame) stack {
	stack := make(stack, len(frames))
	for i, fr := range frames {
		file := shortenFilePath(fr.File)
		stack[i] = frame{file, functionName(fr.Function), fr.Line}
	}
	return stack
}