问题
I am using groovy configuration with logback. Occasionally, I will log a directory or file location, and I'd like it to come up in my HTML log as a link. Here is what my configuration looks like currently.
appender("htmlLog", FileAppender) {
file = "${logPath}/${logName}.html"
append = false
encoder(LayoutWrappingEncoder) {
layout("ch.qos.logback.classic.html.HTMLLayout"){
pattern = "%d{yyyy/MM/dd HH:mm:ss}%-5p%logger{0}%m"
}
}
}
Anyone have a thought as to how I could get this?
回答1:
There are two obstacles to generating anchor tags or any other HTML within the table. I'm working against logback 1.2.3
First you need a way to convert your message, looking for paths and replacing them with anchors. Creating custom converters that you can use from the pattern is straightforward and documented here. My crude implementation looks like this, you'll probably want to modify the path detection to suit you:
package ch.qos.logback.classic.html;
import ch.qos.logback.classic.pattern.ClassicConverter;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.helpers.Transform;
public class LinkConverter extends ClassicConverter {
public String convert(ILoggingEvent iLoggingEvent) {
String message = iLoggingEvent.getMessage();
message = Transform.escapeTags(message);
message = message.replaceAll(" (/\\S+)", " <a href=\"$1\">file://$1</a>");
return message;
}
}
This is attempting to escape any suspicious characters before replacing strings like /path/to/thing
with an anchor tag.
Secondly, the HTMLLayout escapes everything, this is so it doesn't generate a malformed table and improves security (scripts can't be injected etc). So even with your new converter wired up and referenced correctly HTMLLayout will escape the anchor.
To get around this I extended HTMLLayout, unfortunately you have to override the guts of the class and put it in the same package to access package private fields.
All you want to change is the escaping line, I changed it to String s = c.getClass().equals(LinkConverter.class) ? c.convert(event): Transform.escapeTags(c.convert(event));
to try and minimise the impact.
Here is the full implementation:
package ch.qos.logback.classic.html;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.CoreConstants;
import ch.qos.logback.core.helpers.Transform;
import ch.qos.logback.core.pattern.Converter;
public class UnsafeHTMLLayout extends HTMLLayout{
public String doLayout(ILoggingEvent event) {
StringBuilder buf = new StringBuilder();
this.startNewTableIfLimitReached(buf);
boolean odd = true;
if((this.counter++ & 1L) == 0L) {
odd = false;
}
String level = event.getLevel().toString().toLowerCase();
buf.append(CoreConstants.LINE_SEPARATOR);
buf.append("<tr class=\"");
buf.append(level);
if(odd) {
buf.append(" odd\">");
} else {
buf.append(" even\">");
}
buf.append(CoreConstants.LINE_SEPARATOR);
for(Converter c = this.head; c != null; c = c.getNext()) {
this.appendEventToBuffer(buf, c, event);
}
buf.append("</tr>");
buf.append(CoreConstants.LINE_SEPARATOR);
if(event.getThrowableProxy() != null) {
this.throwableRenderer.render(buf, event);
}
return buf.toString();
}
private void appendEventToBuffer(StringBuilder buf, Converter<ILoggingEvent> c, ILoggingEvent event) {
buf.append("<td class=\"");
buf.append(this.computeConverterName(c));
buf.append("\">");
String s = c.getClass().equals(LinkConverter.class) ? c.convert(event): Transform.escapeTags(c.convert(event));
buf.append(s);
buf.append("</td>");
buf.append(CoreConstants.LINE_SEPARATOR);
}
}
My final logback configuration looks like this:
import ch.qos.logback.classic.html.LinkConverter
conversionRule("linkEscaper", LinkConverter.class)
appender("htmlLog", FileAppender) {
file = "/tmp/out.html"
append = false
encoder(LayoutWrappingEncoder) {
layout("ch.qos.logback.classic.html.UnsafeHTMLLayout"){
pattern = "%d{yyyy/MM/dd HH:mm:ss}%-5p%logger{0}%linkEscaper"
}
}
}
root(INFO, ["htmlLog"])
Here's my repo with this code.
来源:https://stackoverflow.com/questions/44637032/format-links-in-logback