Erweiterungen für play-airbrake, errbit-android

Ausgangslage

Wir setzen seit einiger Zeit für unsere Applikationen den Errbit Fehlersammler ein. Errbit ist eine self-hosted Webapplikation mit derselben Schnittstelle wie Airbrake. Damit Fehler einer Applikation dem Errbit-Server oder Airbrake.io gesendet werden können, verwendet man eine Bibliothek, die diese Schnittstelle zur Verfügung stellt.

Wir haben unter anderen die Bibliotheken errbit-android und kürzlich play-airbrake eingesetzt. Beide haben wir für unsere Zwecke erweitert. Bei errbit-android haben wir weitere Request-Parameter und Geräteinformationen hinzugefügt. play-airbrake haben wir so erweitert, dass die verwendete Airbrake/Errbit JavaScript-Bibliothek konfigurierbar ist. Zudem ist diese Version kompatibel mit Play 2.2.x.

Ausdrucksstärke von Scala

Bei der kleinen Erweiterung von play-airbrake ist uns die Ausdrucksstärke von Scala wieder einmal ins Auge gesprungen. Die Fähigkeit Sprachen bzw. DSLs (Domain Specific Languages) direkt in der Scala-Syntax zu integrieren bringt uns diese pragmatische Lösung (Originalimplementation von Tymon Tobolski):

  protected def formatNotice(app: Application, apiKey: String, method: String, uri: String, data: Map[String, String], ex: UsefulException) =
    <notice version="2.2">
      <api-key>{ apiKey }</api-key>
      <notifier>
        <name>play-airbrake</name>
        <version>0.3.2-SNAPSHOT</version>
        <url>http://github.com/tegonal/play-airbrake</url>
      </notifier>
      <error>
        <class>{ ex.title }</class>
        <message>{ ex.description }</message>
        <backtrace>
          { ex.cause.getStackTrace.flatMap(e => formatStacktrace(e)) }
        </backtrace>
      </error>
      <request>
        <url>{ method + " " + uri }</url>
        <component/>
        <action/>
        { formatSession(data) }
      </request>
      <server-environment>
        <environment-name>{ app.mode }</environment-name>
      </server-environment>
    </notice>

  protected def formatSession(vars: Map[String, String]) =
    if (!vars.isEmpty) <session>{ formatVars(vars) }</session>
    else Nil

  protected def formatVars(vars: Map[String, String]) = vars.map {
    case (key, value) =>
      <var key={ key }>{ value }</var>
  }

  protected def formatStacktrace(e: StackTraceElement) = {
    val line = s"${e.getClassName}.${e.getMethodName}(${e.getFileName})"
    <line file={ line } number={ e.getLineNumber.toString }/>
  }

Im Vergleich dazu die Java-Variante der errbit-android-Implementation:

private static void writeExceptionToDisk(Throwable e,
			final Map metaData, Resources res) {
		try {
			// Set up the output stream
			int random = new Random().nextInt(99999);
			String filename = filePath + versionName + "-"
					+ String.valueOf(random) + ".xml";
			BufferedWriter writer = new BufferedWriter(new FileWriter(filename));
			XmlSerializer s = Xml.newSerializer();
			s.setOutput(writer);

			// Start ridiculous xml building
			s.startDocument("UTF-8", true);

			// Top level tag
			s.startTag("", "notice");
			s.attribute("", "version", AIRBRAKE_API_VERSION);

			// Fill in the api key
			s.startTag("", "api-key");
			s.text(apiKey);
// weitere 200 Zeilen wurden der Übersicht halber ausgelassen

Hierbei handelt es sich nicht um die schönste und kürzeste Java-Implementation aber auch so wird schnell ersichtlich wie einfach und aussagekräftig die Scala-Variante ist.

Der Vergleich auf github: play-airbrake XML und errbit-android XML.