diff --git a/README.md b/README.md index 0615e92..41d2c85 100644 --- a/README.md +++ b/README.md @@ -102,6 +102,41 @@ default values: ``` +An complete example for logback-access + +```xml + + + somehost.com + 12201 + + UTF-8 + + + + common + + + combined + + true + Test + + application + Your-Application + + + + + + + + + +``` + + + ## GelfLayout `me.moocar.logbackgelf.GelfLayout` @@ -142,6 +177,33 @@ actually converts a log event into a GELF compatible JSON string. empty * **includeFullMDC**: See additional fields below. Default: `false` + +## GelfLayoutAccess + +`me.moocar.logbackgelf.GelfLayoutAccess` + +GelfLayoutAccess must be used if you want to use Gelf with "logback-access". For example, if you use the Tomcat LogbackValve. +http://logback.qos.ch/access.html#tomcat + +* **useThreadName**: If true, an additional field call "_threadName" + will be added to each gelf message. Its contents will be the name of + the thread. Default: `false` +* **host** The hostname of the host from which the log is being sent. + Displayed under `source` on web interface. Default: + `getLocalHostName()` +* **shortMessageLayout**: The + [Layout/logback-access](http://logback.qos.ch/manual/layouts.html#logback-access) used to create + the gelf `short_message` field. Shows up in the message column of + the log summary in the web interface. Default: `"combined"` + ([AccessPatternLayout](http://logback.qos.ch/manual/layouts.html#AccessPatternLayout)) +* **fullMessageLayout**: The + [Layout/logback-access](http://logback.qos.ch/manual/layouts.html#logback-access) used to create + the gelf `full_message` field. Shows up in the message field of the + log details in the web interface. Default: `"combined"` + ([AccessPatternLayout](http://logback.qos.ch/manual/layouts.html#AccessPatternLayout)) +* **staticFields**: See static fields below. Note, now that facility + is deprecated, use this to set a facility Default: empty + ## Transports Both UDP and TCP transports are supported. UDP is the recommended diff --git a/src/main/java/me/moocar/logbackgelf/GelfLayoutAccess.java b/src/main/java/me/moocar/logbackgelf/GelfLayoutAccess.java new file mode 100644 index 0000000..b1d8219 --- /dev/null +++ b/src/main/java/me/moocar/logbackgelf/GelfLayoutAccess.java @@ -0,0 +1,185 @@ +package me.moocar.logbackgelf; + +import java.net.SocketException; +import java.net.UnknownHostException; +import java.util.HashMap; +import java.util.Map; + +import me.moocar.logbackgelf.Field; +import me.moocar.logbackgelf.InternetUtils; +import ch.qos.logback.access.PatternLayout; +import ch.qos.logback.access.spi.IAccessEvent; +import ch.qos.logback.core.Layout; +import ch.qos.logback.core.LayoutBase; + +import com.google.gson.FieldNamingPolicy; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +/** + * Responsible for formatting a log event into a GELF JSON string + */ +public class GelfLayoutAccess extends LayoutBase { + + private final String DEFAULT_FULL_MESSAGE_PATTERN = "combined"; + private final String DEFAULT_SHORT_MESSAGE_PATTERN = "combined"; + + private boolean useThreadName = false; + + private Map staticFields = new HashMap(); + private String host = getLocalHostName(); + private final Gson gson; + private Layout fullMessageLayout; + private Layout shortMessageLayout; + + public GelfLayoutAccess() { + + // Init GSON for underscores + GsonBuilder gsonBuilder = new GsonBuilder(); + gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES); + this.gson = gsonBuilder.create(); + } + + @Override + public void start() { + + if (fullMessageLayout == null) { + this.fullMessageLayout = initNewPatternLayout(DEFAULT_FULL_MESSAGE_PATTERN); + } + + if (shortMessageLayout == null) { + this.shortMessageLayout = initNewPatternLayout(DEFAULT_SHORT_MESSAGE_PATTERN); + } + + super.start(); + } + + private PatternLayout initNewPatternLayout(String pattern) { + PatternLayout layout = new PatternLayout(); + layout.setPattern(pattern); + layout.setContext(this.getContext()); + layout.start(); + return layout; + } + + @Override + public String doLayout(E event) { + return gson.toJson(mapFields(event)); + } + + /** + * Creates a map of properties that represent the GELF message. + * @param logEvent The log event + * @return map of gelf properties + */ + private Map mapFields(E logEvent) { + Map map = new HashMap(); + + map.put("host", host); + + map.put("full_message", fullMessageLayout.doLayout(logEvent)); + map.put("short_message", shortMessageLayout.doLayout(logEvent)); + + map.put("timestamp", logEvent.getTimeStamp() / 1000.0); + + map.put("version", "1.1"); + + additionalFields(map, logEvent); + + staticAdditionalFields(map); + + return map; + } + + /** + * Converts the additional fields into proper GELF JSON + * @param map The map of additional fields + * @param eventObject The Logging event that we are converting to GELF + */ + private void additionalFields(Map map, IAccessEvent eventObject) { + + if (useThreadName) { + map.put("_threadName", eventObject.getThreadName()); + } + + } + + private void staticAdditionalFields(Map map) { + + for (String key : staticFields.keySet()) { + map.put(key, (staticFields.get(key))); + } + } + + private String getLocalHostName() { + try { + return InternetUtils.getLocalHostName(); + } catch (SocketException e) { + return "UNKNOWN"; + } catch (UnknownHostException e) { + return "UNKNOWN"; + } + } + + // ////////// Logback Property Getter/Setters //////////////// + + /** + * If true, an additional field call "_threadName" will be added to each gelf message. Its contents will be the Name of the thread. + * Defaults to "false". + */ + public boolean isUseThreadName() { + return useThreadName; + } + + public void setUseThreadName(boolean useThreadName) { + this.useThreadName = useThreadName; + } + + /** + * static additional fields to add to every gelf message. Key is the additional field key (and should thus begin with an underscore). + * The value is a static string. + */ + public Map getStaticFields() { + return staticFields; + } + + public void setStaticFields(Map staticFields) { + this.staticFields = staticFields; + } + + /** + * Override the local host using a config option + * @return the local host (defaults to getLocalHost() if not overridden in config + */ + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + /** + * Add a static field. A static field is a key/value pair that should be sent in each Gelf message. This supercedes static additional + * fields, which can't have colon characters in their value. + */ + public void addStaticField(Field entry) { + staticFields.put(entry.getKey(), entry.getValue()); + } + + public Layout getFullMessageLayout() { + return fullMessageLayout; + } + + public void setFullMessageLayout(Layout fullMessageLayout) { + this.fullMessageLayout = fullMessageLayout; + } + + public Layout getShortMessageLayout() { + return shortMessageLayout; + } + + public void setShortMessageLayout(Layout shortMessageLayout) { + this.shortMessageLayout = shortMessageLayout; + } +}