├── .gitignore ├── LICENSE ├── README.md ├── build.gradle ├── gradle.properties.example ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── src ├── main └── java │ └── org │ └── apache │ └── logging │ └── log4j │ └── core │ ├── LogStashLogEvent.java │ ├── jackson │ ├── LogStashLog4jJsonModule.java │ ├── LogStashLog4jJsonObjectMapper.java │ └── LogStashLogEventMixIn.java │ └── layout │ ├── LogStashJSONLayout.java │ └── LogStashJacksonFactory.java └── test ├── java ├── net │ └── logstash │ │ └── logging │ │ └── log4j2 │ │ └── core │ │ └── layout │ │ └── LogStashJSONLayoutIT.java └── org │ └── apache │ └── logging │ └── log4j │ └── core │ └── layout │ └── LogStashJSONLayoutJacksonIT.java └── resources └── log4j2.xml /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | .gradle 3 | .settings 4 | .classpath 5 | .project 6 | test-output 7 | gradle.properties 8 | /bin 9 | gradle.properties.off 10 | /target/ 11 | .idea/ 12 | *.iml -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2013 Liaison, Inc 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | log4j2-logstash-jsonevent-layout 2 | ================================ 3 | 4 | Log4J2 Layout as a Logstash "json_event" 5 | 6 | Note: Currently alpha: under active development and review. Input and contributions most welcome! 7 | 8 | # Overview 9 | 10 | This is a log4j2 layout that produces json that is compliant to logstash v1 spec. JSON produced is a serialization of a given log4j2 LogEvent and is intentionally very similar to that produced by the default log4j2 [JSONLayout](http://logging.apache.org/log4j/2.x/manual/layouts.html). You may use this layout out of the box to connect your java application to a logstash server with maximal speed and minimal redundant processing. 11 | 12 | This layout is fast, flexible, and other superlatives starting with f. 13 | 14 | (see http://logging.apache.org/log4j/2.x/manual/layouts.html and http://logstash.net/) 15 | 16 | # Download 17 | Sorry! Until we have a maven repository you'll have to build it yourself! I'm working on this presently! 18 | 19 | # Getting Started 20 | 21 | You'll need an application that uses [log4j2](http://logging.apache.org/) and an install of [logstash](http://logstash.net/). Explaining these perquisites is an exercise left for the reader, but we'll provide you a configuration sample. 22 | 23 | (see http://logging.apache.org/ and http://logstash.net/) 24 | 25 | ## Simple Configuration: Log4j2 SocketAppender to Logstash TCP Input 26 | 27 | This is the simplest form of the intended use. Once you have this configuration working, you should be able to customize the configuration to your specific needs. 28 | 29 | Log4j2 is configured to connect to logstash via a TCP by using a standard SocketAppender with our LogStashJSONLayout in order to produce logstash v1 spec json. Logstash is configure to receive TCP connections and expect logstash v1 spec json. 30 | 31 | 32 | Example Log4j2 log4j2.xml: 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | Example logstash configuration (later we refer to this as file tcp-logstash.conf): 69 | 70 | input { 71 | tcp { 72 | codec => json_line { charset => "UTF-8" } 73 | # 4560 is default log4j socket appender port 74 | port => 4560 75 | } 76 | } 77 | output { 78 | stdout { codec => rubydebug } 79 | } 80 | 81 | 82 | Note, this example configuration expects port 4560 to be accessible, so make sure your firewall rules allow for this. You may of course opt to change the port to a value of your choosing, just make sure to update both logstash and log4j2 configuration. 83 | Note, tcp input has buffer underrun and overrun conditions that prevent use with non-line delineated codecs and therefore json codec does not work. 84 | 85 | ### Example output 86 | You should see in your logstash console a message like: 87 | 88 | { 89 | "@version" : "1", 90 | "@timestamp" : "2015-07-28T15:24:53.386-07:00", 91 | "timeMillis" : 1438122293386, 92 | "thread" : "main", 93 | "level" : "DEBUG", 94 | "loggerName" : "org.apache.logging.log4j.core.layout.LogStashJSONLayoutJacksonIT", 95 | "message" : "Test Message", 96 | "endOfBatch" : false, 97 | "loggerFqcn" : "org.apache.logging.log4j.core.layout.LogStashJSONLayoutJacksonIT", 98 | "contextMap" : [ { 99 | "key" : "Foo", 100 | "value" : "Bar" 101 | }, { 102 | "key" : "A", 103 | "value" : "B" 104 | } ] 105 | } 106 | 107 | #### Look out for malformed messages 108 | 109 | Something is wrong with your configuration if you see something like the above but escaped and wedged into the message element, example: 110 | 111 | { 112 | "message" => "{\"@version\":\"1\",\"@timestamp\":\"2014-04-29T16:21:03.554-07:00\",\"logger\":\"com.liaison.service.resource.examples.LogStashExampleTest\",\"level\":\"ERROR\",\"thread\":\"Test worker\",\"message\":\"Going on right here\",\"LocationInfo\":{\"class\":\"com.liaison.service.resource.examples.LogStashExampleTest\",\"method\":\"testLogStashLogs\",\"file\":\"LogStashExampleTest.java\",\"line\":\"15\"},\"log\":\"THIS BLOCK IS ARBITRARY FORMAT 16:21:03.554 [Test worker] ERROR com.liaison.service.resource.examples.LogStashExampleTest - Going on right here\",\"environment_type\":\"${sys:deploy_env}\",\"cluster_name\":\"example cluster name\",\"cluster_location\":\"${sys:cluster_location}\",\"application_name\":\"${sys:application.name}\",\"application_user\":\"jeremyfranklin-ross\",\"application_version\":\"${sys:application.version}\",\"hostname\":\"${sys:hostname}\",\"environment_user\":\"jeremyfranklin-ross\",\"host_ip\":\"${sys:host_ip}\"}\r", 113 | "@version" => "1", 114 | "@timestamp" => "2014-04-29T23:21:03.099Z", 115 | "host" => "10.211.55.2:53807" 116 | } 117 | 118 | If you see this behavior it either you have specified the wrong codec in your logstash conf or log4j2 layout is producing malformed json (typically due to incorrect parameters set in log4j2 or unescaped json characters in keypair values). 119 | 120 | 121 | ## Log4j2 Logstash Layout In More Detail 122 | 123 | ### Logstash LogEvent JSON Layout: 124 | Conceptually this is a mashup between log4j2 logevent json schema and logstash event schema. The only required elements are @version and @timestamp which, as you'll note, is in logstash's native format so no modification needs to happen here. 125 | 126 | #### Log4j2 LogEvent Elements 127 | 128 | Log4j2 LogEvent elements have been expanded and a means to omit/include via configuration included. Those elements are: 129 | 130 | * logger: logger name 131 | * level: log level 132 | * thread: thread name 133 | * message: log message 134 | * ndc: the thread context stack 135 | * throwable: the thrown exception 136 | * LocationInfo (element): where the log originated 137 | * Properties (element): thread context map 138 | * log : product of sublayout, element or attribute (see below) 139 | * * : anything... see discussion on Arbitrary KeyValues 140 | 141 | #### SubLayout: 142 | Central to this implementation is the facility to use any layout as a sublayout, which is demonstrated by a patternlayout in the log4j2.xml example, above. By default, a sublayout will render as a string value, which will then be json escaped. It is possible to configure this so that it will skip the escaping, which you would do as a speed enhancement if you had a custom (sub)layout that provided pre-escaped content. It's also possible, in the realm custom layouts, to make a sublayout that provides a json element, in which case you can configure Log4j2 Logstash Layout to treat the sublayout as a element node. 143 | 144 | ** Most likely, for your purposes the pattern logger will be sufficient as a sublayout. ** 145 | 146 | #### Arbitrary Key Values Pairs 147 | This is a very important mechanism that allows you to include arbitrary key values. This is the means by which you should insert application/server context such as application name, host name, cluster name, cluster location, etc. These can be hardcoded in log4j2.xml or you can use log4j2 [Lookups](http://logging.apache.org/log4j/2.x/manual/lookups.html) to render them from configuration values. Examples are provided of both environment and system lookups are given in the sample log4j2.xml, above. 148 | 149 | ### UDP Caveat 150 | UDP can have strict size limits and ambiguous over-sized functionality. This layout does not provide a means to truncate long messages in a manner that produces valid JSON. Therefore, UDP is not appropriate for applications that are able to produce long messages such as an exception containing a stacktrace. 151 | 152 | ## Developer Discussion 153 | 154 | The implementation is fast, using StringBuilder (as opposed to a general use object serializer such as GSON or Jackson). The code is a branch of the log4j2 JSONLayout (http://logging.apache.org/log4j/2.x/log4j-core/apidocs/index.html) and so should be maintained in close parallel (review changeset of JSONLayout when upgrading log4j2 dependency version). 155 | 156 | Project Needs: YOU CAN HELP :) 157 | 158 | * Step by step example 159 | * Tests need to be fleshed out. 160 | * CI needs to be wired. 161 | * Maven Artifact needs to be published. 162 | 163 | 164 | ### Building from source 165 | We use gradle to build. Don't worry, it's embedded. 166 | 167 | To generate a jar file, use: 168 | ./gradlew clean build 169 | 170 | This will produce a jar file that you may import into your current project. For example: 171 | build/libs/log4j2-logstash-jsonevent-layout-1.0.2-SNAPSHOT.jar 172 | 173 | 174 | ### Maven Publishing 175 | We're using gradle maven-publish plugin, which is currently incubating and requires a settings.xml 176 | 177 | 178 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | apply plugin: 'maven' 3 | apply plugin: 'maven-publish' 4 | apply plugin: 'maven-publish-auth' 5 | 6 | description = 'Log4J2 Layout for outputting LogStash json_event format' 7 | 8 | apply plugin: 'java' 9 | 10 | version='4.1.0' 11 | group = 'net.logstash.log4j2' 12 | 13 | buildscript { 14 | dependencies { 15 | classpath 'org.hibernate.build.gradle:gradle-maven-publish-auth:2.0.1' 16 | } 17 | repositories { 18 | mavenLocal() 19 | mavenCentral() 20 | } 21 | } 22 | 23 | repositories { 24 | mavenLocal() 25 | mavenCentral() 26 | } 27 | 28 | publishing { 29 | publications { 30 | mavenJava(MavenPublication) { 31 | from components.java 32 | artifact sourcesJar { 33 | classifier "sources" 34 | } 35 | artifact javadocJar { 36 | classifier "javadoc" 37 | } 38 | 39 | } 40 | 41 | } 42 | repositories { 43 | maven { 44 | if (project.version.endsWith('-SNAPSHOT')) { 45 | if (project.hasProperty('snapshotRepoName')) { 46 | name "${snapshotRepoName}" 47 | } 48 | if (project.hasProperty('snapshotRepoURL')) { 49 | url "${snapshotRepoURL}" 50 | } 51 | } else { 52 | if (project.hasProperty('releaseRepoName')) { 53 | name "$releaseRepoName" 54 | } 55 | if (project.hasProperty('releaseRepoURL')) { 56 | url "$releaseRepoURL" 57 | } 58 | } 59 | } 60 | } 61 | } 62 | 63 | dependencies { 64 | 65 | def log4j2Version = '2.5'; 66 | compile "org.apache.logging.log4j:log4j-api:$log4j2Version" 67 | compile "org.apache.logging.log4j:log4j-core:$log4j2Version" 68 | 69 | compile "org.apache.commons:commons-csv:1.2" 70 | 71 | // Testing stuff, hamcrest comes first please 72 | testCompile 'org.hamcrest:hamcrest-all:1.+' 73 | testCompile 'org.testng:testng:6.+' 74 | testCompile 'commons-collections:commons-collections:3.+' 75 | 76 | // JACKSON for JSONification 77 | def jacksonVersion = '2.+' 78 | compile "com.fasterxml.jackson.core:jackson-core:${jacksonVersion}" 79 | compile "com.fasterxml.jackson.core:jackson-annotations:${jacksonVersion}" 80 | compile "com.fasterxml.jackson.core:jackson-databind:${jacksonVersion}" 81 | compile "com.fasterxml.jackson.module:jackson-module-jaxb-annotations:${jacksonVersion}" 82 | compile "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider:${jacksonVersion}" 83 | compile "com.fasterxml.jackson.dataformat:jackson-dataformat-xml:${jacksonVersion}" 84 | 85 | 86 | //This JSON Hamcrest Matcher is pretty useful (https://github.com/hertzsprung/hamcrest-json) 87 | testCompile 'uk.co.datumedge:hamcrest-json:0.+' 88 | } 89 | 90 | 91 | task sourcesJar(type: Jar, dependsOn: classes) { 92 | classifier = 'sources' 93 | from sourceSets.main.allSource 94 | } 95 | 96 | task javadocJar(type: Jar, dependsOn: javadoc) { 97 | classifier = 'javadoc' 98 | from javadoc.destinationDir 99 | } 100 | 101 | artifacts { 102 | archives sourcesJar 103 | archives javadocJar 104 | } 105 | 106 | 107 | test { 108 | useTestNG() { 109 | excludeGroups 'integration' 110 | } 111 | } 112 | 113 | task integrationTest(type: Test, dependsOn: ['test']) { 114 | logger.info 'Integration Tests' 115 | useTestNG() { 116 | } 117 | include '**/*IT.class' 118 | 119 | // copy resource over??? 120 | 121 | // set a system property for the test JVM(s) 122 | 123 | doFirst { 124 | logger.info 'Logstash required for integration tests' 125 | // start logstash or check for logstash and fail 126 | } 127 | 128 | doLast { 129 | //logger.info 'Stop Logstash' 130 | // stop logstash 131 | } 132 | } 133 | 134 | task wrapper(type: Wrapper) { 135 | gradleVersion = '2.4' 136 | } 137 | -------------------------------------------------------------------------------- /gradle.properties.example: -------------------------------------------------------------------------------- 1 | # gradle properties only used for maven deployment 2 | 3 | # Here the 'name' of a repo must match the maven settings.xml server credentials necessary for authentication 4 | # if authentication is required 5 | 6 | releaseRepoName= 7 | releaseRepoURL= 8 | snapshotRepoName= 9 | snapshotRepoURL= 10 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/majikthys/log4j2-logstash-jsonevent-layout/3b9e1169b2c341fb5b01b3db7781d5445a6eacfe/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon May 18 12:50:31 CEST 2015 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.4-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /src/main/java/org/apache/logging/log4j/core/LogStashLogEvent.java: -------------------------------------------------------------------------------- 1 | package org.apache.logging.log4j.core; 2 | 3 | import java.text.DateFormat; 4 | import java.text.SimpleDateFormat; 5 | import java.util.Date; 6 | import java.util.Map; 7 | 8 | import com.fasterxml.jackson.databind.util.StdConverter; 9 | import org.apache.logging.log4j.Level; 10 | import org.apache.logging.log4j.Marker; 11 | import org.apache.logging.log4j.ThreadContext; 12 | import org.apache.logging.log4j.core.impl.ThrowableProxy; 13 | import org.apache.logging.log4j.message.Message; 14 | 15 | /** 16 | * To serialize with mixin AND add two properties (@version and @timestamp) we 17 | * need to convert from LogEvent to a POJO with these new accessor methods. 18 | * 19 | * All LogEvent methods pass through to wrappedLogEvent used to instantiate object. 20 | * 21 | * Created by jeremyfranklin-ross on 7/28/15. 22 | */ 23 | public class LogStashLogEvent implements LogEvent{ 24 | 25 | static final String LOG_STASH_ISO8601_TIMESTAMP_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX"; 26 | static final DateFormat iso8601DateFormat = new SimpleDateFormat(LOG_STASH_ISO8601_TIMESTAMP_FORMAT); 27 | 28 | private LogEvent wrappedLogEvent; 29 | 30 | public LogStashLogEvent(LogEvent wrappedLogEvent) { 31 | this.wrappedLogEvent = wrappedLogEvent; 32 | } 33 | 34 | public String getVersion() { 35 | return "1";//LOGSTASH VERSION 36 | } 37 | 38 | public String getTimestamp() { 39 | return iso8601DateFormat.format(new Date(this.getTimeMillis())); 40 | } 41 | 42 | @Override 43 | public Map getContextMap() { 44 | return wrappedLogEvent.getContextMap(); 45 | } 46 | 47 | @Override 48 | public ThreadContext.ContextStack getContextStack() { 49 | return wrappedLogEvent.getContextStack(); 50 | } 51 | 52 | @Override 53 | public String getLoggerFqcn() { 54 | return wrappedLogEvent.getLoggerFqcn(); 55 | } 56 | 57 | @Override 58 | public Level getLevel() { 59 | return wrappedLogEvent.getLevel(); 60 | } 61 | 62 | @Override 63 | public String getLoggerName() { 64 | return wrappedLogEvent.getLoggerName(); 65 | } 66 | 67 | @Override 68 | public Marker getMarker() { 69 | return wrappedLogEvent.getMarker(); 70 | } 71 | 72 | @Override 73 | public Message getMessage() { 74 | return wrappedLogEvent.getMessage(); 75 | } 76 | 77 | @Override 78 | public long getTimeMillis() { 79 | return wrappedLogEvent.getTimeMillis(); 80 | } 81 | 82 | @Override 83 | public StackTraceElement getSource() { 84 | return wrappedLogEvent.getSource(); 85 | } 86 | 87 | @Override 88 | public String getThreadName() { 89 | return wrappedLogEvent.getThreadName(); 90 | } 91 | 92 | @Override 93 | public Throwable getThrown() { 94 | return wrappedLogEvent.getThrown(); 95 | } 96 | 97 | @Override 98 | public ThrowableProxy getThrownProxy() { 99 | return wrappedLogEvent.getThrownProxy(); 100 | } 101 | 102 | @Override 103 | public boolean isEndOfBatch() { 104 | return wrappedLogEvent.isEndOfBatch(); 105 | } 106 | 107 | @Override 108 | public boolean isIncludeLocation() { 109 | return wrappedLogEvent.isIncludeLocation(); 110 | } 111 | 112 | @Override 113 | public void setEndOfBatch(boolean endOfBatch) { 114 | wrappedLogEvent.setEndOfBatch(endOfBatch); 115 | 116 | } 117 | 118 | @Override 119 | public void setIncludeLocation(boolean locationRequired) { 120 | wrappedLogEvent.setIncludeLocation(locationRequired); 121 | } 122 | 123 | @Override 124 | public long getNanoTime() { 125 | return wrappedLogEvent.getNanoTime(); 126 | } 127 | 128 | /** 129 | * Converter used by JsonSerilize annotation on mixin. 130 | * 131 | * Created by jeremyfranklin-ross on 7/28/15. 132 | */ 133 | public static class LogEventToLogStashLogEventConverter extends StdConverter { 134 | 135 | @Override 136 | public LogStashLogEvent convert(LogEvent value) { 137 | return new LogStashLogEvent(value); 138 | } 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/main/java/org/apache/logging/log4j/core/jackson/LogStashLog4jJsonModule.java: -------------------------------------------------------------------------------- 1 | package org.apache.logging.log4j.core.jackson; 2 | 3 | import org.apache.logging.log4j.core.LogEvent; 4 | 5 | /** 6 | * Cribbed from core Log4jJsonModule this merely to override the LogEvent mixin with LogStashLogEventMixIn 7 | * 8 | * Created by jeremyfranklin-ross on 7/28/15. 9 | */ 10 | public class LogStashLog4jJsonModule extends Log4jJsonModule { 11 | 12 | private static final long serialVersionUID = 1L; 13 | 14 | LogStashLog4jJsonModule() { 15 | super(); 16 | } 17 | 18 | 19 | @Override 20 | public void setupModule(final SetupContext context) { 21 | // Calling super is a MUST! 22 | super.setupModule(context); 23 | 24 | //OVERRIDE LogEvent.class mixin with our custom one. 25 | context.setMixInAnnotations(LogEvent.class, LogStashLogEventMixIn.class); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/org/apache/logging/log4j/core/jackson/LogStashLog4jJsonObjectMapper.java: -------------------------------------------------------------------------------- 1 | package org.apache.logging.log4j.core.jackson; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | 6 | /** 7 | * Class cribbed from core Log4jJsonObjectMapper 8 | * 9 | * This merely registers LogStashLog4JsonModule 10 | * 11 | * Created by jeremyfranklin-ross on 7/27/15. 12 | */ 13 | public class LogStashLog4jJsonObjectMapper extends ObjectMapper { 14 | 15 | public LogStashLog4jJsonObjectMapper() { 16 | this.registerModule(new LogStashLog4jJsonModule()); 17 | this.setSerializationInclusion(JsonInclude.Include.NON_EMPTY); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/org/apache/logging/log4j/core/jackson/LogStashLogEventMixIn.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache license, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the license for the specific language governing permissions and 15 | * limitations under the license. 16 | */ 17 | package org.apache.logging.log4j.core.jackson; 18 | 19 | import org.apache.logging.log4j.core.LogStashLogEvent; 20 | import com.fasterxml.jackson.annotation.JsonFilter; 21 | import com.fasterxml.jackson.annotation.JsonProperty; 22 | import com.fasterxml.jackson.annotation.JsonPropertyOrder; 23 | import com.fasterxml.jackson.annotation.JsonRootName; 24 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 25 | 26 | /** 27 | * Extends LogEventMixIn but adds two elements 28 | */ 29 | @JsonSerialize(converter = LogStashLogEvent.LogEventToLogStashLogEventConverter.class) 30 | @JsonRootName(XmlConstants.ELT_EVENT) 31 | @JsonFilter("org.apache.logging.log4j.core.impl.Log4jLogEvent") 32 | @JsonPropertyOrder({"@version", "timestamp", "timeMillis", "threadName", "level", "loggerName", "marker", "message", "thrown", XmlConstants.ELT_CONTEXT_MAP, 33 | JsonConstants.ELT_CONTEXT_STACK, "loggerFQCN", "Source", "endOfBatch" }) 34 | abstract class LogStashLogEventMixIn extends LogEventMixIn { 35 | 36 | @JsonProperty("@timestamp") 37 | public abstract String getTimestamp(); 38 | 39 | private static final long serialVersionUID = 1L; 40 | 41 | @JsonProperty("@version") 42 | public abstract String getVersion(); 43 | 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/org/apache/logging/log4j/core/layout/LogStashJSONLayout.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache license, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the license for the specific language governing permissions and 15 | * limitations under the license. 16 | */ 17 | package org.apache.logging.log4j.core.layout; 18 | 19 | 20 | import java.nio.charset.Charset; 21 | import java.util.HashMap; 22 | import java.util.Map; 23 | 24 | import com.fasterxml.jackson.core.JsonProcessingException; 25 | import org.apache.logging.log4j.core.LogEvent; 26 | import org.apache.logging.log4j.core.config.plugins.Plugin; 27 | import org.apache.logging.log4j.core.config.plugins.PluginAttribute; 28 | import org.apache.logging.log4j.core.config.plugins.PluginElement; 29 | import org.apache.logging.log4j.core.config.plugins.PluginFactory; 30 | import org.apache.logging.log4j.core.util.KeyValuePair; 31 | import org.apache.logging.log4j.util.Strings; 32 | 33 | import static java.nio.charset.StandardCharsets.UTF_8; 34 | 35 | /** 36 | * Copy Pasta version of JsonLayout that uses a different JSON writer which adds 37 | * required logstash "@version" and "@timestamp" fields to the default serialized form. 38 | * 39 | * @see org.apache.logging.log4j.core.layout.JsonLayout 40 | */ 41 | @Plugin(name = "LogStashJSONLayout", category = "Core", elementType = "layout", printObject = true) 42 | public class LogStashJSONLayout extends AbstractJacksonLayout { 43 | 44 | static final String CONTENT_TYPE = "application/json"; 45 | 46 | private static final Map additionalLogAttributes = new HashMap(); 47 | 48 | protected LogStashJSONLayout(final boolean locationInfo, final boolean properties, final boolean complete, final boolean compact, 49 | boolean eventEol, final Charset charset, final Map additionalLogAttributes) { 50 | 51 | super(new LogStashJacksonFactory.JSON().newWriter(locationInfo, properties, compact), charset, compact, complete, eventEol); 52 | this.additionalLogAttributes.putAll(additionalLogAttributes); 53 | } 54 | 55 | /** 56 | * Returns appropriate JSON header. 57 | * 58 | * @return a byte array containing the header, opening the JSON array. 59 | */ 60 | @Override 61 | public byte[] getHeader() { 62 | if (!this.complete) { 63 | return null; 64 | } 65 | final StringBuilder buf = new StringBuilder(); 66 | buf.append('['); 67 | buf.append(this.eol); 68 | return getBytes(buf.toString()); 69 | } 70 | 71 | /** 72 | * Returns appropriate JSON footer. 73 | * 74 | * @return a byte array containing the footer, closing the JSON array. 75 | */ 76 | @Override 77 | public byte[] getFooter() { 78 | if (!this.complete) { 79 | return null; 80 | } 81 | return getBytes(this.eol + ']' + this.eol); 82 | } 83 | 84 | @Override 85 | public Map getContentFormat() { 86 | final Map result = new HashMap(); 87 | result.put("version", "2.0"); 88 | return result; 89 | } 90 | 91 | @Override 92 | /** 93 | * @return The content type. 94 | */ 95 | public String getContentType() { 96 | return CONTENT_TYPE + "; charset=" + this.getCharset(); 97 | } 98 | 99 | /** 100 | * Creates a JSON Layout. 101 | * 102 | * @param locationInfo 103 | * If "true", includes the location information in the generated JSON. 104 | * @param properties 105 | * If "true", includes the thread context in the generated JSON. 106 | * @param complete 107 | * If "true", includes the JSON header and footer, defaults to "false". 108 | * @param compact 109 | * If "true", does not use end-of-lines and indentation, defaults to "false". 110 | * @param eventEol 111 | * If "true", forces an EOL after each log event (even if compact is "true"), defaults to "false". This 112 | * allows one even per line, even in compact mode. 113 | * @param charset 114 | * The character set to use, if {@code null}, uses "UTF-8". 115 | * @return A JSON Layout. 116 | */ 117 | @PluginFactory 118 | public static AbstractJacksonLayout createLayout( 119 | // @formatter:off 120 | @PluginAttribute(value = "locationInfo", defaultBoolean = false) final boolean locationInfo, 121 | @PluginAttribute(value = "properties", defaultBoolean = false) final boolean properties, 122 | @PluginAttribute(value = "complete", defaultBoolean = false) final boolean complete, 123 | @PluginAttribute(value = "compact", defaultBoolean = false) final boolean compact, 124 | @PluginAttribute(value = "eventEol", defaultBoolean = false) final boolean eventEol, 125 | @PluginAttribute(value = "charset", defaultString = "UTF-8") final Charset charset, 126 | @PluginElement("Pairs") final KeyValuePair[] pairs 127 | // @formatter:on 128 | ) { 129 | 130 | 131 | //Unpacke the pairs list 132 | final Map additionalLogAttributes = new HashMap(); 133 | if (pairs != null && pairs.length > 0) { 134 | for (final KeyValuePair pair : pairs) { 135 | final String key = pair.getKey(); 136 | if (key == null) { 137 | LOGGER.error("A null key is not valid in MapFilter"); 138 | continue; 139 | } 140 | final String value = pair.getValue(); 141 | if (value == null) { 142 | LOGGER.error("A null value for key " + key + " is not allowed in MapFilter"); 143 | continue; 144 | } 145 | if (additionalLogAttributes.containsKey(key)) { 146 | LOGGER.error("Duplicate entry for key: {} is forbidden!", key); 147 | } 148 | additionalLogAttributes.put(key, value); 149 | } 150 | 151 | } 152 | 153 | 154 | return new LogStashJSONLayout(locationInfo, properties, complete, compact, eventEol, charset, additionalLogAttributes); 155 | 156 | } 157 | 158 | /** 159 | * Creates a JSON Layout using the default settings. 160 | * 161 | * @return A JSON Layout. 162 | */ 163 | public static AbstractJacksonLayout createDefaultLayout() { 164 | return new LogStashJSONLayout(false, false, false, false, false, UTF_8, new HashMap()); 165 | } 166 | 167 | /** 168 | * Formats a {@link org.apache.logging.log4j.core.LogEvent}. 169 | * 170 | * @param event The LogEvent. 171 | * @return The XML representation of the LogEvent. 172 | */ 173 | @Override 174 | public String toSerializable(final LogEvent event) { 175 | event.getContextMap().putAll(additionalLogAttributes); 176 | try { 177 | return this.objectWriter.writeValueAsString(event) + eol; 178 | } catch (final JsonProcessingException e) { 179 | // Should this be an ISE or IAE? 180 | LOGGER.error(e); 181 | return Strings.EMPTY; 182 | } 183 | } 184 | 185 | 186 | 187 | } 188 | -------------------------------------------------------------------------------- /src/main/java/org/apache/logging/log4j/core/layout/LogStashJacksonFactory.java: -------------------------------------------------------------------------------- 1 | package org.apache.logging.log4j.core.layout; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import com.fasterxml.jackson.databind.ObjectWriter; 5 | import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter; 6 | import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider; 7 | import org.apache.logging.log4j.core.impl.Log4jLogEvent; 8 | import org.apache.logging.log4j.core.jackson.LogStashLog4jJsonObjectMapper; 9 | 10 | import java.util.HashSet; 11 | import java.util.Set; 12 | 13 | /** 14 | * Cribbed from log4j core JacksonFactory, this merely introduces LogStashLog4jJsonObjectMapper 15 | * 16 | * Created by jeremyfranklin-ross on 7/27/15. 17 | */ 18 | abstract class LogStashJacksonFactory extends JacksonFactory { 19 | static class JSON extends JacksonFactory.JSON { 20 | @Override 21 | protected ObjectMapper newObjectMapper() { 22 | return new LogStashLog4jJsonObjectMapper(); 23 | } 24 | 25 | } 26 | 27 | ObjectWriter newWriter(final boolean locationInfo, final boolean properties, final boolean compact) { 28 | final SimpleFilterProvider filters = new SimpleFilterProvider(); 29 | final Set except = new HashSet(2); 30 | if (!locationInfo) { 31 | except.add(this.getPropertNameForSource()); 32 | } 33 | if (!properties) { 34 | except.add(this.getPropertNameForContextMap()); 35 | } 36 | filters.addFilter(Log4jLogEvent.class.getName(), SimpleBeanPropertyFilter.serializeAllExcept(except)); 37 | final ObjectWriter writer = this.newObjectMapper().writer(compact ? this.newCompactPrinter() : this.newPrettyPrinter()); 38 | return writer.with(filters); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/test/java/net/logstash/logging/log4j2/core/layout/LogStashJSONLayoutIT.java: -------------------------------------------------------------------------------- 1 | package net.logstash.logging.log4j2.core.layout; 2 | 3 | import java.io.IOException; 4 | import java.util.Date; 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | import com.fasterxml.jackson.core.JsonParseException; 9 | import com.fasterxml.jackson.databind.JsonMappingException; 10 | import com.fasterxml.jackson.databind.ObjectMapper; 11 | import com.fasterxml.jackson.databind.node.ObjectNode; 12 | import org.apache.logging.log4j.Level; 13 | import org.apache.logging.log4j.LogManager; 14 | import org.apache.logging.log4j.Logger; 15 | import org.apache.logging.log4j.core.LogEvent; 16 | import org.apache.logging.log4j.core.impl.Log4jLogEvent; 17 | import org.apache.logging.log4j.message.Message; 18 | import org.apache.logging.log4j.message.SimpleMessage; 19 | import org.testng.annotations.AfterTest; 20 | import org.testng.annotations.BeforeTest; 21 | import org.testng.annotations.DataProvider; 22 | import org.testng.annotations.Test; 23 | 24 | import static org.hamcrest.CoreMatchers.containsString; 25 | import static org.hamcrest.CoreMatchers.is; 26 | import static org.hamcrest.CoreMatchers.startsWith; 27 | import static org.hamcrest.MatcherAssert.assertThat; 28 | import static uk.co.datumedge.hamcrest.json.SameJSONAs.sameJSONAs; 29 | 30 | public class LogStashJSONLayoutIT { 31 | // public static final String LOCATION_INFO = "LocationInfo"; 32 | // private static Logger logger = LogManager.getLogger(LogStashJSONLayoutIT.class); 33 | // 34 | // 35 | // @Test(enabled = false, dataProvider = "dp") 36 | // public void f(Integer n, String s) { 37 | // } 38 | // 39 | // @DataProvider 40 | // public Object[][] dp() { 41 | // return new Object[][]{ 42 | // new Object[]{1, "a"}, 43 | // new Object[]{2, "b"}, 44 | // }; 45 | // } 46 | // 47 | // @BeforeTest 48 | // public void beforeTest() { 49 | // } 50 | // 51 | // @AfterTest 52 | // public void afterTest() { 53 | // } 54 | // 55 | // 56 | // ObjectMapper mapper = new ObjectMapper(); 57 | // 58 | // String expectedBasicSimpleTestJSON = "{" + 59 | // "\"@version\":\"1\"," + 60 | // // REMOVE timestamp b/c it'll alwayhs be wrong "\"@timestamp\":\"2014-10-03T09:58:03.391-07:00\"," + 61 | // "\"logger\":\"net.logstash.logging.log4j2.core.layout.LogStashJSONLayoutIT\"," + 62 | // "\"level\":\"DEBUG\"," + 63 | // "\"thread\":\""+ Thread.currentThread().getName() +"\"," + 64 | // "\"message\":\"Test Message\"," + 65 | // "\"log\":\"Test Message\\n\"}"; 66 | // 67 | // @Test 68 | // public void BasicSimpleTest() throws JsonParseException, JsonMappingException, IOException { 69 | // Message simpleMessage = new SimpleMessage("Test Message"); 70 | // LogEvent event = new Log4jLogEvent(logger.getName(), 71 | // null, 72 | // this.getClass().getCanonicalName(), 73 | // Level.DEBUG, 74 | // simpleMessage, 75 | // null); 76 | // 77 | // 78 | // LogStashJSONLayout layout = LogStashJSONLayout.createLayout( 79 | // null, // @PluginAttribute("locationInfo") final String locationInfo, 80 | // null, // @PluginAttribute("properties") final String properties, 81 | // null, // @PluginAttribute("complete") final String completeStr, 82 | // null, // @PluginAttribute("compact") final String compactStr, 83 | // null, // @PluginAttribute("newline") final String newlineStr, 84 | // null, // @PluginAttribute("commaAtEventEnd") final String commaAtEventEndStr, 85 | // null, // @PluginAttribute("charset") final String charsetName, 86 | // null, // @PluginAttribute("excludeLogger") final String excludeLoggerStr, 87 | // null, // @PluginAttribute("excludeLevel") final String excludeLevelStr, 88 | // null, // @PluginAttribute("excludeThread") final String excludeThreadStr, 89 | // null, // @PluginAttribute("excludeMessage") final String excludeMessageStr, 90 | // null, // @PluginAttribute("excludeLog") final String excludeLogStr, null, // @PluginAttribute("excludeLog") final String excludeLogStr, 91 | // null, // @PluginAttribute("excludeNDC") final String excludeNDCStr, 92 | // null, // @PluginAttribute("excludeThrown") final String excludeThrownStr, 93 | // null, // @PluginAttribute("skipJsonEscapeSubLayout") final String skipJsonEscapeSubLayoutStr, 94 | // null, // @PluginAttribute("subLayoutAsElement") final String subLayoutAsElementStr, 95 | // null, // @PluginElement("Layout") Layout subLayout, 96 | // null // @PluginElement("Pairs") final KeyValuePair[] pairs 97 | // ); 98 | // 99 | // String actualJSON = layout.toSerializable(event); 100 | // 101 | // assertThat(actualJSON, sameJSONAs(expectedBasicSimpleTestJSON) 102 | // .allowingExtraUnexpectedFields() 103 | // .allowingAnyArrayOrdering()); 104 | // 105 | // } 106 | // 107 | // @SuppressWarnings({"rawtypes", "unchecked"}) 108 | // @Test 109 | // public void TestThrowable() throws IOException { 110 | // Message simpleMessage = new SimpleMessage("Throw an exception"); 111 | // 112 | // Throwable nestedThrowable = null; 113 | // try { 114 | // @SuppressWarnings("unused") 115 | // int divide_by_zero = 12 / 0; 116 | // } catch (RuntimeException r) { 117 | // try { 118 | // throw new IOException("Test IO Exception", r); 119 | // } catch (IOException e) { 120 | // nestedThrowable = new RuntimeException("Runtime Exception", e); 121 | // } 122 | // } 123 | // 124 | // LogEvent event = new Log4jLogEvent(logger.getName(), 125 | // null, 126 | // this.getClass().getCanonicalName(), 127 | // Level.DEBUG, 128 | // simpleMessage, 129 | // nestedThrowable); 130 | // 131 | // LogStashJSONLayout layout = LogStashJSONLayout.createLayout( 132 | // null, // @PluginAttribute("locationInfo") final String locationInfo, 133 | // null, // @PluginAttribute("properties") final String properties, 134 | // null, // @PluginAttribute("complete") final String completeStr, 135 | // null, // @PluginAttribute("compact") final String compactStr, 136 | // null, // @PluginAttribute("newline") final String newlineStr, 137 | // null, // @PluginAttribute("commaAtEventEnd") final String commaAtEventEndStr, 138 | // null, // @PluginAttribute("charset") final String charsetName, 139 | // null, // @PluginAttribute("excludeLogger") final String excludeLoggerStr, 140 | // null, // @PluginAttribute("excludeLevel") final String excludeLevelStr, 141 | // null, // @PluginAttribute("excludeThread") final String excludeThreadStr, 142 | // null, // @PluginAttribute("excludeMessage") final String excludeMessageStr, 143 | // null, // @PluginAttribute("excludeLog") final String excludeLogStr, 144 | // null, // @PluginAttribute("excludeNDC") final String excludeNDCStr, 145 | // null, // @PluginAttribute("excludeThrown") final String excludeThrownStr, 146 | // null, // @PluginAttribute("skipJsonEscapeSubLayout") final String skipJsonEscapeSubLayoutStr, 147 | // null, // @PluginAttribute("subLayoutAsElement") final String subLayoutAsElementStr, 148 | // null, // @PluginElement("Layout") Layout subLayout, 149 | // null // @PluginElement("Pairs") final KeyValuePair[] pairs 150 | // ); 151 | // 152 | // String layoutJSON = layout.toSerializable(event); 153 | // 154 | // ObjectNode resultLayout = mapper.readValue(layoutJSON, ObjectNode.class); 155 | // 156 | // assertThat(resultLayout.has(KEY_THROWABLE), is(true)); 157 | // assertThat(resultLayout.get(KEY_THROWABLE).asText(), containsString("/ by zero")); 158 | // assertThat(resultLayout.get(KEY_THROWABLE).asText(), startsWith("java.lang.RuntimeException: Runtime Exception")); 159 | // 160 | // } 161 | // 162 | // private final static String KEY_THROWABLE = "throwable"; 163 | // private final static Map MDC = new HashMap(); 164 | // 165 | // static { 166 | // MDC.put("CKey1", "CVal1"); 167 | // MDC.put("CKey2", "CVal2"); 168 | // MDC.put("CKey3", "CVal3"); 169 | // } 170 | // 171 | // private final static Date LOG_DATE = new Date(); 172 | // private final static Throwable THROWABLE = null; 173 | // 174 | // private final static String TEST_THREAD_NAME = "threadname.foo1"; 175 | // 176 | // private final static StackTraceElement STACK_TRACE_ELEMENT = new StackTraceElement("FakeClass", "isFakeMethod", "/fake/file.java", 66); 177 | // private final static String EXPECTED_LOCATION_INFO = "{\"class\":\"FakeClass\",\"method\":\"isFakeMethod\",\"file\":\"/fake/file.java\",\"line\":\"66\"}"; 178 | // 179 | // // --------------------------------------------------- 180 | // // --------------------------------------------------- 181 | // // Below, test to make sure configuration works 182 | // // --------------------------------------------------- 183 | // // --------------------------------------------------- 184 | // 185 | // @Test 186 | // public void testPluginAttributePropertiesDefault() throws IOException { 187 | // LogStashJSONLayout layout = LogStashJSONLayout.createLayout( 188 | // null, // @PluginAttribute("locationInfo") final String locationInfo, 189 | // null, // defaultTrue @PluginAttribute("properties") final String properties, 190 | // null, // @PluginAttribute("complete") final String completeStr, 191 | // null, // @PluginAttribute("compact") final String compactStr, 192 | // null, // @PluginAttribute("newline") final String newlineStr, 193 | // null, // @PluginAttribute("commaAtEventEnd") final String commaAtEventEndStr, 194 | // null, // @PluginAttribute("charset") final String charsetName, 195 | // null, // @PluginAttribute("excludeLogger") final String excludeLoggerStr, 196 | // null, // @PluginAttribute("excludeLevel") final String excludeLevelStr, 197 | // null, // @PluginAttribute("excludeThread") final String excludeThreadStr, 198 | // null, // @PluginAttribute("excludeMessage") final String excludeMessageStr, 199 | // null, // @PluginAttribute("excludeLog") final String excludeLogStr, 200 | // null, // @PluginAttribute("excludeNDC") final String excludeNDCStr, 201 | // null, // @PluginAttribute("excludeThrown") final String excludeThrownStr, 202 | // null, // @PluginAttribute("skipJsonEscapeSubLayout") final String skipJsonEscapeSubLayoutStr, 203 | // null, // @PluginAttribute("subLayoutAsElement") final String subLayoutAsElementStr, 204 | // null, // @PluginElement("Layout") Layout subLayout, 205 | // null // @PluginElement("Pairs") final KeyValuePair[] pairs 206 | // ); 207 | // 208 | // Message simpleMessage = new SimpleMessage("testPluginAttributeLocationInfo"); 209 | // 210 | // 211 | // LogEvent event = new Log4jLogEvent(logger.getName(), 212 | // null, // final Marker marker, 213 | // this.getClass().getCanonicalName(), //final String loggerFQCN, 214 | // Level.DEBUG, //final Level level, 215 | // simpleMessage, //final Message message, 216 | // THROWABLE, //final Throwable t, 217 | // MDC, //final Map mdc, 218 | // null, //final ThreadContext.ContextStack ndc, 219 | // TEST_THREAD_NAME, //final String threadName, 220 | // null, // final StackTraceElement location, 221 | // LOG_DATE.getTime()); //final long timestamp); 222 | // 223 | // 224 | // String actualJSON = layout.toSerializable(event); 225 | // 226 | // ObjectNode resultLayout = mapper.readValue(actualJSON, ObjectNode.class); 227 | // 228 | // String expectedJSON = "{" + 229 | // "\"@version\":\"1\"," + 230 | // // REMOVE timestamp b/c it'll alwayhs be wrong "\"@timestamp\":\"2014-10-03T09:58:03.391-07:00\"," + 231 | // "\"logger\":\"net.logstash.logging.log4j2.core.layout.LogStashJSONLayoutIT\"," + 232 | // "\"level\":\"DEBUG\"," + 233 | // "\"thread\":\"threadname.foo1\"," + 234 | // "\"message\":\"testPluginAttributeLocationInfo\"," + 235 | // "\"Properties\":{\"CKey3\":\"CVal3\",\"CKey2\":\"CVal2\",\"CKey1\":\"CVal1\"}," + 236 | // "\"log\":\"testPluginAttributeLocationInfo\\n\"}"; 237 | // 238 | // System.out.println("--- testPluginAttributeLocationInfo ---"); 239 | // System.out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(resultLayout)); 240 | // System.out.println(mapper.writer().writeValueAsString(resultLayout.get("Properties"))); 241 | // System.out.println("--- testPluginAttributeLocationInfo ---"); 242 | // 243 | // 244 | // assertThat(actualJSON, sameJSONAs(expectedJSON) 245 | // .allowingExtraUnexpectedFields() 246 | // .allowingAnyArrayOrdering()); 247 | // 248 | // 249 | // } 250 | // 251 | // 252 | // @Test 253 | // public void testPluginAttributePropertiesFalse() throws IOException { 254 | // LogStashJSONLayout layout = LogStashJSONLayout.createLayout( 255 | // null, // @PluginAttribute("locationInfo") final String locationInfo, 256 | // "false", // defaultTrue @PluginAttribute("properties") final String properties, 257 | // null, // @PluginAttribute("complete") final String completeStr, 258 | // null, // @PluginAttribute("compact") final String compactStr, 259 | // null, // @PluginAttribute("newline") final String newlineStr, 260 | // null, // @PluginAttribute("commaAtEventEnd") final String commaAtEventEndStr, 261 | // null, // @PluginAttribute("charset") final String charsetName, 262 | // null, // @PluginAttribute("excludeLogger") final String excludeLoggerStr, 263 | // null, // @PluginAttribute("excludeLevel") final String excludeLevelStr, 264 | // null, // @PluginAttribute("excludeThread") final String excludeThreadStr, 265 | // null, // @PluginAttribute("excludeMessage") final String excludeMessageStr, 266 | // null, // @PluginAttribute("excludeLog") final String excludeLogStr, 267 | // null, // @PluginAttribute("excludeNDC") final String excludeNDCStr, 268 | // null, // @PluginAttribute("excludeThrown") final String excludeThrownStr, 269 | // null, // @PluginAttribute("skipJsonEscapeSubLayout") final String skipJsonEscapeSubLayoutStr, 270 | // null, // @PluginAttribute("subLayoutAsElement") final String subLayoutAsElementStr, 271 | // null, // @PluginElement("Layout") Layout subLayout, 272 | // null // @PluginElement("Pairs") final KeyValuePair[] pairs 273 | // ); 274 | // 275 | // Message simpleMessage = new SimpleMessage("testPluginAttributeLocationInfo"); 276 | // 277 | // 278 | // LogEvent event = new Log4jLogEvent(logger.getName(), 279 | // null, // final Marker marker, 280 | // this.getClass().getCanonicalName(), //final String loggerFQCN, 281 | // Level.DEBUG, //final Level level, 282 | // simpleMessage, //final Message message, 283 | // THROWABLE, //final Throwable t, 284 | // MDC, //final Map mdc, 285 | // null, //final ThreadContext.ContextStack ndc, 286 | // TEST_THREAD_NAME, //final String threadName, 287 | // null, // final StackTraceElement location, 288 | // LOG_DATE.getTime()); //final long timestamp); 289 | // 290 | // 291 | // String actualJSON = layout.toSerializable(event); 292 | // ObjectNode resultLayout = mapper.readValue(actualJSON, ObjectNode.class); 293 | // 294 | // assertThat(resultLayout.has("Properties"), is(false)); 295 | // 296 | // 297 | // } 298 | // 299 | // 300 | // @Test 301 | // public void testPluginAttributeLocationInfoDefault() throws IOException { 302 | // LogStashJSONLayout layout = LogStashJSONLayout.createLayout( 303 | // null, // true by default @PluginAttribute("locationInfo") final String locationInfo, 304 | // null, // @PluginAttribute("properties") final String properties, 305 | // null, // @PluginAttribute("complete") final String completeStr, 306 | // null, // @PluginAttribute("compact") final String compactStr, 307 | // null, // @PluginAttribute("newline") final String newlineStr, 308 | // null, // @PluginAttribute("commaAtEventEnd") final String commaAtEventEndStr, 309 | // null, // @PluginAttribute("charset") final String charsetName, 310 | // null, // @PluginAttribute("excludeLogger") final String excludeLoggerStr, 311 | // null, // @PluginAttribute("excludeLevel") final String excludeLevelStr, 312 | // null, // @PluginAttribute("excludeThread") final String excludeThreadStr, 313 | // null, // @PluginAttribute("excludeMessage") final String excludeMessageStr, 314 | // null, // @PluginAttribute("excludeLog") final String excludeLogStr, 315 | // null, // @PluginAttribute("excludeNDC") final String excludeNDCStr, 316 | // null, // @PluginAttribute("excludeThrown") final String excludeThrownStr, 317 | // null, // @PluginAttribute("skipJsonEscapeSubLayout") final String skipJsonEscapeSubLayoutStr, 318 | // null, // @PluginAttribute("subLayoutAsElement") final String subLayoutAsElementStr, 319 | // null, // @PluginElement("Layout") Layout subLayout, 320 | // null // @PluginElement("Pairs") final KeyValuePair[] pairs 321 | // ); 322 | // 323 | // Message simpleMessage = new SimpleMessage("testPluginAttributeLocationInfo"); 324 | // 325 | // 326 | // LogEvent event = new Log4jLogEvent(logger.getName(), 327 | // null, // final Marker marker, 328 | // this.getClass().getCanonicalName(), //final String loggerFQCN, 329 | // Level.DEBUG, //final Level level, 330 | // simpleMessage, //final Message message, 331 | // THROWABLE, //final Throwable t, 332 | // null, //final Map mdc, 333 | // null, //final ThreadContext.ContextStack ndc, 334 | // TEST_THREAD_NAME, //final String threadName, 335 | // STACK_TRACE_ELEMENT, // final StackTraceElement location, 336 | // LOG_DATE.getTime()); //final long timestamp); 337 | // 338 | // String actualJSON = layout.toSerializable(event); 339 | // ObjectNode resultLayout = mapper.readValue(actualJSON, ObjectNode.class); 340 | //// 341 | //// System.out.println("++++++++++++++++++++++++++++++++++"); 342 | //// System.out.println(actualJSON); 343 | //// System.out.println("++++++++++++++++++++++++++++++++++"); 344 | //// 345 | // 346 | // assertThat(mapper.writeValueAsString(resultLayout.get(LOCATION_INFO)), sameJSONAs(EXPECTED_LOCATION_INFO) 347 | // .allowingAnyArrayOrdering()); 348 | // 349 | //// 350 | //// assertThat(resultLayout.has("Properties"), is(false)); 351 | // 352 | // 353 | // } 354 | // 355 | // @Test 356 | // public void testPluginAttributeLocationInfoFalse() throws IOException { 357 | // LogStashJSONLayout layout = LogStashJSONLayout.createLayout( 358 | // "false", // true by default @PluginAttribute("locationInfo") final String locationInfo, 359 | // null, // @PluginAttribute("properties") final String properties, 360 | // null, // @PluginAttribute("complete") final String completeStr, 361 | // null, // @PluginAttribute("compact") final String compactStr, 362 | // null, // @PluginAttribute("newline") final String newlineStr, 363 | // null, // @PluginAttribute("commaAtEventEnd") final String commaAtEventEndStr, 364 | // null, // @PluginAttribute("charset") final String charsetName, 365 | // null, // @PluginAttribute("excludeLogger") final String excludeLoggerStr, 366 | // null, // @PluginAttribute("excludeLevel") final String excludeLevelStr, 367 | // null, // @PluginAttribute("excludeThread") final String excludeThreadStr, 368 | // null, // @PluginAttribute("excludeMessage") final String excludeMessageStr, 369 | // null, // @PluginAttribute("excludeLog") final String excludeLogStr, 370 | // null, // @PluginAttribute("excludeNDC") final String excludeNDCStr, 371 | // null, // @PluginAttribute("excludeThrown") final String excludeThrownStr, 372 | // null, // @PluginAttribute("skipJsonEscapeSubLayout") final String skipJsonEscapeSubLayoutStr, 373 | // null, // @PluginAttribute("subLayoutAsElement") final String subLayoutAsElementStr, 374 | // null, // @PluginElement("Layout") Layout subLayout, 375 | // null // @PluginElement("Pairs") final KeyValuePair[] pairs 376 | // ); 377 | // 378 | // Message simpleMessage = new SimpleMessage("testPluginAttributeLocationInfo"); 379 | // 380 | // 381 | // LogEvent event = new Log4jLogEvent(logger.getName(), 382 | // null, // final Marker marker, 383 | // this.getClass().getCanonicalName(), //final String loggerFQCN, 384 | // Level.DEBUG, //final Level level, 385 | // simpleMessage, //final Message message, 386 | // THROWABLE, //final Throwable t, 387 | // null, //final Map mdc, 388 | // null, //final ThreadContext.ContextStack ndc, 389 | // TEST_THREAD_NAME, //final String threadName, 390 | // STACK_TRACE_ELEMENT, // final StackTraceElement location, 391 | // LOG_DATE.getTime()); //final long timestamp); 392 | // 393 | // String actualJSON = layout.toSerializable(event); 394 | // ObjectNode resultLayout = mapper.readValue(actualJSON, ObjectNode.class); 395 | // 396 | // 397 | // assertThat(resultLayout.has("Properties"), is(false)); 398 | // 399 | // 400 | // } 401 | // 402 | // @Test 403 | // public void testPluginAttributeComplete() { 404 | // LogStashJSONLayout layout = LogStashJSONLayout.createLayout( 405 | // null, // @PluginAttribute("locationInfo") final String locationInfo, 406 | // null, // @PluginAttribute("properties") final String properties, 407 | // null, // @PluginAttribute("complete") final String completeStr, 408 | // null, // @PluginAttribute("compact") final String compactStr, 409 | // null, // @PluginAttribute("newline") final String newlineStr, 410 | // null, // @PluginAttribute("commaAtEventEnd") final String commaAtEventEndStr, 411 | // null, // @PluginAttribute("charset") final String charsetName, 412 | // null, // @PluginAttribute("excludeLogger") final String excludeLoggerStr, 413 | // null, // @PluginAttribute("excludeLevel") final String excludeLevelStr, 414 | // null, // @PluginAttribute("excludeThread") final String excludeThreadStr, 415 | // null, // @PluginAttribute("excludeMessage") final String excludeMessageStr, 416 | // null, // @PluginAttribute("excludeLog") final String excludeLogStr, 417 | // null, // @PluginAttribute("excludeNDC") final String excludeNDCStr, 418 | // null, // @PluginAttribute("excludeThrown") final String excludeThrownStr, 419 | // null, // @PluginAttribute("skipJsonEscapeSubLayout") final String skipJsonEscapeSubLayoutStr, 420 | // null, // @PluginAttribute("subLayoutAsElement") final String subLayoutAsElementStr, 421 | // null, // @PluginElement("Layout") Layout subLayout, 422 | // null // @PluginElement("Pairs") final KeyValuePair[] pairs 423 | // ); 424 | // } 425 | // 426 | // @Test 427 | // public void testPluginAttributeCompact() { 428 | // LogStashJSONLayout layout = LogStashJSONLayout.createLayout( 429 | // null, // @PluginAttribute("locationInfo") final String locationInfo, 430 | // null, // @PluginAttribute("properties") final String properties, 431 | // null, // @PluginAttribute("complete") final String completeStr, 432 | // null, // @PluginAttribute("compact") final String compactStr, 433 | // null, // @PluginAttribute("newline") final String newlineStr, 434 | // null, // @PluginAttribute("commaAtEventEnd") final String commaAtEventEndStr, 435 | // null, // @PluginAttribute("charset") final String charsetName, 436 | // null, // @PluginAttribute("excludeLogger") final String excludeLoggerStr, 437 | // null, // @PluginAttribute("excludeLevel") final String excludeLevelStr, 438 | // null, // @PluginAttribute("excludeThread") final String excludeThreadStr, 439 | // null, // @PluginAttribute("excludeMessage") final String excludeMessageStr, 440 | // null, // @PluginAttribute("excludeLog") final String excludeLogStr, 441 | // null, // @PluginAttribute("excludeNDC") final String excludeNDCStr, 442 | // null, // @PluginAttribute("excludeThrown") final String excludeThrownStr, 443 | // null, // @PluginAttribute("skipJsonEscapeSubLayout") final String skipJsonEscapeSubLayoutStr, 444 | // null, // @PluginAttribute("subLayoutAsElement") final String subLayoutAsElementStr, 445 | // null, // @PluginElement("Layout") Layout subLayout, 446 | // null // @PluginElement("Pairs") final KeyValuePair[] pairs 447 | // ); 448 | // } 449 | // 450 | // @Test 451 | // public void testPluginAttributeNewLine() { 452 | // LogStashJSONLayout layout = LogStashJSONLayout.createLayout( 453 | // null, // @PluginAttribute("locationInfo") final String locationInfo, 454 | // null, // @PluginAttribute("properties") final String properties, 455 | // null, // @PluginAttribute("complete") final String completeStr, 456 | // null, // @PluginAttribute("compact") final String compactStr, 457 | // null, // @PluginAttribute("newline") final String newlineStr, 458 | // null, // @PluginAttribute("commaAtEventEnd") final String commaAtEventEndStr, 459 | // null, // @PluginAttribute("charset") final String charsetName, 460 | // null, // @PluginAttribute("excludeLogger") final String excludeLoggerStr, 461 | // null, // @PluginAttribute("excludeLevel") final String excludeLevelStr, 462 | // null, // @PluginAttribute("excludeThread") final String excludeThreadStr, 463 | // null, // @PluginAttribute("excludeMessage") final String excludeMessageStr, 464 | // null, // @PluginAttribute("excludeLog") final String excludeLogStr, 465 | // null, // @PluginAttribute("excludeNDC") final String excludeNDCStr, 466 | // null, // @PluginAttribute("excludeThrown") final String excludeThrownStr, 467 | // null, // @PluginAttribute("skipJsonEscapeSubLayout") final String skipJsonEscapeSubLayoutStr, 468 | // null, // @PluginAttribute("subLayoutAsElement") final String subLayoutAsElementStr, 469 | // null, // @PluginElement("Layout") Layout subLayout, 470 | // null // @PluginElement("Pairs") final KeyValuePair[] pairs 471 | // ); 472 | // } 473 | // 474 | // @Test 475 | // public void testPluginAttributeCommaAtEventEnd() { 476 | // LogStashJSONLayout layout = LogStashJSONLayout.createLayout( 477 | // null, // @PluginAttribute("locationInfo") final String locationInfo, 478 | // null, // @PluginAttribute("properties") final String properties, 479 | // null, // @PluginAttribute("complete") final String completeStr, 480 | // null, // @PluginAttribute("compact") final String compactStr, 481 | // null, // @PluginAttribute("newline") final String newlineStr, 482 | // null, // @PluginAttribute("commaAtEventEnd") final String commaAtEventEndStr, 483 | // null, // @PluginAttribute("charset") final String charsetName, 484 | // null, // @PluginAttribute("excludeLogger") final String excludeLoggerStr, 485 | // null, // @PluginAttribute("excludeLevel") final String excludeLevelStr, 486 | // null, // @PluginAttribute("excludeThread") final String excludeThreadStr, 487 | // null, // @PluginAttribute("excludeMessage") final String excludeMessageStr, 488 | // null, // @PluginAttribute("excludeLog") final String excludeLogStr, 489 | // null, // @PluginAttribute("excludeNDC") final String excludeNDCStr, 490 | // null, // @PluginAttribute("excludeThrown") final String excludeThrownStr, 491 | // null, // @PluginAttribute("skipJsonEscapeSubLayout") final String skipJsonEscapeSubLayoutStr, 492 | // null, // @PluginAttribute("subLayoutAsElement") final String subLayoutAsElementStr, 493 | // null, // @PluginElement("Layout") Layout subLayout, 494 | // null // @PluginElement("Pairs") final KeyValuePair[] pairs 495 | // ); 496 | // } 497 | // 498 | // @Test 499 | // public void testPluginAttributeCharSet() { 500 | // LogStashJSONLayout layout = LogStashJSONLayout.createLayout( 501 | // null, // @PluginAttribute("locationInfo") final String locationInfo, 502 | // null, // @PluginAttribute("properties") final String properties, 503 | // null, // @PluginAttribute("complete") final String completeStr, 504 | // null, // @PluginAttribute("compact") final String compactStr, 505 | // null, // @PluginAttribute("newline") final String newlineStr, 506 | // null, // @PluginAttribute("commaAtEventEnd") final String commaAtEventEndStr, 507 | // null, // @PluginAttribute("charset") final String charsetName, 508 | // null, // @PluginAttribute("excludeLogger") final String excludeLoggerStr, 509 | // null, // @PluginAttribute("excludeLevel") final String excludeLevelStr, 510 | // null, // @PluginAttribute("excludeThread") final String excludeThreadStr, 511 | // null, // @PluginAttribute("excludeMessage") final String excludeMessageStr, 512 | // null, // @PluginAttribute("excludeLog") final String excludeLogStr, 513 | // null, // @PluginAttribute("excludeNDC") final String excludeNDCStr, 514 | // null, // @PluginAttribute("excludeThrown") final String excludeThrownStr, 515 | // null, // @PluginAttribute("skipJsonEscapeSubLayout") final String skipJsonEscapeSubLayoutStr, 516 | // null, // @PluginAttribute("subLayoutAsElement") final String subLayoutAsElementStr, 517 | // null, // @PluginElement("Layout") Layout subLayout, 518 | // null // @PluginElement("Pairs") final KeyValuePair[] pairs 519 | // ); 520 | // } 521 | // 522 | // @Test 523 | // public void testPluginAttributeExcludeLogger() { 524 | // LogStashJSONLayout layout = LogStashJSONLayout.createLayout( 525 | // null, // @PluginAttribute("locationInfo") final String locationInfo, 526 | // null, // @PluginAttribute("properties") final String properties, 527 | // null, // @PluginAttribute("complete") final String completeStr, 528 | // null, // @PluginAttribute("compact") final String compactStr, 529 | // null, // @PluginAttribute("newline") final String newlineStr, 530 | // null, // @PluginAttribute("commaAtEventEnd") final String commaAtEventEndStr, 531 | // null, // @PluginAttribute("charset") final String charsetName, 532 | // null, // @PluginAttribute("excludeLogger") final String excludeLoggerStr, 533 | // null, // @PluginAttribute("excludeLevel") final String excludeLevelStr, 534 | // null, // @PluginAttribute("excludeThread") final String excludeThreadStr, 535 | // null, // @PluginAttribute("excludeMessage") final String excludeMessageStr, 536 | // null, // @PluginAttribute("excludeLog") final String excludeLogStr, 537 | // null, // @PluginAttribute("excludeNDC") final String excludeNDCStr, 538 | // null, // @PluginAttribute("excludeThrown") final String excludeThrownStr, 539 | // null, // @PluginAttribute("skipJsonEscapeSubLayout") final String skipJsonEscapeSubLayoutStr, 540 | // null, // @PluginAttribute("subLayoutAsElement") final String subLayoutAsElementStr, 541 | // null, // @PluginElement("Layout") Layout subLayout, 542 | // null // @PluginElement("Pairs") final KeyValuePair[] pairs 543 | // ); 544 | // } 545 | // 546 | // @Test 547 | // public void testPluginAttributeExcludeLevel() { 548 | // LogStashJSONLayout layout = LogStashJSONLayout.createLayout( 549 | // null, // @PluginAttribute("locationInfo") final String locationInfo, 550 | // null, // @PluginAttribute("properties") final String properties, 551 | // null, // @PluginAttribute("complete") final String completeStr, 552 | // null, // @PluginAttribute("compact") final String compactStr, 553 | // null, // @PluginAttribute("newline") final String newlineStr, 554 | // null, // @PluginAttribute("commaAtEventEnd") final String commaAtEventEndStr, 555 | // null, // @PluginAttribute("charset") final String charsetName, 556 | // null, // @PluginAttribute("excludeLogger") final String excludeLoggerStr, 557 | // null, // @PluginAttribute("excludeLevel") final String excludeLevelStr, 558 | // null, // @PluginAttribute("excludeThread") final String excludeThreadStr, 559 | // null, // @PluginAttribute("excludeMessage") final String excludeMessageStr, 560 | // null, // @PluginAttribute("excludeLog") final String excludeLogStr, 561 | // null, // @PluginAttribute("excludeNDC") final String excludeNDCStr, 562 | // null, // @PluginAttribute("excludeThrown") final String excludeThrownStr, 563 | // null, // @PluginAttribute("skipJsonEscapeSubLayout") final String skipJsonEscapeSubLayoutStr, 564 | // null, // @PluginAttribute("subLayoutAsElement") final String subLayoutAsElementStr, 565 | // null, // @PluginElement("Layout") Layout subLayout, 566 | // null // @PluginElement("Pairs") final KeyValuePair[] pairs 567 | // ); 568 | // } 569 | // 570 | // @Test 571 | // public void testPluginAttributeExcludeThread() { 572 | // LogStashJSONLayout layout = LogStashJSONLayout.createLayout( 573 | // null, // @PluginAttribute("locationInfo") final String locationInfo, 574 | // null, // @PluginAttribute("properties") final String properties, 575 | // null, // @PluginAttribute("complete") final String completeStr, 576 | // null, // @PluginAttribute("compact") final String compactStr, 577 | // null, // @PluginAttribute("newline") final String newlineStr, 578 | // null, // @PluginAttribute("commaAtEventEnd") final String commaAtEventEndStr, 579 | // null, // @PluginAttribute("charset") final String charsetName, 580 | // null, // @PluginAttribute("excludeLogger") final String excludeLoggerStr, 581 | // null, // @PluginAttribute("excludeLevel") final String excludeLevelStr, 582 | // null, // @PluginAttribute("excludeThread") final String excludeThreadStr, 583 | // null, // @PluginAttribute("excludeMessage") final String excludeMessageStr, 584 | // null, // @PluginAttribute("excludeLog") final String excludeLogStr, 585 | // null, // @PluginAttribute("excludeNDC") final String excludeNDCStr, 586 | // null, // @PluginAttribute("excludeThrown") final String excludeThrownStr, 587 | // null, // @PluginAttribute("skipJsonEscapeSubLayout") final String skipJsonEscapeSubLayoutStr, 588 | // null, // @PluginAttribute("subLayoutAsElement") final String subLayoutAsElementStr, 589 | // null, // @PluginElement("Layout") Layout subLayout, 590 | // null // @PluginElement("Pairs") final KeyValuePair[] pairs 591 | // ); 592 | // 593 | // } 594 | // 595 | // @Test 596 | // public void testPluginAttributeMessage() { 597 | // LogStashJSONLayout layout = LogStashJSONLayout.createLayout( 598 | // null, // @PluginAttribute("locationInfo") final String locationInfo, 599 | // null, // @PluginAttribute("properties") final String properties, 600 | // null, // @PluginAttribute("complete") final String completeStr, 601 | // null, // @PluginAttribute("compact") final String compactStr, 602 | // null, // @PluginAttribute("newline") final String newlineStr, 603 | // null, // @PluginAttribute("commaAtEventEnd") final String commaAtEventEndStr, 604 | // null, // @PluginAttribute("charset") final String charsetName, 605 | // null, // @PluginAttribute("excludeLogger") final String excludeLoggerStr, 606 | // null, // @PluginAttribute("excludeLevel") final String excludeLevelStr, 607 | // null, // @PluginAttribute("excludeThread") final String excludeThreadStr, 608 | // null, // @PluginAttribute("excludeMessage") final String excludeMessageStr, 609 | // null, // @PluginAttribute("excludeLog") final String excludeLogStr, 610 | // null, // @PluginAttribute("excludeNDC") final String excludeNDCStr, 611 | // null, // @PluginAttribute("excludeThrown") final String excludeThrownStr, 612 | // null, // @PluginAttribute("skipJsonEscapeSubLayout") final String skipJsonEscapeSubLayoutStr, 613 | // null, // @PluginAttribute("subLayoutAsElement") final String subLayoutAsElementStr, 614 | // null, // @PluginElement("Layout") Layout subLayout, 615 | // null // @PluginElement("Pairs") final KeyValuePair[] pairs 616 | // ); 617 | // 618 | // } 619 | // 620 | // @Test 621 | // public void testPluginAttributeNDC() { 622 | // LogStashJSONLayout layout = LogStashJSONLayout.createLayout( 623 | // null, // @PluginAttribute("locationInfo") final String locationInfo, 624 | // null, // @PluginAttribute("properties") final String properties, 625 | // null, // @PluginAttribute("complete") final String completeStr, 626 | // null, // @PluginAttribute("compact") final String compactStr, 627 | // null, // @PluginAttribute("newline") final String newlineStr, 628 | // null, // @PluginAttribute("commaAtEventEnd") final String commaAtEventEndStr, 629 | // null, // @PluginAttribute("charset") final String charsetName, 630 | // null, // @PluginAttribute("excludeLogger") final String excludeLoggerStr, 631 | // null, // @PluginAttribute("excludeLevel") final String excludeLevelStr, 632 | // null, // @PluginAttribute("excludeThread") final String excludeThreadStr, 633 | // null, // @PluginAttribute("excludeMessage") final String excludeMessageStr, 634 | // null, // @PluginAttribute("excludeLog") final String excludeLogStr, 635 | // null, // @PluginAttribute("excludeNDC") final String excludeNDCStr, 636 | // null, // @PluginAttribute("excludeThrown") final String excludeThrownStr, 637 | // null, // @PluginAttribute("skipJsonEscapeSubLayout") final String skipJsonEscapeSubLayoutStr, 638 | // null, // @PluginAttribute("subLayoutAsElement") final String subLayoutAsElementStr, 639 | // null, // @PluginElement("Layout") Layout subLayout, 640 | // null // @PluginElement("Pairs") final KeyValuePair[] pairs 641 | // ); 642 | // 643 | // } 644 | // 645 | // @Test 646 | // public void testPluginAttributeThrown() { 647 | // LogStashJSONLayout layout = LogStashJSONLayout.createLayout( 648 | // null, // @PluginAttribute("locationInfo") final String locationInfo, 649 | // null, // @PluginAttribute("properties") final String properties, 650 | // null, // @PluginAttribute("complete") final String completeStr, 651 | // null, // @PluginAttribute("compact") final String compactStr, 652 | // null, // @PluginAttribute("newline") final String newlineStr, 653 | // null, // @PluginAttribute("commaAtEventEnd") final String commaAtEventEndStr, 654 | // null, // @PluginAttribute("charset") final String charsetName, 655 | // null, // @PluginAttribute("excludeLogger") final String excludeLoggerStr, 656 | // null, // @PluginAttribute("excludeLevel") final String excludeLevelStr, 657 | // null, // @PluginAttribute("excludeThread") final String excludeThreadStr, 658 | // null, // @PluginAttribute("excludeMessage") final String excludeMessageStr, 659 | // null, // @PluginAttribute("excludeLog") final String excludeLogStr, 660 | // null, // @PluginAttribute("excludeNDC") final String excludeNDCStr, 661 | // null, // @PluginAttribute("excludeThrown") final String excludeThrownStr, 662 | // null, // @PluginAttribute("skipJsonEscapeSubLayout") final String skipJsonEscapeSubLayoutStr, 663 | // null, // @PluginAttribute("subLayoutAsElement") final String subLayoutAsElementStr, 664 | // null, // @PluginElement("Layout") Layout subLayout, 665 | // null // @PluginElement("Pairs") final KeyValuePair[] pairs 666 | // ); 667 | // 668 | // } 669 | // 670 | // @Test 671 | // public void testPluginAttributeSkipJSONSublayout() { 672 | // LogStashJSONLayout layout = LogStashJSONLayout.createLayout( 673 | // null, // @PluginAttribute("locationInfo") final String locationInfo, 674 | // null, // @PluginAttribute("properties") final String properties, 675 | // null, // @PluginAttribute("complete") final String completeStr, 676 | // null, // @PluginAttribute("compact") final String compactStr, 677 | // null, // @PluginAttribute("newline") final String newlineStr, 678 | // null, // @PluginAttribute("commaAtEventEnd") final String commaAtEventEndStr, 679 | // null, // @PluginAttribute("charset") final String charsetName, 680 | // null, // @PluginAttribute("excludeLogger") final String excludeLoggerStr, 681 | // null, // @PluginAttribute("excludeLevel") final String excludeLevelStr, 682 | // null, // @PluginAttribute("excludeThread") final String excludeThreadStr, 683 | // null, // @PluginAttribute("excludeMessage") final String excludeMessageStr, 684 | // null, // @PluginAttribute("excludeLog") final String excludeLogStr, 685 | // null, // @PluginAttribute("excludeNDC") final String excludeNDCStr, 686 | // null, // @PluginAttribute("excludeThrown") final String excludeThrownStr, 687 | // null, // @PluginAttribute("skipJsonEscapeSubLayout") final String skipJsonEscapeSubLayoutStr, 688 | // null, // @PluginAttribute("subLayoutAsElement") final String subLayoutAsElementStr, 689 | // null, // @PluginElement("Layout") Layout subLayout, 690 | // null // @PluginElement("Pairs") final KeyValuePair[] pairs 691 | // ); 692 | // 693 | // } 694 | // 695 | // @Test 696 | // public void testPluginAttributeLayout() { 697 | // LogStashJSONLayout layout = LogStashJSONLayout.createLayout( 698 | // null, // @PluginAttribute("locationInfo") final String locationInfo, 699 | // null, // @PluginAttribute("properties") final String properties, 700 | // null, // @PluginAttribute("complete") final String completeStr, 701 | // null, // @PluginAttribute("compact") final String compactStr, 702 | // null, // @PluginAttribute("newline") final String newlineStr, 703 | // null, // @PluginAttribute("commaAtEventEnd") final String commaAtEventEndStr, 704 | // null, // @PluginAttribute("charset") final String charsetName, 705 | // null, // @PluginAttribute("excludeLogger") final String excludeLoggerStr, 706 | // null, // @PluginAttribute("excludeLevel") final String excludeLevelStr, 707 | // null, // @PluginAttribute("excludeThread") final String excludeThreadStr, 708 | // null, // @PluginAttribute("excludeMessage") final String excludeMessageStr, 709 | // null, // @PluginAttribute("excludeLog") final String excludeLogStr, 710 | // null, // @PluginAttribute("excludeNDC") final String excludeNDCStr, 711 | // null, // @PluginAttribute("excludeThrown") final String excludeThrownStr, 712 | // null, // @PluginAttribute("skipJsonEscapeSubLayout") final String skipJsonEscapeSubLayoutStr, 713 | // null, // @PluginAttribute("subLayoutAsElement") final String subLayoutAsElementStr, 714 | // null, // @PluginElement("Layout") Layout subLayout, 715 | // null // @PluginElement("Pairs") final KeyValuePair[] pairs 716 | // ); 717 | // } 718 | // 719 | // @Test 720 | // public void testPluginAttributePairs() { 721 | // LogStashJSONLayout layout = LogStashJSONLayout.createLayout( 722 | // null, // @PluginAttribute("locationInfo") final String locationInfo, 723 | // null, // @PluginAttribute("properties") final String properties, 724 | // null, // @PluginAttribute("complete") final String completeStr, 725 | // null, // @PluginAttribute("compact") final String compactStr, 726 | // null, // @PluginAttribute("newline") final String newlineStr, 727 | // null, // @PluginAttribute("commaAtEventEnd") final String commaAtEventEndStr, 728 | // null, // @PluginAttribute("charset") final String charsetName, 729 | // null, // @PluginAttribute("excludeLogger") final String excludeLoggerStr, 730 | // null, // @PluginAttribute("excludeLevel") final String excludeLevelStr, 731 | // null, // @PluginAttribute("excludeThread") final String excludeThreadStr, 732 | // null, // @PluginAttribute("excludeMessage") final String excludeMessageStr, 733 | // null, // @PluginAttribute("excludeLog") final String excludeLogStr, 734 | // null, // @PluginAttribute("excludeNDC") final String excludeNDCStr, 735 | // null, // @PluginAttribute("excludeThrown") final String excludeThrownStr, 736 | // null, // @PluginAttribute("skipJsonEscapeSubLayout") final String skipJsonEscapeSubLayoutStr, 737 | // null, // @PluginAttribute("subLayoutAsElement") final String subLayoutAsElementStr, 738 | // null, // @PluginElement("Layout") Layout subLayout, 739 | // null // @PluginElement("Pairs") final KeyValuePair[] pairs 740 | // ); 741 | // 742 | // } 743 | // 744 | // 745 | // //TODO test variety of objects 746 | // 747 | // //TODO test all message types 748 | // 749 | // //TODO test markers 750 | // 751 | // //TODO demo expansion scenarios like audit log marker 752 | // 753 | // //TODO demo parameter object like audit 754 | // 755 | // //TODO test context 756 | // 757 | // 758 | // /** 759 | // * This test requires logstash (installed manually) and makes assumptions 760 | // * about both configuration and operating system... 761 | // *

