001/*
002 * Licensed under the Apache License, Version 2.0 (the "License");
003 * you may not use this file except in compliance with the License.
004 * You may obtain a copy of the License at
005 *
006 * http://www.apache.org/licenses/LICENSE-2.0
007 *
008 * Unless required by applicable law or agreed to in writing, software
009 * distributed under the License is distributed on an "AS IS" BASIS,
010 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
011 * See the License for the specific language governing permissions and
012 * limitations under the License.
013 */
014package org.atteo.moonshine.firebuglogger;
015
016import java.io.IOException;
017import java.io.StringWriter;
018
019import javax.servlet.http.HttpServletResponse;
020
021import org.codehaus.jackson.JsonFactory;
022import org.codehaus.jackson.JsonGenerator;
023
024import ch.qos.logback.classic.PatternLayout;
025import ch.qos.logback.classic.spi.ILoggingEvent;
026import ch.qos.logback.core.Context;
027import ch.qos.logback.core.UnsynchronizedAppenderBase;
028
029public class FireBugAppender extends UnsynchronizedAppenderBase<ILoggingEvent> {
030    private static ThreadLocal<HttpServletResponse> response = new ThreadLocal<>();
031    private static ThreadLocal<Integer> headerNumber = new ThreadLocal<>();
032
033    private PatternLayout layout;
034
035    public FireBugAppender() {
036        layout = new PatternLayout();
037        layout.setPattern("%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n");
038    }
039
040    @Override
041    public void setContext(Context context) {
042        super.setContext(context);
043        layout.setContext(context);
044    }
045
046    @Override
047    public void start() {
048        super.start();
049        layout.start();
050    }
051
052    public static void setServletResponse(HttpServletResponse resp) {
053        response.set(resp);
054        headerNumber.set(0);
055    }
056
057    @Override
058    protected void append(ILoggingEvent event) {
059        if (response.get() == null) {
060            return;
061        }
062
063        event.prepareForDeferredProcessing();
064
065        String message = layout.doLayout(event);
066        String filename = null;
067        Integer line = null;
068        if (event.hasCallerData()) {
069            StackTraceElement callerData = event.getCallerData()[0];
070            filename = callerData.getFileName();
071            line = callerData.getLineNumber();
072        }
073
074        StringWriter writer = new StringWriter();
075        try {
076            JsonGenerator builder = new JsonFactory().createJsonGenerator(writer);
077            builder.writeStartArray();
078            builder.writeStartObject();
079            builder.writeFieldName("Type");
080            builder.writeString(event.getLevel().toString());
081            builder.writeFieldName("File");
082            if (filename != null) {
083                builder.writeString(filename);
084            } else {
085                builder.writeNull();
086            }
087            builder.writeFieldName("Line");
088            if (line != null) {
089                builder.writeNumber(line);
090            } else {
091                builder.writeNull();
092            }
093            builder.writeFieldName("Label");
094            builder.writeString("[S]");
095            builder.writeEndObject();
096            builder.writeString(message);
097            builder.writeEndArray();
098            builder.flush();
099        } catch (IOException e) {
100            throw new RuntimeException(e);
101        }
102
103        HttpServletResponse resp = response.get();
104        resp.setHeader("X-Wf-Protocol-1", "http://meta.wildfirehq.org/Protocol/JsonStream/0.2");
105        resp.setHeader("X-Wf-1-Plugin-1",
106                "http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/0.3");
107        resp.setHeader("X-Wf-1-Structure-1",
108                "http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1");
109
110        int number = headerNumber.get();
111        int offset = 0;
112        int size = writer.getBuffer().length();
113        while (size > offset) {
114            StringBuilder header = new StringBuilder();
115            if (offset == 0) {
116                header.append(size);
117            }
118            header.append("|");
119            int end = offset + 4000;
120            if (end > size) {
121                end = size;
122            }
123            header.append(writer.getBuffer().substring(offset, end));
124            header.append("|");
125            offset = end;
126            if (offset != size) {
127                header.append("\\");
128            }
129
130            resp.setHeader("X-Wf-1-1-1-" + number, header.toString());
131            number++;
132        }
133
134
135        headerNumber.set(number);
136    }
137}