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}