├── .gitignore ├── src └── main │ └── java │ └── com │ └── twimba │ └── fluentd4log4j │ ├── ManagedRawSocketSender.java │ └── FluentdAppender.java ├── README.md └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | .classpath 2 | .project 3 | -------------------------------------------------------------------------------- /src/main/java/com/twimba/fluentd4log4j/ManagedRawSocketSender.java: -------------------------------------------------------------------------------- 1 | package com.twimba.fluentd4log4j; 2 | 3 | import org.fluentd.logger.sender.RawSocketSender; 4 | import org.fluentd.logger.sender.Reconnector; 5 | 6 | public class ManagedRawSocketSender extends RawSocketSender 7 | { 8 | 9 | private boolean closed = false; 10 | 11 | public ManagedRawSocketSender() 12 | { 13 | super(); 14 | } 15 | 16 | public ManagedRawSocketSender(String host, int port, int timeout, int bufferCapacity, Reconnector reconnector) 17 | { 18 | super(host, port, timeout, bufferCapacity, reconnector); 19 | if (isClosed()) 20 | { 21 | throw new RuntimeException("Unable to start the fluentd sender."); 22 | } 23 | } 24 | 25 | public ManagedRawSocketSender(String host, int port, int timeout, int bufferCapacity) 26 | { 27 | super(host, port, timeout, bufferCapacity); 28 | } 29 | 30 | public ManagedRawSocketSender(String host, int port) 31 | { 32 | super(host, port); 33 | } 34 | 35 | @Override 36 | public void close() 37 | { 38 | super.close(); 39 | closed = true; 40 | } 41 | 42 | public boolean isClosed() 43 | { 44 | return closed; 45 | } 46 | 47 | public void setClosed(boolean closed) 48 | { 49 | this.closed = closed; 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fluentd4log4j 2 | 3 | [![Join the chat at https://gitter.im/tuxetuxe/fluentd4log4j](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/tuxetuxe/fluentd4log4j?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 4 | _A Log4J appender to push log events to a fluentd server._ 5 | 6 | ## How to Use 7 | 8 | ### Maven dependency 9 | ``` 10 | 11 | com.twimba 12 | fluentd4log4j 13 | 1.0 14 | 15 | ``` 16 | 17 | ### Configuration 18 | | property | default value | Description | 19 | | ------------- |------------------| -------------| 20 | | mdcKeys | "" | The MDC keys (comma separated) that should be added to the log structure. | 21 | | tagPrefix | ""| The fluentd tag prefix to be used | 22 | | tag | "log" | The fluentd tag to be used in all the log messages sent there | 23 | | host | "localhost" | The fluentd server host to where to send the log messages. | 24 | | port | 24224 | The fluentd server port to where to send the log messages. | 25 | | timeout | 15000 (15s) | The timeout (in milliseconds) to connect to the fluentd server| 26 | | bufferCapacity | 1048576 (1Mb) | The socket buffer capacity to connect to the fluentd server | 27 | | useConstantDelayReconnector| false | Switch from the default Exponential Delay reconnector to a constant delay reconnector | 28 | 29 | ### Example 30 | **log4j.properties** 31 | ``` 32 | log4j.rootLogger=info, fluentd 33 | log4j.appender.fluentd=com.twimba.fluentd4log4j.FluentdAppender.java 34 | log4j.appender.fluentd.mdcKeys=user,host,whatever 35 | log4j.appender.fluentd.host=fluentdhost 36 | ``` 37 | **fluentd configuration** 38 | ``` 39 | 40 | type forward 41 | port 24224 42 | 43 | 44 | 45 | type stdout 46 | 47 | ``` 48 | 49 | ## License 50 | This is available in the Apache Licence 2.0 51 | http://www.tldrlegal.com/license/apache-license-2.0-(apache-2.0) 52 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | 5 | org.sonatype.oss 6 | oss-parent 7 | 7 8 | 9 | 10 | com.twimba 11 | fluentd4log4j 12 | 1.0.1 13 | jar 14 | 15 | Fluentd Log4j Appender 16 | Integration between fluentd and log4j 17 | 18 | 19 | 1.2.17 20 | 0.2.11 21 | 22 | 23 | 24 | 25 | org.fluentd 26 | fluent-logger 27 | ${fluentd.version} 28 | 29 | 30 | log4j 31 | log4j 32 | ${log4j.version} 33 | 34 | 35 | 36 | 37 | 38 | The Apache Software License, Version 2.0 39 | http://www.apache.org/licenses/LICENSE-2.0.txt 40 | repo 41 | 42 | 43 | 44 | http://www.twimba.com/fluentd4log4j 45 | 46 | 47 | scm:git:git@github.com:tuxetuxe/fluentd4log4j.git 48 | scm:git:git@github.com:tuxetuxe/fluentd4log4j.git 49 | git@github.com:tuxetuxe/fluentd4log4j.git 50 | 51 | 52 | 53 | 54 | tuxetuxe 55 | Luis Santos 56 | luis.santos+fluentd4log4j@twimba.com 57 | https://github.com/tuxetuxe 58 | 59 | 60 | 61 | 62 | Twimba 63 | 64 | 65 | -------------------------------------------------------------------------------- /src/main/java/com/twimba/fluentd4log4j/FluentdAppender.java: -------------------------------------------------------------------------------- 1 | package com.twimba.fluentd4log4j; 2 | 3 | import java.net.InetAddress; 4 | import java.net.UnknownHostException; 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | import org.apache.log4j.AppenderSkeleton; 9 | import org.apache.log4j.helpers.LogLog; 10 | import org.apache.log4j.spi.LoggingEvent; 11 | import org.fluentd.logger.Config; 12 | import org.fluentd.logger.FluentLogger; 13 | import org.fluentd.logger.sender.ConstantDelayReconnector; 14 | import org.fluentd.logger.sender.ExponentialDelayReconnector; 15 | import org.fluentd.logger.sender.Reconnector; 16 | 17 | public class FluentdAppender extends AppenderSkeleton 18 | { 19 | 20 | private FluentLogger fluentLogger; 21 | 22 | /** 23 | * The MDC keys (comma separated) that should be added to the log structure. 24 | * Default: none 25 | */ 26 | private String mdcKeys = ""; 27 | 28 | /** 29 | * The fluentd tag prefix to be used 30 | * Default: "" (empty string) 31 | */ 32 | private String tagPrefix = ""; 33 | 34 | /** 35 | * The fluentd tag to be used in all the log messages sent there. 36 | * Default: "log" 37 | */ 38 | private String tag = "log"; 39 | /** 40 | * The fluentd server host to where to send the log messages. 41 | * Default: "localhost" 42 | */ 43 | private String host = "localhost"; 44 | /** 45 | * The fluentd server port to where to send the log messages. 46 | * Default: 24224 47 | */ 48 | private int port = 24224; 49 | /** 50 | * The timeout (in milliseconds) to connect to the fluentd server 51 | * Default: 15000 (15s) 52 | */ 53 | private int timeout = 15 * 1000; // 15s 54 | /** 55 | * The socket buffer capacity to connect to the fluentd server 56 | * Default: 1048576 (1Mb) 57 | */ 58 | private int bufferCapacity = 1024 * 1024; // 1M 59 | 60 | /** 61 | * Switch from the default Exponential Delay reconnector to a constant delay reconnector 62 | * Default: false (use the Exponential Delay reconnector) 63 | */ 64 | private boolean useConstantDelayReconnector = false; 65 | 66 | /** 67 | * Adds a value with the current hostname. This value key is "host" 68 | * 69 | * Default: false 70 | */ 71 | private boolean addHostname = false; 72 | 73 | @Override 74 | public void activateOptions() 75 | { 76 | Reconnector reconnector = null; 77 | 78 | if (useConstantDelayReconnector) 79 | { 80 | reconnector = new ConstantDelayReconnector(); 81 | } 82 | else 83 | { 84 | reconnector = new ExponentialDelayReconnector(); 85 | } 86 | 87 | System.setProperty(Config.FLUENT_SENDER_CLASS, ManagedRawSocketSender.class.getName()); 88 | 89 | try 90 | { 91 | fluentLogger = FluentLogger.getLogger(tagPrefix, host, port, timeout, bufferCapacity, reconnector); 92 | LogLog.warn("FluentdAppender connected to fluentd! (host=" + host + ", port=" + port + ",timeout=" + timeout + ",bufferCapacity=" 93 | + bufferCapacity + ",tagPrefix=" + tagPrefix + ")"); 94 | } 95 | catch (Exception e) 96 | { 97 | LogLog.warn("FluentdAppender NOT connected to fluentd! (host=" + host + ", port=" + port + ",timeout=" + timeout + ",bufferCapacity=" 98 | + bufferCapacity + ",tagPrefix=" + tagPrefix + ")"); 99 | fluentLogger = null; 100 | } 101 | } 102 | 103 | @Override 104 | public void close() 105 | { 106 | if (fluentLogger != null) 107 | { 108 | fluentLogger.flush(); 109 | fluentLogger.close(); 110 | } 111 | } 112 | 113 | @Override 114 | public boolean requiresLayout() 115 | { 116 | return false; 117 | } 118 | 119 | @Override 120 | protected void append(LoggingEvent event) 121 | { 122 | if (fluentLogger == null) 123 | { 124 | // Ups! Something very wrong is going on here! Bail out! 125 | LogLog.debug("FluentdAppender has no fluentLogger. Please check your configuration and/or logs for errors!"); 126 | return; 127 | } 128 | Map data = new HashMap(); 129 | data.put("message", event.getMessage()); 130 | data.put("loggerClass", event.getFQNOfLoggerClass()); 131 | data.put("level", event.getLevel().toString()); 132 | data.put("locationInformation", event.getLocationInformation().fullInfo); 133 | data.put("logger", event.getLoggerName()); 134 | data.put("threadName", event.getThreadName()); 135 | if (event.getThrowableStrRep() != null) 136 | { 137 | data.put("throwableInformation", event.getThrowableStrRep()); 138 | } 139 | if (event.getNDC() != null) 140 | { 141 | data.put("NDC", event.getNDC()); 142 | } 143 | for (String mdcKey : mdcKeys.split(",")) 144 | { 145 | Object value = event.getMDC(mdcKey); 146 | if (value != null) 147 | { 148 | data.put(mdcKey, value); 149 | } 150 | } 151 | 152 | if (addHostname) 153 | { 154 | try 155 | { 156 | data.put("hostname", InetAddress.getLocalHost().getHostName()); 157 | } 158 | catch (UnknownHostException e) 159 | { 160 | LogLog 161 | .warn("FluentdAppender is unable to get the current hostname. Please check your configuration and/or disable the addition of the hostname!"); 162 | } 163 | } 164 | fluentLogger.log(tag, data); 165 | } 166 | 167 | // 168 | // Getters and Setters 169 | // 170 | 171 | public FluentLogger getFluentLogger() 172 | { 173 | return fluentLogger; 174 | } 175 | 176 | public void setFluentLogger(FluentLogger fluentLogger) 177 | { 178 | this.fluentLogger = fluentLogger; 179 | } 180 | 181 | public String getMdcKeys() 182 | { 183 | return mdcKeys; 184 | } 185 | 186 | public void setMdcKeys(String mdcKeys) 187 | { 188 | this.mdcKeys = mdcKeys; 189 | } 190 | 191 | public String getTagPrefix() 192 | { 193 | return tagPrefix; 194 | } 195 | 196 | public void setTagPrefix(String tagPrefix) 197 | { 198 | this.tagPrefix = tagPrefix; 199 | } 200 | 201 | public String getTag() 202 | { 203 | return tag; 204 | } 205 | 206 | public void setTag(String tag) 207 | { 208 | this.tag = tag; 209 | } 210 | 211 | public String getHost() 212 | { 213 | return host; 214 | } 215 | 216 | public void setHost(String host) 217 | { 218 | this.host = host; 219 | } 220 | 221 | public int getPort() 222 | { 223 | return port; 224 | } 225 | 226 | public void setPort(int port) 227 | { 228 | this.port = port; 229 | } 230 | 231 | public int getTimeout() 232 | { 233 | return timeout; 234 | } 235 | 236 | public void setTimeout(int timeout) 237 | { 238 | this.timeout = timeout; 239 | } 240 | 241 | public int getBufferCapacity() 242 | { 243 | return bufferCapacity; 244 | } 245 | 246 | public void setBufferCapacity(int bufferCapacity) 247 | { 248 | this.bufferCapacity = bufferCapacity; 249 | } 250 | 251 | public boolean isUseConstantDelayReconnector() 252 | { 253 | return useConstantDelayReconnector; 254 | } 255 | 256 | public void setUseConstantDelayReconnector(boolean useConstantDelayReconnector) 257 | { 258 | this.useConstantDelayReconnector = useConstantDelayReconnector; 259 | } 260 | 261 | public boolean isAddHostname() 262 | { 263 | return addHostname; 264 | } 265 | 266 | public void setAddHostname(boolean addHostname) 267 | { 268 | this.addHostname = addHostname; 269 | } 270 | 271 | } --------------------------------------------------------------------------------