762 | // * ... So, you probably ought not run it by default :) 763 | // */ 764 | // @Test(groups = "integration") 765 | // public void LogToLogStashTest() { 766 | // System.out.println("&&&&&&&&&&&&&&&&&&&"); 767 | // System.out.println("&&&&&&&&&&&&&&&&&&&"); 768 | // System.out.println("&&&&&&&&&&&&&&&&&&&"); 769 | // System.out.println("&&&&&&&&&&&&&&&&&&&"); 770 | // logger.info("TEST IS WIRED"); 771 | // 772 | // } 773 | 774 | } 775 | -------------------------------------------------------------------------------- /src/test/java/org/apache/logging/log4j/core/layout/LogStashJSONLayoutJacksonIT.java: -------------------------------------------------------------------------------- 1 | package org.apache.logging.log4j.core.layout; 2 | 3 | import java.nio.charset.Charset; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | 7 | import com.fasterxml.jackson.databind.ObjectMapper; 8 | import org.apache.logging.log4j.Level; 9 | import org.apache.logging.log4j.LogManager; 10 | import org.apache.logging.log4j.Logger; 11 | import org.apache.logging.log4j.core.LogEvent; 12 | import org.apache.logging.log4j.core.impl.Log4jLogEvent; 13 | import org.apache.logging.log4j.core.util.KeyValuePair; 14 | import org.apache.logging.log4j.message.Message; 15 | import org.apache.logging.log4j.message.SimpleMessage; 16 | import org.testng.annotations.AfterTest; 17 | import org.testng.annotations.BeforeTest; 18 | import org.testng.annotations.DataProvider; 19 | import org.testng.annotations.Test; 20 | 21 | import static org.hamcrest.MatcherAssert.assertThat; 22 | import static uk.co.datumedge.hamcrest.json.SameJSONAs.sameJSONAs; 23 | 24 | public class LogStashJSONLayoutJacksonIT { 25 | public static final String LOCATION_INFO = "LocationInfo"; 26 | private static Logger logger = LogManager.getLogger(LogStashJSONLayoutJacksonIT.class); 27 | 28 | 29 | @Test(enabled = false, dataProvider = "dp") 30 | public void f(Integer n, String s) { 31 | } 32 | 33 | @DataProvider 34 | public Object[][] dp() { 35 | return new Object[][]{ 36 | new Object[]{1, "a"}, 37 | new Object[]{2, "b"}, 38 | }; 39 | } 40 | 41 | @BeforeTest 42 | public void beforeTest() { 43 | } 44 | 45 | @AfterTest 46 | public void afterTest() { 47 | } 48 | 49 | 50 | ObjectMapper mapper = new ObjectMapper(); 51 | 52 | String expectedBasicSimpleTestJSON = "{\"@version\":\"1\"," + 53 | // "\"@timestamp\":\"2015-07-28T11:31:18.492-07:00\",\"timeMillis\":1438108278492," + 54 | "\"thread\":\""+ Thread.currentThread().getName() +"\"," + 55 | "\"level\":\"DEBUG\"," + 56 | "\"loggerName\":\"org.apache.logging.log4j.core.layout.LogStashJSONLayoutJacksonIT\"," + 57 | "\"message\":\"Test Message\"," + 58 | "\"endOfBatch\":false," + 59 | "\"loggerFqcn\":\"org.apache.logging.log4j.core.layout.LogStashJSONLayoutJacksonIT\","+ 60 | "\"contextMap\":[{\"key\":\"Foo\",\"value\":\"Bar\"},{\"key\":\"A\",\"value\":\"B\"}]}"; 61 | 62 | @Test 63 | public void BasicSimpleTest() throws Exception { 64 | Message simpleMessage = new SimpleMessage("Test Message"); 65 | 66 | Map mdc = new HashMap(); 67 | mdc.put("A","B");//Already some threadcontext 68 | 69 | LogEvent event = new Log4jLogEvent( 70 | logger.getName(), 71 | null, 72 | this.getClass().getCanonicalName(), 73 | Level.DEBUG, 74 | simpleMessage, 75 | null, 76 | mdc, 77 | null, 78 | Thread.currentThread().getName(), 79 | null, 80 | System.currentTimeMillis() 81 | ); 82 | 83 | 84 | AbstractJacksonLayout layout = LogStashJSONLayout.createLayout( 85 | true, //location 86 | true, //properties 87 | true, //complete 88 | false, //compact 89 | false, //eventEol 90 | Charset.defaultCharset(), 91 | new KeyValuePair[]{new KeyValuePair("Foo", "Bar")} 92 | ); 93 | 94 | String actualJSON = layout.toSerializable(event); 95 | System.out.println(actualJSON); 96 | assertThat(actualJSON, sameJSONAs(expectedBasicSimpleTestJSON) 97 | .allowingExtraUnexpectedFields() 98 | .allowingAnyArrayOrdering()); 99 | 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /src/test/resources/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | --------------------------------------------------------------------------------