package com.github.kyleburton.logback;

import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.classic.spi.IThrowableProxy;
import ch.qos.logback.classic.spi.StackTraceElementProxy;
import ch.qos.logback.core.AppenderBase;
import com.aphyr.riemann.client.EventDSL;
import com.aphyr.riemann.client.RiemannClient;
import com.aphyr.riemann.client.SynchronousTransport;

import java.io.IOException;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;

public class RiemannAppender<E> extends AppenderBase<E> {
  private static final String DEFAULT_PORT = "5555";
  private static final String DEFAULT_HOST = "localhost";

  private String serviceName     = "*no-service-name*";
  private String riemannHostname = DEFAULT_HOST;
  private String riemannPort     = DEFAULT_PORT;
  private String hostname        = "*no-host-name*";

  public static AtomicLong timesCalled = new AtomicLong(0);

  private static boolean debug = false;

  private RiemannClient riemannClient = null;

  public void start() {
    try {
      if (debug) {
        System.err.println(String.format("%s.start()", this));
      }
      SynchronousTransport transport = new SimpleUdpTransport(riemannHostname, Integer.parseInt(riemannPort));
      riemannClient = new RiemannClient(transport);
      if (debug) {
        System.err.println(String.format("%s.start: connecting", this));
      }
      riemannClient.connect();
      if (debug) {
        System.err.println(String.format("%s.start: connected", this));
      }
    }
    catch (IOException ex) {
      if (debug) {
        System.err.println(String.format("%s: Error initializing: %s", this, ex));
      }
      throw new RuntimeException(ex);
    }
    super.start();
  }

  public void stop() {
    if (debug) {
      System.err.println(String.format("%s.stop()", this));
    }
    if (riemannClient != null) {
      try {
        riemannClient.disconnect();
      }
      catch (IOException ex) {
        // do nothing, it's ok
      }
    }
    super.stop();
  }

  public String toString () {
    return String.format(
        "RiemannAppender{hashCode=%s;serviceName=%s;riemannHostname=%s;riemannPort=%s;hostname=%s}",
        hashCode(),
        serviceName,
        riemannHostname,
        riemannPort,
        hostname);
  }

  private String getStackTraceFromEvent( ILoggingEvent logEvent ) {
    IThrowableProxy throwable = logEvent.getThrowableProxy();
    if ( null != throwable && null != throwable.getStackTraceElementProxyArray() ) {
      StringBuilder sb = new StringBuilder();
      for ( StackTraceElementProxy elt : throwable.getStackTraceElementProxyArray() ) {
        sb.append(elt.toString());
        sb.append("\n");
      }
      return sb.toString();
    }

    if (logEvent.getCallerData() != null) {
      if (debug) {
        System.err.println(String.format("%s.append: falling back to appender stacktrace: ", this));
      }
      StringBuilder sb = new StringBuilder();
      for ( StackTraceElement elt : logEvent.getCallerData()) {
        sb.append(elt);
        sb.append("\n");
      }
      return sb.toString();
    }

    return null;
  }

  protected synchronized void append(E event) {
    timesCalled.incrementAndGet();
    ILoggingEvent logEvent = (ILoggingEvent) event;
    EventDSL rEvent;
    try {
      rEvent = riemannClient.event().
        service(serviceName).
        host(hostname).
        state(logEvent.getLevel().levelStr).
        attribute("logger", logEvent.getLoggerName()).
        description(logEvent.getFormattedMessage());

      if (logEvent.getMarker() != null) {
        rEvent.tags(logEvent.getMarker().getName());
      }

      for (Map.Entry<String, String> entry : logEvent.getMDCPropertyMap().entrySet()) {
        rEvent.attribute(entry.getKey(), entry.getValue());
      }

      String stInfo = getStackTraceFromEvent(logEvent);
      if (null != stInfo) {
        rEvent.attribute("stacktrace", stInfo);
      }

      try {
        if (debug) {
          System.err.println(String.format("%s.append: sending riemann event: %s", this, rEvent));
        }
        rEvent.send();
        if (debug) {
          System.err.println(String.format("%s.append(logEvent): sent to riemann %s:%s", this, riemannHostname, riemannPort));
        }
      }
      catch (Exception ex) {
        if (debug) {
          System.err.println(String.format(
                "%s: Error sending event %s",
                this,
                ex));
          ex.printStackTrace();
        }

        riemannClient.reconnect();
        if (null != rEvent) {
          rEvent.send();
        }
      }
    }
    catch (Exception ex) {
      // do nothing
      if (debug) {
        System.err.println(String.format("RiemannAppender.append: Error during append(): %s", ex));
        ex.printStackTrace(System.err);
      }
    }
  }


  public void setServiceName (String s) {
    serviceName = s;
  }

  public void setRiemannHostname (String s) {
    riemannHostname = s;
  }

  public void setRiemannPort (String s) {
    riemannPort = s;
  }

  public void setHostname (String s) {
    hostname = s;
  }

  public void setDebug (String s) {
    debug = "true".equals(s);
  }
}

@@ -2357,24 +2357,50 @@
 ogEvent ) %7B%0A
+    String result = null;%0A
     IThrowab
@@ -2457,17 +2457,16 @@
     if (
- 
 null != 
@@ -2478,113 +2478,235 @@
 able
- && null != throwable.getStackTraceElementProxyArray() ) %7B%0A      StringBuilder sb = new StringBuilder();%0A
+) %7B%0A      String firstLine = String.format(%22%25s: %25s%5Cn%22, throwable.getClassName(), throwable.getMessage());%0A      StringBuilder sb = new StringBuilder(firstLine);%0A      if (null != throwable.getStackTraceElementProxyArray()) %7B%0A  
     
@@ -2795,41 +2795,8 @@
 %7B%0A  
-      sb.append(elt.toString());%0A
     
@@ -2815,356 +2815,68 @@
 d(%22%5C
-n
+t
 %22)
-;
 %0A      
-%7D%0A
       
-return sb.toString();%0A    %7D%0A%0A    if (logEvent.getCallerData() != null) %7B%0A      if (debug) %7B%0A        System.err.println(String.format(%22%25s.append: falling back to appender stacktrace: %22, this));%0A      %7D%0A      StringBuilder sb = new StringBuilder();
+.append(elt.toString())
 %0A      
-for ( StackTraceElement elt : logEvent.getCallerData()) %7B%0A        sb
+      
 .append(
 elt)
@@ -2871,19 +2871,20 @@
 .append(
-elt
+%22%5Cn%22
 );%0A     
@@ -2890,24 +2890,9 @@
     
-sb.append(%22%5Cn%22);
+%7D
 %0A   
@@ -2904,20 +2904,22 @@
       re
-turn
+sult =
  sb.toSt
@@ -2928,25 +2928,24 @@
 ng();%0A    %7D%0A
-%0A
     return n
@@ -2943,20 +2943,22 @@
  return 
-null
+result
 ;%0A  %7D%0A%0A 

Change handling of the `:stacktrace` in the Riemann event: - only include a `:stacktrace` if the log event includes an exception - prepend the exception type and message to the stacktrace as the first line - indent all subsequent lines with a tab