/*
 * This file is part of the logback-journal project.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.gnieh.logback;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.classic.spi.StackTraceElementProxy;
import ch.qos.logback.core.AppenderBase;

/**
 * An appender that send the events to systemd journal
 *
 * @author Lucas Satabin
 *
 */
public class SystemdJournalAppender extends AppenderBase<ILoggingEvent> {

    boolean logLocation = true;

    boolean logException = true;

    boolean logThreadName = true;

    String syslogIdentifier = "";

    @Override
    protected void append(ILoggingEvent event) {
        try {
            // get the message id if any
            Map<String, String> mdc = event.getMDCPropertyMap();

            List<Object> messages = new ArrayList<>();
            // the formatted human readable message
            messages.add(event.getFormattedMessage());

            // the log level
            messages.add("PRIORITY=%i");
            messages.add(levelToInt(event.getLevel()));

            if (event.getThrowableProxy() != null) {
                StackTraceElementProxy[] stack = event.getThrowableProxy()
                        .getStackTraceElementProxyArray();
                if (stack.length > 0) {

                    // the location information if any is available and it is
                    // enabled
                    if (logLocation) {
                        StackTraceElement elt = stack[0].getStackTraceElement();
                        messages.add("CODE_FILE=%s");
                        messages.add(elt.getFileName());
                        messages.add("CODE_LINE=%i");
                        messages.add(elt.getLineNumber());
                        messages.add("CODE_FUNC=%s.%s");
                        messages.add(elt.getClassName());
                        messages.add(elt.getMethodName());
                    }

                    // if one wants to log the exception name and message, just
                    // do it
                    if (logException) {
                        messages.add("EXN_NAME=%s");
                        messages.add(event.getThrowableProxy().getClassName());
                        messages.add("EXN_MESSAGE=%s");
                        messages.add(event.getThrowableProxy().getMessage());
                    }
                }
            }

            // log thread name if enabled
            if (logThreadName) {
                messages.add("THREAD_NAME=%s");
                messages.add(event.getThreadName());
            }

            // add a message id field if any is defined for this logging event
            if (mdc.containsKey(SystemdJournal.MESSAGE_ID)) {
                messages.add("MESSAGE_ID=%s");
                messages.add(mdc.get(SystemdJournal.MESSAGE_ID));
            }

            // override the syslog identifier string if set
            if (!syslogIdentifier.isEmpty()) {
                messages.add("SYSLOG_IDENTIFIER=%s");
                messages.add(syslogIdentifier);
            }

            // the vararg list is null terminated
            messages.add(null);

            SystemdJournalLibrary journald = SystemdJournalLibrary.INSTANCE;

            journald.sd_journal_send("MESSAGE=%s", messages.toArray());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private int levelToInt(Level l) {
        switch (l.toInt()) {
        case Level.TRACE_INT:
        case Level.DEBUG_INT:
            return 7;
        case Level.INFO_INT:
            return 6;
        case Level.WARN_INT:
            return 4;
        case Level.ERROR_INT:
            return 3;
        default:
            throw new IllegalArgumentException("Unknown level value: " + l);
        }
    }

    public boolean isLogLocation() {
        return logLocation;
    }

    public void setLogLocation(boolean logLocation) {
        this.logLocation = logLocation;
    }

    public boolean isLogThreadName() {
        return logThreadName;
    }

    public void setLogThreadName(boolean logThreadName) {
        this.logThreadName = logThreadName;
    }

    public boolean isLogException() {
        return logException;
    }

    public void setLogException(boolean logException) {
        this.logException = logException;
    }

    public String getSyslogIdentifier() {
        return syslogIdentifier;
    }

    public void setSyslogIdentifier(String syslogIdentifier) {
        this.syslogIdentifier = syslogIdentifier;
    }
}

@@ -635,16 +635,54 @@
 gback;%0A%0A
+import java.io.ByteArrayOutputStream;%0A
 import j
@@ -697,24 +697,24 @@
 .ArrayList;%0A
-
 import java.
@@ -931,16 +931,60 @@
 derBase;
+%0Aimport ch.qos.logback.core.encoder.Encoder;
 %0A%0A/**%0A *
@@ -1283,16 +1283,60 @@
  = %22%22;%0A%0A
+    Encoder%3CILoggingEvent%3E encoder = null;%0A%0A
     @Ove
@@ -1562,24 +1562,25 @@
 ayList%3C%3E();%0A
+%0A
             
@@ -1619,16 +1619,53 @@
 message%0A
+            if (encoder == null)%0A    
         
@@ -1710,16 +1710,289 @@
 sage());
+%0A            else %7B%0A                ByteArrayOutputStream baos = new ByteArrayOutputStream();%0A                encoder.init(baos);%0A                encoder.doEncode(event);%0A                String message = baos.toString();%0A                messages.add(message);%0A            %7D
 %0A%0A      
@@ -5611,11 +5611,191 @@
 r;%0A    %7D
+%0A%0A    public Encoder%3CILoggingEvent%3E getEncoder() %7B%0A        return encoder;%0A    %7D%0A%0A    public void setEncoder(Encoder%3CILoggingEvent%3E encoder) %7B%0A        this.encoder = encoder;%0A    %7D
 %0A%7D%0A

Use the encoder to format messages if configured.