├── .gitignore ├── README.md └── java-log-aggregation ├── .mvn └── wrapper │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── docker-compose.yml ├── mvnw ├── mvnw.cmd ├── pom.xml └── src ├── main ├── docker │ └── Dockerfile ├── java │ └── com │ │ └── vlotar │ │ └── demo │ │ ├── JavaLogAggregationApplication.java │ │ ├── MetricAndHealthExporterService.java │ │ ├── RequestLoggingFilter.java │ │ ├── SwaggerConfiguration.java │ │ ├── dao │ │ └── UserDAO.java │ │ ├── domain │ │ └── User.java │ │ ├── exception │ │ └── ResourceNotFoundException.java │ │ ├── service │ │ ├── UserService.java │ │ └── converter │ │ │ └── UserResourceConverter.java │ │ └── web │ │ ├── controller │ │ ├── ErrorHandler.java │ │ └── UserController.java │ │ ├── request │ │ └── UserResourceRequest.java │ │ └── response │ │ ├── CustomErrorResponse.java │ │ ├── ResourceIdResponse.java │ │ ├── SuccessResponse.java │ │ └── UserResourceResponse.java ├── logstash │ └── config │ │ └── logstash.conf └── resources │ ├── application.properties │ └── logback.xml └── test └── java └── com └── vlotar └── demo ├── JavaLogAggregationApplicationTests.java ├── service ├── UserServiceTest.java └── converter │ └── UserResourceConverterTest.java └── web └── controller └── UserControllerTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .idea 3 | */.mvn 4 | **/*.iml 5 | */target/ 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # spring-boot-elk 2 | Log Management (ELK) for Spring Boot application 3 | ======================== 4 | 5 | This simple example demonstrates how easy you can enable quite powerful and advanced log management in to your Spring Boot application using Docker. 6 | 7 | **Stack of frameworks/technologies:** 8 | * Spring Boot 9 | * Swagger 10 | * Logback 11 | * Logstash 12 | * ElasticSearch 13 | * Kibana 14 | * Docker 15 | 16 | _Prerequisites:_ 17 | * docker version 1.10.3 18 | * docker-compose version 1.6.2 19 | * JDK8 20 | * Maven 21 | 22 | ## Start the app 23 | 24 | 1. In order to run the application in docker you would need to build a docker image first. 25 | This simple maven command will to the trick: 26 | `mvn clean package docker:build -Dmaven.test.skip=true` 27 |
_**Note**: for Mac users it can be more difficult. 28 | Make sure that you have Environment variables properly exported. 29 | The easiest way to do this is the following command (https://docs.docker.com/machine/reference/env/): 30 | `eval $(docker-machine env default)`
31 | DOCKER_TLS_VERIFY=1
32 | DOCKER_HOST=tcp://192.168.99.100:2376; //you can find this value by typing: `docker-machine ls`
33 | DOCKER_CERT_PATH=/Users/{user}/.docker/machine/certs_
34 | 35 | 2. Go to root of the project and run: `docker-compose up -d` 36 | Docker will pull all missing images and start new containers. 37 | After a while you would be ready to use the app. 38 | 39 | 3. Run: `docker ps -a`. Find an image *"javalogaggregation_web"* and check the exposed port (For example 0.0.0.0:**32808**->8080/tcp). 40 | You would be able to access the application by typing the following URL in the browser: 41 | http://192.168.99.100:32808/swagger-ui.html#! 42 |
_**Note**: ip address is a docker machine IP address._ 43 | Here you can play with existing REST API. 44 | 45 | If there is an error that elasticsearch conntainer not run, may be because of "max virtual memory areas vm.max_map_count [65530] likely too low, increase to at least [262144]" 46 | 47 | To fix it, run: 48 | 49 | `sudo sysctl -w vm.max_map_count=262144` 50 | 51 | https://www.elastic.co/guide/en/elasticsearch/reference/current/docker.html 52 | 53 | 4. In order to configure Kibana you would need to go to: 54 |
http://localhost:5601/ or http://{$DOCKER_HOST_IP}:5601/ 55 |
Configure your Kibana and play with your logs. 56 |
_**Note**: For Mac users the port 5601 should be added to Port Forwarding Rules of you Docker Virtual Machine_ 57 | 58 | ## Stop the app 59 | Simply type: `docker-compose down` 60 | 61 | ## Security 62 | user/user 63 | -------------------------------------------------------------------------------- /java-log-aggregation/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vlotar/spring-boot-elk/89f3f5075a009d70515ca8c7768e5a8548becf62/java-log-aggregation/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /java-log-aggregation/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.3.3/apache-maven-3.3.3-bin.zip -------------------------------------------------------------------------------- /java-log-aggregation/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | services: 3 | 4 | web: 5 | image: javalogaggregation_web 6 | ports: 7 | - "8080:8080" 8 | links: 9 | - logstash 10 | networks: 11 | - front-tier 12 | 13 | elasticsearch: 14 | image: docker.elastic.co/elasticsearch/elasticsearch:6.2.2 15 | container_name: elasticsearch 16 | environment: 17 | - cluster.name=docker-cluster 18 | - bootstrap.memory_lock=true 19 | - xpack.security.enabled=false 20 | - "ES_JAVA_OPTS=-Xms512m -Xmx512m" 21 | ulimits: 22 | memlock: 23 | soft: -1 24 | hard: -1 25 | nofile: 26 | soft: 65536 27 | hard: 65536 28 | mem_limit: 2g 29 | cap_add: 30 | - IPC_LOCK 31 | volumes: 32 | - esdata1:/usr/share/elasticsearch/data 33 | ports: 34 | - "9200:9200" 35 | - "9300:9300" 36 | networks: 37 | - back-tier 38 | 39 | logstash: 40 | image: docker.elastic.co/logstash/logstash:6.2.2 41 | container_name: logstash 42 | command: logstash -f /etc/logstash/conf.d/logstash.conf 43 | volumes: 44 | - ./src/main/logstash/config:/etc/logstash/conf.d 45 | ports: 46 | - "5000:5000" 47 | links: 48 | - elasticsearch 49 | networks: 50 | - front-tier 51 | - back-tier 52 | 53 | kibana: 54 | image: docker.elastic.co/kibana/kibana:6.2.2 55 | container_name: kibana 56 | environment: 57 | - ELASTICSEARCH_URL=http://elasticsearch:9200 58 | ports: 59 | - "5601:5601" 60 | links: 61 | - elasticsearch 62 | networks: 63 | - back-tier 64 | 65 | 66 | volumes: 67 | esdata1: 68 | driver: local 69 | 70 | networks: 71 | front-tier: 72 | back-tier: 73 | driver: bridge 74 | -------------------------------------------------------------------------------- /java-log-aggregation/mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven2 Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /etc/mavenrc ] ; then 40 | . /etc/mavenrc 41 | fi 42 | 43 | if [ -f "$HOME/.mavenrc" ] ; then 44 | . "$HOME/.mavenrc" 45 | fi 46 | 47 | fi 48 | 49 | # OS specific support. $var _must_ be set to either true or false. 50 | cygwin=false; 51 | darwin=false; 52 | mingw=false 53 | case "`uname`" in 54 | CYGWIN*) cygwin=true ;; 55 | MINGW*) mingw=true;; 56 | Darwin*) darwin=true 57 | # 58 | # Look for the Apple JDKs first to preserve the existing behaviour, and then look 59 | # for the new JDKs provided by Oracle. 60 | # 61 | if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK ] ; then 62 | # 63 | # Apple JDKs 64 | # 65 | export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home 66 | fi 67 | 68 | if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Java/JavaVirtualMachines/CurrentJDK ] ; then 69 | # 70 | # Apple JDKs 71 | # 72 | export JAVA_HOME=/System/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home 73 | fi 74 | 75 | if [ -z "$JAVA_HOME" ] && [ -L "/Library/Java/JavaVirtualMachines/CurrentJDK" ] ; then 76 | # 77 | # Oracle JDKs 78 | # 79 | export JAVA_HOME=/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home 80 | fi 81 | 82 | if [ -z "$JAVA_HOME" ] && [ -x "/usr/libexec/java_home" ]; then 83 | # 84 | # Apple JDKs 85 | # 86 | export JAVA_HOME=`/usr/libexec/java_home` 87 | fi 88 | ;; 89 | esac 90 | 91 | if [ -z "$JAVA_HOME" ] ; then 92 | if [ -r /etc/gentoo-release ] ; then 93 | JAVA_HOME=`java-config --jre-home` 94 | fi 95 | fi 96 | 97 | if [ -z "$M2_HOME" ] ; then 98 | ## resolve links - $0 may be a link to maven's home 99 | PRG="$0" 100 | 101 | # need this for relative symlinks 102 | while [ -h "$PRG" ] ; do 103 | ls=`ls -ld "$PRG"` 104 | link=`expr "$ls" : '.*-> \(.*\)$'` 105 | if expr "$link" : '/.*' > /dev/null; then 106 | PRG="$link" 107 | else 108 | PRG="`dirname "$PRG"`/$link" 109 | fi 110 | done 111 | 112 | saveddir=`pwd` 113 | 114 | M2_HOME=`dirname "$PRG"`/.. 115 | 116 | # make it fully qualified 117 | M2_HOME=`cd "$M2_HOME" && pwd` 118 | 119 | cd "$saveddir" 120 | # echo Using m2 at $M2_HOME 121 | fi 122 | 123 | # For Cygwin, ensure paths are in UNIX format before anything is touched 124 | if $cygwin ; then 125 | [ -n "$M2_HOME" ] && 126 | M2_HOME=`cygpath --unix "$M2_HOME"` 127 | [ -n "$JAVA_HOME" ] && 128 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 129 | [ -n "$CLASSPATH" ] && 130 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 131 | fi 132 | 133 | # For Migwn, ensure paths are in UNIX format before anything is touched 134 | if $mingw ; then 135 | [ -n "$M2_HOME" ] && 136 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 137 | [ -n "$JAVA_HOME" ] && 138 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 139 | # TODO classpath? 140 | fi 141 | 142 | if [ -z "$JAVA_HOME" ]; then 143 | javaExecutable="`which javac`" 144 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 145 | # readlink(1) is not available as standard on Solaris 10. 146 | readLink=`which readlink` 147 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 148 | if $darwin ; then 149 | javaHome="`dirname \"$javaExecutable\"`" 150 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 151 | else 152 | javaExecutable="`readlink -f \"$javaExecutable\"`" 153 | fi 154 | javaHome="`dirname \"$javaExecutable\"`" 155 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 156 | JAVA_HOME="$javaHome" 157 | export JAVA_HOME 158 | fi 159 | fi 160 | fi 161 | 162 | if [ -z "$JAVACMD" ] ; then 163 | if [ -n "$JAVA_HOME" ] ; then 164 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 165 | # IBM's JDK on AIX uses strange locations for the executables 166 | JAVACMD="$JAVA_HOME/jre/sh/java" 167 | else 168 | JAVACMD="$JAVA_HOME/bin/java" 169 | fi 170 | else 171 | JAVACMD="`which java`" 172 | fi 173 | fi 174 | 175 | if [ ! -x "$JAVACMD" ] ; then 176 | echo "Error: JAVA_HOME is not defined correctly." >&2 177 | echo " We cannot execute $JAVACMD" >&2 178 | exit 1 179 | fi 180 | 181 | if [ -z "$JAVA_HOME" ] ; then 182 | echo "Warning: JAVA_HOME environment variable is not set." 183 | fi 184 | 185 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 186 | 187 | # For Cygwin, switch paths to Windows format before running java 188 | if $cygwin; then 189 | [ -n "$M2_HOME" ] && 190 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 191 | [ -n "$JAVA_HOME" ] && 192 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 193 | [ -n "$CLASSPATH" ] && 194 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 195 | fi 196 | 197 | # traverses directory structure from process work directory to filesystem root 198 | # first directory with .mvn subdirectory is considered project base directory 199 | find_maven_basedir() { 200 | local basedir=$(pwd) 201 | local wdir=$(pwd) 202 | while [ "$wdir" != '/' ] ; do 203 | if [ -d "$wdir"/.mvn ] ; then 204 | basedir=$wdir 205 | break 206 | fi 207 | wdir=$(cd "$wdir/.."; pwd) 208 | done 209 | echo "${basedir}" 210 | } 211 | 212 | # concatenates all lines of a file 213 | concat_lines() { 214 | if [ -f "$1" ]; then 215 | echo "$(tr -s '\n' ' ' < "$1")" 216 | fi 217 | } 218 | 219 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-$(find_maven_basedir)} 220 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 221 | 222 | # Provide a "standardized" way to retrieve the CLI args that will 223 | # work with both Windows and non-Windows executions. 224 | MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" 225 | export MAVEN_CMD_LINE_ARGS 226 | 227 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 228 | 229 | exec "$JAVACMD" \ 230 | $MAVEN_OPTS \ 231 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 232 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 233 | ${WRAPPER_LAUNCHER} "$@" 234 | -------------------------------------------------------------------------------- /java-log-aggregation/mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM http://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven2 Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' 39 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 40 | 41 | @REM set %HOME% to equivalent of $HOME 42 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 43 | 44 | @REM Execute a user defined script before this one 45 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 46 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 47 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 48 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 49 | :skipRcPre 50 | 51 | @setlocal 52 | 53 | set ERROR_CODE=0 54 | 55 | @REM To isolate internal variables from possible post scripts, we use another setlocal 56 | @setlocal 57 | 58 | @REM ==== START VALIDATION ==== 59 | if not "%JAVA_HOME%" == "" goto OkJHome 60 | 61 | echo. 62 | echo Error: JAVA_HOME not found in your environment. >&2 63 | echo Please set the JAVA_HOME variable in your environment to match the >&2 64 | echo location of your Java installation. >&2 65 | echo. 66 | goto error 67 | 68 | :OkJHome 69 | if exist "%JAVA_HOME%\bin\java.exe" goto init 70 | 71 | echo. 72 | echo Error: JAVA_HOME is set to an invalid directory. >&2 73 | echo JAVA_HOME = "%JAVA_HOME%" >&2 74 | echo Please set the JAVA_HOME variable in your environment to match the >&2 75 | echo location of your Java installation. >&2 76 | echo. 77 | goto error 78 | 79 | @REM ==== END VALIDATION ==== 80 | 81 | :init 82 | 83 | set MAVEN_CMD_LINE_ARGS=%* 84 | 85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 86 | @REM Fallback to current working directory if not found. 87 | 88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 90 | 91 | set EXEC_DIR=%CD% 92 | set WDIR=%EXEC_DIR% 93 | :findBaseDir 94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 95 | cd .. 96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 97 | set WDIR=%CD% 98 | goto findBaseDir 99 | 100 | :baseDirFound 101 | set MAVEN_PROJECTBASEDIR=%WDIR% 102 | cd "%EXEC_DIR%" 103 | goto endDetectBaseDir 104 | 105 | :baseDirNotFound 106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 107 | cd "%EXEC_DIR%" 108 | 109 | :endDetectBaseDir 110 | 111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 112 | 113 | @setlocal EnableExtensions EnableDelayedExpansion 114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 116 | 117 | :endReadAdditionalConfig 118 | 119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 120 | 121 | set WRAPPER_JAR="".\.mvn\wrapper\maven-wrapper.jar"" 122 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 123 | 124 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CMD_LINE_ARGS% 125 | if ERRORLEVEL 1 goto error 126 | goto end 127 | 128 | :error 129 | set ERROR_CODE=1 130 | 131 | :end 132 | @endlocal & set ERROR_CODE=%ERROR_CODE% 133 | 134 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 135 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 136 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 137 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 138 | :skipRcPost 139 | 140 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 141 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 142 | 143 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 144 | 145 | exit /B %ERROR_CODE% -------------------------------------------------------------------------------- /java-log-aggregation/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.vlotar.demo 7 | javalogaggregation 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | java-log-aggregation 12 | Demo project for Spring Boot, Logstash, ElasticSearch, Kibana using Docker 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 1.5.2.RELEASE 18 | 19 | 20 | 21 | 22 | UTF-8 23 | 1.8 24 | 25 | 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-data-jpa 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-starter-data-rest 35 | 36 | 37 | org.springframework.boot 38 | spring-boot-starter-security 39 | 40 | 41 | org.springframework.boot 42 | spring-boot-starter-web 43 | 44 | 45 | org.springframework.boot 46 | spring-boot-starter-actuator 47 | 48 | 49 | 50 | 51 | org.projectlombok 52 | lombok 53 | 1.16.6 54 | 55 | 56 | 57 | 58 | com.h2database 59 | h2 60 | runtime 61 | 62 | 63 | 64 | org.springframework.boot 65 | spring-boot-starter-test 66 | test 67 | 68 | 69 | org.springframework.restdocs 70 | spring-restdocs-mockmvc 71 | test 72 | 73 | 74 | 75 | com.google.code.gson 76 | gson 77 | 78 | 79 | 80 | 81 | net.logstash.logback 82 | logstash-logback-encoder 83 | 4.6 84 | 85 | 86 | ch.qos.logback 87 | logback-core 88 | 89 | 90 | ch.qos.logback 91 | logback-classic 92 | 93 | 94 | ch.qos.logback 95 | logback-access 96 | 97 | 98 | 99 | 100 | 101 | 102 | io.springfox 103 | springfox-swagger2 104 | 2.4.0 105 | 106 | 107 | 108 | io.springfox 109 | springfox-swagger-ui 110 | 2.4.0 111 | 112 | 113 | 114 | 115 | 116 | java-log-aggregation 117 | 118 | 119 | org.springframework.boot 120 | spring-boot-maven-plugin 121 | 122 | 123 | com.spotify 124 | docker-maven-plugin 125 | 0.4.13 126 | 127 | ${project.artifactId}_web 128 | src/main/docker 129 | 130 | 131 | / 132 | ${project.build.directory} 133 | ${project.build.finalName}.jar 134 | 135 | 136 | 137 | 138 | 139 | ch.qos.logback 140 | logback-classic 141 | 1.1.5 142 | 143 | 144 | 145 | 146 | 147 | com.github.kongchen 148 | swagger-maven-plugin 149 | 3.1.0 150 | 151 | 152 | 153 | true 154 | com.vlotar.demo.web.controller 155 | service.url:8080 156 | / 157 | 158 | Demo project for Spring Boot, Logstash, ElasticSearch, Kibana using Docker 159 | 0.0.1 160 | 161 | https://github.com/vlotar/spring-boot-elk 162 | 163 | 164 | vadim.lot@gmail.com 165 | Vadym Lotar 166 | 167 | 168 | http://www.apache.org/licenses/LICENSE-2.0.html 169 | Apache 2.0 170 | 171 | 172 | ${project.build.directory}/generated/swagger-ui 173 | 174 | 175 | 176 | 177 | 178 | compile 179 | 180 | generate 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | -------------------------------------------------------------------------------- /java-log-aggregation/src/main/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM java:openjdk-8-jdk-alpine 2 | VOLUME /tmp 3 | ADD java-log-aggregation.jar app.jar 4 | RUN sh -c 'touch /app.jar' 5 | ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"] 6 | -------------------------------------------------------------------------------- /java-log-aggregation/src/main/java/com/vlotar/demo/JavaLogAggregationApplication.java: -------------------------------------------------------------------------------- 1 | package com.vlotar.demo; 2 | 3 | import ch.qos.logback.classic.helpers.MDCInsertingServletFilter; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | import org.springframework.boot.web.servlet.FilterRegistrationBean; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.scheduling.annotation.EnableScheduling; 9 | 10 | /** 11 | * Spring Boot application launcher 12 | */ 13 | @SpringBootApplication 14 | @EnableScheduling 15 | public class JavaLogAggregationApplication { 16 | 17 | public static void main(String[] args) { 18 | //starts spring application (embedded tomcat, security, logging etc.) 19 | SpringApplication.run(JavaLogAggregationApplication.class, args); 20 | } 21 | 22 | /** 23 | * A servlet filter that inserts various values retrieved from the incoming http 24 | * request into the MDC 25 | * 26 | * @return {@link FilterRegistrationBean} 27 | */ 28 | @Bean 29 | public FilterRegistrationBean userInsertingMdcFilterRegistrationBean() { 30 | FilterRegistrationBean registrationBean = new FilterRegistrationBean(); 31 | MDCInsertingServletFilter userFilter = new MDCInsertingServletFilter(); 32 | registrationBean.setFilter(userFilter); 33 | return registrationBean; 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /java-log-aggregation/src/main/java/com/vlotar/demo/MetricAndHealthExporterService.java: -------------------------------------------------------------------------------- 1 | package com.vlotar.demo; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.boot.actuate.endpoint.HealthEndpoint; 7 | import org.springframework.boot.actuate.endpoint.MetricsEndpoint; 8 | import org.springframework.boot.actuate.health.Health; 9 | import org.springframework.scheduling.annotation.Scheduled; 10 | import org.springframework.stereotype.Service; 11 | 12 | import static net.logstash.logback.marker.Markers.append; 13 | 14 | /** 15 | * Allows to expose actuator metrics 16 | * 17 | * @author lotarvad 18 | */ 19 | @Service 20 | class MetricAndHealthExporterService { 21 | 22 | private static final Logger LOGGER = LoggerFactory.getLogger(MetricAndHealthExporterService.class); 23 | 24 | @Autowired 25 | private MetricsEndpoint metricsEndpoint; 26 | 27 | @Autowired 28 | private HealthEndpoint healthEndpoint; 29 | 30 | /** 31 | * Exposes all metrics each 10 minutes after an initial delay of a minute 32 | */ 33 | @Scheduled(initialDelay = 60000, fixedDelay = 600000) 34 | void exportMetrics() { 35 | this.metricsEndpoint.invoke().forEach(this::log); 36 | } 37 | 38 | /** 39 | * Pushes heart beats every 10 seconds 40 | */ 41 | @Scheduled(initialDelay = 10000, fixedDelay = 10000) 42 | void pushHeartbeat() { 43 | Health health = this.healthEndpoint.invoke(); 44 | LOGGER.info(append("Heartbeat", health.getStatus()), "Heartbeat details {}", health.getDetails()); 45 | } 46 | 47 | private void log(String metricName, Object metricValue) { 48 | LOGGER.info(append("metric", metricName), "Reporting metric {}={}", metricName, metricValue); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /java-log-aggregation/src/main/java/com/vlotar/demo/RequestLoggingFilter.java: -------------------------------------------------------------------------------- 1 | package com.vlotar.demo; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.stereotype.Component; 6 | import org.springframework.util.StopWatch; 7 | 8 | import javax.servlet.*; 9 | import javax.servlet.http.HttpServletRequest; 10 | import javax.servlet.http.HttpServletResponse; 11 | import java.io.IOException; 12 | import java.util.HashMap; 13 | import java.util.Map; 14 | 15 | import static net.logstash.logback.marker.Markers.appendEntries; 16 | 17 | /** 18 | * Allows to track requests and log needed information 19 | * 20 | * @author lotarvad 21 | */ 22 | @Component 23 | public class RequestLoggingFilter implements Filter { 24 | 25 | private final Logger LOGGER = LoggerFactory.getLogger(RequestLoggingFilter.class); 26 | 27 | public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { 28 | HttpServletResponse response = (HttpServletResponse) res; 29 | HttpServletRequest request = (HttpServletRequest) req; 30 | String url = request.getServletPath(); 31 | 32 | StopWatch stopWatch = new StopWatch(); 33 | stopWatch.start(); 34 | chain.doFilter(req, res); 35 | stopWatch.stop(); 36 | 37 | Map markers = new HashMap<>(); 38 | markers.put("res.status", response.getStatus()); 39 | markers.put("req.executionTime", stopWatch.getTotalTimeMillis()); 40 | LOGGER.info(appendEntries(markers), "Execution time for {} {} is {} ms", url, request.getMethod(), stopWatch.getTotalTimeMillis()); 41 | } 42 | 43 | public void init(FilterConfig filterConfig) { 44 | } 45 | 46 | public void destroy() { 47 | } 48 | 49 | } -------------------------------------------------------------------------------- /java-log-aggregation/src/main/java/com/vlotar/demo/SwaggerConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.vlotar.demo; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import springfox.documentation.builders.PathSelectors; 6 | import springfox.documentation.builders.RequestHandlerSelectors; 7 | import springfox.documentation.spi.DocumentationType; 8 | import springfox.documentation.spring.web.plugins.Docket; 9 | import springfox.documentation.swagger2.annotations.EnableSwagger2; 10 | 11 | /** 12 | * Configuration class which enables Swagger 13 | * 14 | * @author vlotar 15 | */ 16 | @Configuration 17 | @EnableSwagger2 18 | public class SwaggerConfiguration { 19 | 20 | @Bean 21 | public Docket api() { 22 | return new Docket(DocumentationType.SWAGGER_2) 23 | .select() 24 | .apis(RequestHandlerSelectors.any()) 25 | .paths(PathSelectors.any()) 26 | .build(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /java-log-aggregation/src/main/java/com/vlotar/demo/dao/UserDAO.java: -------------------------------------------------------------------------------- 1 | package com.vlotar.demo.dao; 2 | 3 | import com.vlotar.demo.domain.User; 4 | import org.springframework.data.repository.CrudRepository; 5 | import org.springframework.stereotype.Repository; 6 | 7 | /** 8 | * An interface declaration in order to perform CRUD operations for {@link User} resource 9 | * 10 | * @author vlotar 11 | */ 12 | @Repository 13 | public interface UserDAO extends CrudRepository { 14 | 15 | } 16 | -------------------------------------------------------------------------------- /java-log-aggregation/src/main/java/com/vlotar/demo/domain/User.java: -------------------------------------------------------------------------------- 1 | package com.vlotar.demo.domain; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | import lombok.ToString; 6 | 7 | import javax.persistence.Column; 8 | import javax.persistence.Entity; 9 | import javax.persistence.GeneratedValue; 10 | import javax.persistence.Id; 11 | import javax.persistence.Table; 12 | import java.io.Serializable; 13 | 14 | /** 15 | * {@link User} resource persisted in the database. 16 | * 17 | * @author vlotar 18 | */ 19 | @Entity 20 | @Table(name = "users") 21 | @Getter 22 | @Setter 23 | @ToString 24 | public class User implements Serializable { 25 | 26 | private static final long serialVersionUID = -1674204092853306884L; 27 | 28 | @Id 29 | @GeneratedValue 30 | private Long id; 31 | 32 | @Column(name = "first_name", nullable = false) 33 | private String firstName; 34 | 35 | @Column(name = "last_name", nullable = false) 36 | private String lastName; 37 | 38 | @Column(name = "country", nullable = false) 39 | private String country; 40 | 41 | } 42 | -------------------------------------------------------------------------------- /java-log-aggregation/src/main/java/com/vlotar/demo/exception/ResourceNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.vlotar.demo.exception; 2 | 3 | /** 4 | * This exception should be thrown in all cases when a resource cannot be found 5 | * 6 | * @author vlotar 7 | */ 8 | public class ResourceNotFoundException extends RuntimeException { 9 | 10 | /** 11 | * Instantiates a new {@link ResourceNotFoundException}. 12 | * 13 | * @param message the message 14 | */ 15 | public ResourceNotFoundException(final String message) { 16 | super(message); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /java-log-aggregation/src/main/java/com/vlotar/demo/service/UserService.java: -------------------------------------------------------------------------------- 1 | package com.vlotar.demo.service; 2 | 3 | import com.google.common.collect.Lists; 4 | import com.vlotar.demo.dao.UserDAO; 5 | import com.vlotar.demo.domain.User; 6 | import com.vlotar.demo.exception.ResourceNotFoundException; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.stereotype.Service; 9 | import org.springframework.transaction.annotation.Transactional; 10 | 11 | import java.util.Collection; 12 | 13 | /** 14 | * Service layer is responsible for performing basic CRUD operations on {@link User} resource. 15 | * 16 | * @author vlotar 17 | */ 18 | @Service 19 | public class UserService { 20 | 21 | @Autowired 22 | private UserDAO userDAO; 23 | 24 | /** 25 | * Allows to retrieve all existing users from the database. 26 | * Pagination is not implemented in order to keep it simple. 27 | * 28 | * @return the all existing {@link User} entities 29 | */ 30 | public Collection getAllUsers() { 31 | return Lists.newArrayList(this.userDAO.findAll()); 32 | } 33 | 34 | /** 35 | * Allows to get a user by the given user identifier. 36 | * 37 | * @param userId the user id 38 | * @return the {@link User} 39 | */ 40 | public User getUser(final Long userId) { 41 | return findUserOrThrowNotFoundException(userId); 42 | } 43 | 44 | /** 45 | * Allows to create a new user and persist the resource in the database. 46 | * Operation is transactional. 47 | * Operation cannot be executed if client specifies user identifier. 48 | * 49 | * @param user the {@link User} resource 50 | * @return newly created user identifier 51 | */ 52 | @Transactional 53 | public Long createUser(final User user) { 54 | //persist new user and return user identifier 55 | return this.userDAO.save(user).getId(); 56 | } 57 | 58 | /** 59 | * Allows to update specific user. 60 | * Operation is transactional. 61 | * 62 | * @param user the {@link User} resource 63 | */ 64 | @Transactional 65 | public void updateUser(final User user) { 66 | //check that user exists 67 | findUserOrThrowNotFoundException(user.getId()); 68 | //persist user 69 | this.userDAO.save(user); 70 | } 71 | 72 | /** 73 | * Allows to delete a user by the given user identifier. 74 | * Operation is transactional. 75 | * 76 | * @param userId user identifier 77 | */ 78 | @Transactional 79 | public void deleteUser(final Long userId) { 80 | //check that user exists 81 | findUserOrThrowNotFoundException(userId); 82 | //delete existing user 83 | this.userDAO.delete(userId); 84 | } 85 | 86 | private User findUserOrThrowNotFoundException(final Long userId) { 87 | User user = this.userDAO.findOne(userId); 88 | if (user == null) { 89 | throw new ResourceNotFoundException(String.format("User %s not found", userId)); 90 | } 91 | return user; 92 | } 93 | 94 | /** 95 | * Sets user dao. 96 | * 97 | * @param userDAO the user dao 98 | */ 99 | void setUserDAO(final UserDAO userDAO) { 100 | this.userDAO = userDAO; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /java-log-aggregation/src/main/java/com/vlotar/demo/service/converter/UserResourceConverter.java: -------------------------------------------------------------------------------- 1 | package com.vlotar.demo.service.converter; 2 | 3 | import com.vlotar.demo.domain.User; 4 | import com.vlotar.demo.web.request.UserResourceRequest; 5 | import com.vlotar.demo.web.response.UserResourceResponse; 6 | import org.springframework.stereotype.Component; 7 | 8 | /** 9 | * Helper class which is responsible for converting domain {@link User} entity to some DTO objects. 10 | * {@link UserResourceResponse} 11 | * {@link UserResourceRequest} 12 | * 13 | * @author vlotar 14 | */ 15 | @Component 16 | public class UserResourceConverter { 17 | 18 | /** 19 | * Converts {@link User} to {@link UserResourceResponse} 20 | * 21 | * @param user {@link User} 22 | * @return {@link UserResourceResponse} 23 | */ 24 | public UserResourceResponse convert(final User user) { 25 | return new UserResourceResponse(user.getId(), user.getFirstName(), user.getLastName(), user.getCountry()); 26 | } 27 | 28 | /** 29 | * Converts {@link UserResourceRequest} to a domain {@link User}. 30 | * 31 | * @param request {@link UserResourceRequest} 32 | * @return {@link User} 33 | */ 34 | public User convert(final UserResourceRequest request) { 35 | final User user = new User(); 36 | convertCommonFields(request, user); 37 | return user; 38 | } 39 | 40 | /** 41 | * Converts {@link UserResourceRequest} to a domain {@link User}. 42 | * 43 | * @param request {@link UserResourceRequest} 44 | * @return {@link User} 45 | */ 46 | public User convert(final UserResourceRequest request, final Long userId) { 47 | final User user = new User(); 48 | user.setId(userId); 49 | convertCommonFields(request, user); 50 | return user; 51 | } 52 | 53 | private void convertCommonFields(final UserResourceRequest request, final User user) { 54 | user.setFirstName(request.getFirstName()); 55 | user.setLastName(request.getLastName()); 56 | user.setCountry(request.getCountry()); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /java-log-aggregation/src/main/java/com/vlotar/demo/web/controller/ErrorHandler.java: -------------------------------------------------------------------------------- 1 | package com.vlotar.demo.web.controller; 2 | 3 | import com.google.gson.Gson; 4 | import com.vlotar.demo.exception.ResourceNotFoundException; 5 | import com.vlotar.demo.web.response.CustomErrorResponse; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import org.springframework.http.HttpStatus; 9 | import org.springframework.web.bind.annotation.ControllerAdvice; 10 | import org.springframework.web.bind.annotation.ExceptionHandler; 11 | import org.springframework.web.bind.annotation.ResponseBody; 12 | import org.springframework.web.bind.annotation.ResponseStatus; 13 | 14 | /** 15 | * Generic error handling mechanism. 16 | * 17 | * @author vlotar 18 | */ 19 | @ControllerAdvice 20 | class ErrorHandler { 21 | 22 | private static final Logger LOGGER = LoggerFactory.getLogger(ErrorHandler.class); 23 | 24 | private enum ERROR_CODE { 25 | E0001 26 | } 27 | 28 | @ResponseStatus(HttpStatus.NOT_FOUND) // 404 29 | @ExceptionHandler(ResourceNotFoundException.class) 30 | @ResponseBody 31 | public String handleNotFound(ResourceNotFoundException ex) { 32 | LOGGER.warn("Entity was not found", ex); 33 | return new Gson().toJson(new CustomErrorResponse(ERROR_CODE.E0001.name(), ex.getMessage())); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /java-log-aggregation/src/main/java/com/vlotar/demo/web/controller/UserController.java: -------------------------------------------------------------------------------- 1 | package com.vlotar.demo.web.controller; 2 | 3 | import com.google.gson.Gson; 4 | import com.vlotar.demo.service.UserService; 5 | import com.vlotar.demo.service.converter.UserResourceConverter; 6 | import com.vlotar.demo.web.request.UserResourceRequest; 7 | import com.vlotar.demo.web.response.ResourceIdResponse; 8 | import com.vlotar.demo.web.response.SuccessResponse; 9 | import com.vlotar.demo.web.response.UserResourceResponse; 10 | import io.swagger.annotations.Api; 11 | import io.swagger.annotations.ApiOperation; 12 | import io.swagger.annotations.ApiParam; 13 | import org.slf4j.Logger; 14 | import org.slf4j.LoggerFactory; 15 | import org.springframework.beans.factory.annotation.Autowired; 16 | import org.springframework.web.bind.annotation.PathVariable; 17 | import org.springframework.web.bind.annotation.RequestBody; 18 | import org.springframework.web.bind.annotation.RequestMapping; 19 | import org.springframework.web.bind.annotation.RequestMethod; 20 | import org.springframework.web.bind.annotation.ResponseBody; 21 | import org.springframework.web.bind.annotation.RestController; 22 | 23 | import javax.validation.Valid; 24 | import java.util.stream.Collectors; 25 | 26 | /** 27 | * @author vlotar 28 | */ 29 | @Api(value = "/users", description = "API manages 'users' allowing to perform basic CRUD operations") 30 | @RestController 31 | @RequestMapping("/users") class UserController { 32 | 33 | private static final Logger LOGGER = LoggerFactory.getLogger(UserController.class); 34 | 35 | @Autowired 36 | private UserService userService; 37 | 38 | @Autowired 39 | private UserResourceConverter converter; 40 | 41 | @ApiOperation( 42 | value = "Retrieve 'request' by Id", 43 | notes = "Allows to retrieve existing 'request' resource by its identifier", 44 | response = UserResourceResponse.class 45 | ) 46 | @RequestMapping(value = "/{userId}", method = RequestMethod.GET, produces = "application/json") 47 | @ResponseBody 48 | public String getUser(@ApiParam(value = "Unique 'request' identifier") @PathVariable final Long userId) { 49 | LOGGER.debug("Trying to retrieve User by ID: " + userId); 50 | return toJson(this.converter.convert(this.userService.getUser(userId))); 51 | } 52 | 53 | @ApiOperation( 54 | value = "Retrieve all 'users'", 55 | notes = "Allows to retrieve all existing 'users'", 56 | response = UserResourceResponse.class, 57 | responseContainer = "Set" 58 | ) 59 | @RequestMapping(method = RequestMethod.GET, produces = "application/json") 60 | @ResponseBody 61 | public String getAllUsers() { 62 | LOGGER.debug("Trying to retrieve all users"); 63 | return toJson( 64 | this.userService.getAllUsers().stream() 65 | .map(user -> this.converter.convert(user)).collect(Collectors.toSet())); 66 | } 67 | 68 | @ApiOperation( 69 | value = "Create new 'request'", 70 | notes = "Allows to create new 'request'", 71 | response = ResourceIdResponse.class 72 | ) 73 | @RequestMapping(method = RequestMethod.POST, produces = "application/json") 74 | @ResponseBody 75 | public String createUser(@Valid @RequestBody UserResourceRequest request) { 76 | LOGGER.debug("Trying to create a user: " + request.toString()); 77 | Long userId = this.userService.createUser(this.converter.convert(request)); 78 | return toJson(new ResourceIdResponse(userId)); 79 | } 80 | 81 | @ApiOperation( 82 | value = "Update existing 'request'", 83 | notes = "Allows to update existing 'request'", 84 | response = SuccessResponse.class 85 | ) 86 | @RequestMapping(value = "/{userId}", method = RequestMethod.PUT, produces = "application/json") 87 | @ResponseBody 88 | public String updateUser(@ApiParam(value = "Unique 'request' identifier") @PathVariable final Long userId, 89 | @Valid @RequestBody UserResourceRequest request) { 90 | LOGGER.debug("Trying to update a user: " + request.toString()); 91 | this.userService.updateUser(this.converter.convert(request, userId)); 92 | return toJson(new SuccessResponse()); 93 | } 94 | 95 | @ApiOperation( 96 | value = "Delete existing 'request'", 97 | notes = "Allows to delete existing 'request'", 98 | response = SuccessResponse.class 99 | ) 100 | @RequestMapping(value = "/{userId}", method = RequestMethod.DELETE, produces = "application/json") 101 | @ResponseBody 102 | public String deleteUser(@ApiParam(value = "Unique 'request' identifier") @PathVariable final Long userId) { 103 | LOGGER.debug("Trying to delete a user: " + userId); 104 | this.userService.deleteUser(userId); 105 | return toJson(new SuccessResponse()); 106 | } 107 | 108 | private static String toJson(Object object) { 109 | return new Gson().toJson(object); 110 | } 111 | 112 | /** 113 | * Sets request service. 114 | * 115 | * @param userService the request service 116 | */ 117 | void setUserService(final UserService userService) { 118 | this.userService = userService; 119 | } 120 | 121 | /** 122 | * Sets converter. 123 | * 124 | * @param converter the converter 125 | */ 126 | void setConverter(final UserResourceConverter converter) { 127 | this.converter = converter; 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /java-log-aggregation/src/main/java/com/vlotar/demo/web/request/UserResourceRequest.java: -------------------------------------------------------------------------------- 1 | package com.vlotar.demo.web.request; 2 | 3 | import io.swagger.annotations.ApiModelProperty; 4 | import lombok.Getter; 5 | import lombok.Setter; 6 | import lombok.ToString; 7 | import org.hibernate.validator.constraints.NotBlank; 8 | 9 | import java.io.Serializable; 10 | 11 | /** 12 | * Represents request body for Create User operation. 13 | * Necessary for proper Swagger documentation. 14 | * 15 | * @author vlotar 16 | */ 17 | @Getter 18 | @Setter 19 | @ToString 20 | public class UserResourceRequest implements Serializable { 21 | 22 | private static final long serialVersionUID = 2657944775357946081L; 23 | 24 | @ApiModelProperty(value = "User's first name", required = true) 25 | @NotBlank 26 | private String firstName; 27 | 28 | @ApiModelProperty(value = "User's last name", required = true) 29 | @NotBlank 30 | private String lastName; 31 | 32 | @ApiModelProperty(value = "Country where user's living", required = true) 33 | @NotBlank 34 | private String country; 35 | 36 | } 37 | -------------------------------------------------------------------------------- /java-log-aggregation/src/main/java/com/vlotar/demo/web/response/CustomErrorResponse.java: -------------------------------------------------------------------------------- 1 | package com.vlotar.demo.web.response; 2 | 3 | import io.swagger.annotations.ApiModelProperty; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Getter; 6 | 7 | import java.io.Serializable; 8 | 9 | /** 10 | * {@link CustomErrorResponse} will be returned in case of custom error occurrence 11 | * Necessary for proper Swagger documentation. 12 | * 13 | * @author vlotar 14 | */ 15 | @SuppressWarnings("unused") 16 | @AllArgsConstructor 17 | @Getter 18 | public class CustomErrorResponse implements Serializable { 19 | 20 | private static final long serialVersionUID = -7755563009111273632L; 21 | 22 | @ApiModelProperty(value = "Custom Error code", required = true) 23 | private String errorCode; 24 | 25 | @ApiModelProperty(value = "Custom Error message") 26 | private String errorMessage; 27 | 28 | } 29 | -------------------------------------------------------------------------------- /java-log-aggregation/src/main/java/com/vlotar/demo/web/response/ResourceIdResponse.java: -------------------------------------------------------------------------------- 1 | package com.vlotar.demo.web.response; 2 | 3 | import io.swagger.annotations.ApiModelProperty; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Getter; 6 | 7 | import java.io.Serializable; 8 | 9 | /** 10 | * Specific Response which is used for the cases when only resource identifier has to be present in the response. 11 | * Necessary for proper Swagger documentation. 12 | * 13 | * @author vlotar 14 | */ 15 | @SuppressWarnings("unused") 16 | @AllArgsConstructor 17 | @Getter 18 | public class ResourceIdResponse implements Serializable { 19 | 20 | private static final long serialVersionUID = -7692197744600484558L; 21 | 22 | @ApiModelProperty(value = "Resource identifier", required = true) 23 | private Long id; 24 | } 25 | -------------------------------------------------------------------------------- /java-log-aggregation/src/main/java/com/vlotar/demo/web/response/SuccessResponse.java: -------------------------------------------------------------------------------- 1 | package com.vlotar.demo.web.response; 2 | 3 | import io.swagger.annotations.ApiModelProperty; 4 | import lombok.Getter; 5 | 6 | import java.io.Serializable; 7 | 8 | /** 9 | * Represents successful response when there is nothing to return 10 | * Necessary for proper Swagger documentation. 11 | * 12 | * @author vlotar 13 | */ 14 | @SuppressWarnings("unused") 15 | @Getter 16 | public class SuccessResponse implements Serializable { 17 | 18 | private static final long serialVersionUID = -355194637398843627L; 19 | 20 | @ApiModelProperty(value = "Status of the response", allowableValues = "success", required = true) 21 | private final String status = "success"; 22 | } 23 | -------------------------------------------------------------------------------- /java-log-aggregation/src/main/java/com/vlotar/demo/web/response/UserResourceResponse.java: -------------------------------------------------------------------------------- 1 | package com.vlotar.demo.web.response; 2 | 3 | import io.swagger.annotations.ApiModelProperty; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Getter; 6 | 7 | import java.io.Serializable; 8 | 9 | /** 10 | * Represents response body when {@link com.vlotar.demo.domain.User} resource has to be returned 11 | * Necessary for proper Swagger documentation. 12 | * 13 | * @author vlotar 14 | */ 15 | @AllArgsConstructor 16 | @Getter 17 | public class UserResourceResponse implements Serializable { 18 | 19 | private static final long serialVersionUID = -8761235292937715094L; 20 | 21 | @ApiModelProperty(value = "Unique user identifier", required = true) 22 | private Long id; 23 | 24 | @ApiModelProperty(value = "User's first name", required = true) 25 | private String firstName; 26 | 27 | @ApiModelProperty(value = "User's last name", required = true) 28 | private String lastName; 29 | 30 | @ApiModelProperty(value = "Country where user's living", required = true) 31 | private String country; 32 | } 33 | -------------------------------------------------------------------------------- /java-log-aggregation/src/main/logstash/config/logstash.conf: -------------------------------------------------------------------------------- 1 | input { 2 | tcp { 3 | port => 5000 4 | codec => json_lines 5 | } 6 | } 7 | 8 | output { 9 | elasticsearch { 10 | hosts => "elasticsearch:9200" 11 | } 12 | } -------------------------------------------------------------------------------- /java-log-aggregation/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | security.user.password=user -------------------------------------------------------------------------------- /java-log-aggregation/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | logstash:5000 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | { 20 | 21 | 22 | "appName": "elk-demo", 23 | "appVersion": "1.0" 24 | } 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /java-log-aggregation/src/test/java/com/vlotar/demo/JavaLogAggregationApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.vlotar.demo; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 7 | import org.springframework.test.context.web.WebAppConfiguration; 8 | 9 | @RunWith(SpringJUnit4ClassRunner.class) 10 | @SpringBootTest(classes = JavaLogAggregationApplication.class) 11 | @WebAppConfiguration 12 | public class JavaLogAggregationApplicationTests { 13 | 14 | @Test 15 | public void contextLoads() { 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /java-log-aggregation/src/test/java/com/vlotar/demo/service/UserServiceTest.java: -------------------------------------------------------------------------------- 1 | package com.vlotar.demo.service; 2 | 3 | import com.vlotar.demo.dao.UserDAO; 4 | import com.vlotar.demo.domain.User; 5 | import com.vlotar.demo.exception.ResourceNotFoundException; 6 | import org.junit.Assert; 7 | import org.junit.Before; 8 | import org.junit.Test; 9 | import org.mockito.Mockito; 10 | import org.mockito.stubbing.Answer; 11 | 12 | import java.util.Collection; 13 | import java.util.Collections; 14 | 15 | /** 16 | * @author vlotar 17 | */ 18 | public class UserServiceTest { 19 | 20 | private UserService userService; 21 | 22 | private UserDAO userDAO; 23 | 24 | @Before 25 | public void setUp() throws Exception { 26 | this.userService = new UserService(); 27 | this.userDAO = Mockito.mock(UserDAO.class); 28 | this.userService.setUserDAO(this.userDAO); 29 | } 30 | 31 | @Test 32 | public void getAllUsers() throws Exception { 33 | User user = new User(); 34 | Mockito.when(this.userDAO.findAll()).thenReturn(Collections.singleton(user)); 35 | Collection users = this.userService.getAllUsers(); 36 | Assert.assertEquals(1, users.size()); 37 | Assert.assertEquals(user, users.iterator().next()); 38 | } 39 | 40 | @Test 41 | public void getUser() throws Exception { 42 | User user = new User(); 43 | Mockito.when(this.userDAO.findOne(1L)).thenReturn(user); 44 | Assert.assertEquals(user, this.userService.getUser(1L)); 45 | } 46 | 47 | @Test(expected = ResourceNotFoundException.class) 48 | public void getUser_notFound() throws Exception { 49 | Mockito.when(this.userDAO.findOne(1L)).thenReturn(null); 50 | this.userService.getUser(1L); 51 | } 52 | 53 | @Test 54 | public void createUser() throws Exception { 55 | User user = new User(); 56 | Mockito.when(this.userDAO.save(user)).thenAnswer((Answer) invocationOnMock -> { 57 | User user1 = (User) invocationOnMock.getArguments()[0]; 58 | user1.setId(1L); 59 | return user1; 60 | }); 61 | 62 | Assert.assertEquals(1, this.userService.createUser(user).longValue()); 63 | } 64 | 65 | @Test 66 | public void updateUser() throws Exception { 67 | User user = new User(); 68 | user.setId(1L); 69 | Mockito.when(this.userDAO.findOne(1L)).thenReturn(user); 70 | this.userService.updateUser(user); 71 | } 72 | 73 | @Test(expected = ResourceNotFoundException.class) 74 | public void updateUser_notFound() throws Exception { 75 | User user = new User(); 76 | user.setId(1L); 77 | Mockito.when(this.userDAO.findOne(1L)).thenReturn(null); 78 | this.userService.updateUser(user); 79 | } 80 | 81 | @Test 82 | public void deleteUser() throws Exception { 83 | Mockito.when(this.userDAO.findOne(1L)).thenReturn(new User()); 84 | this.userService.deleteUser(1L); 85 | } 86 | 87 | @Test(expected = ResourceNotFoundException.class) 88 | public void deleteUser_notFound() throws Exception { 89 | Mockito.when(this.userDAO.findOne(1L)).thenReturn(null); 90 | this.userService.deleteUser(1L); 91 | } 92 | } -------------------------------------------------------------------------------- /java-log-aggregation/src/test/java/com/vlotar/demo/service/converter/UserResourceConverterTest.java: -------------------------------------------------------------------------------- 1 | package com.vlotar.demo.service.converter; 2 | 3 | import com.vlotar.demo.domain.User; 4 | import com.vlotar.demo.web.request.UserResourceRequest; 5 | import com.vlotar.demo.web.response.UserResourceResponse; 6 | import org.junit.Assert; 7 | import org.junit.Before; 8 | import org.junit.Test; 9 | 10 | /** 11 | * @author vlotar 12 | */ 13 | public class UserResourceConverterTest { 14 | 15 | private UserResourceConverter converter; 16 | 17 | @Before 18 | public void setUp() throws Exception { 19 | this.converter = new UserResourceConverter(); 20 | } 21 | 22 | @Test 23 | public void convertUserToUserResourceResponse() throws Exception { 24 | User user = new User(); 25 | user.setId(1L); 26 | user.setFirstName("Mickey"); 27 | user.setLastName("Mouse"); 28 | user.setCountry("UA"); 29 | UserResourceResponse response = this.converter.convert(user); 30 | Assert.assertEquals(1, response.getId().longValue()); 31 | Assert.assertEquals("Mickey", response.getFirstName()); 32 | Assert.assertEquals("Mouse", response.getLastName()); 33 | Assert.assertEquals("UA", response.getCountry()); 34 | } 35 | 36 | @Test 37 | public void convertCreateUserRequestToUser() throws Exception { 38 | UserResourceRequest request = new UserResourceRequest(); 39 | request.setFirstName("Mickey"); 40 | request.setLastName("Mouse"); 41 | request.setCountry("UA"); 42 | 43 | User user = this.converter.convert(request); 44 | Assert.assertEquals("Mickey", user.getFirstName()); 45 | Assert.assertEquals("Mouse", user.getLastName()); 46 | Assert.assertEquals("UA", user.getCountry()); 47 | Assert.assertNull(user.getId()); 48 | } 49 | 50 | @Test 51 | public void convertUpdateUserRequestToUser() throws Exception { 52 | UserResourceRequest request = new UserResourceRequest(); 53 | request.setFirstName("Mickey"); 54 | request.setLastName("Mouse"); 55 | request.setCountry("UA"); 56 | 57 | User user = this.converter.convert(request, 1L); 58 | Assert.assertEquals("Mickey", user.getFirstName()); 59 | Assert.assertEquals("Mouse", user.getLastName()); 60 | Assert.assertEquals("UA", user.getCountry()); 61 | Assert.assertEquals(1, user.getId().longValue()); 62 | } 63 | } -------------------------------------------------------------------------------- /java-log-aggregation/src/test/java/com/vlotar/demo/web/controller/UserControllerTest.java: -------------------------------------------------------------------------------- 1 | package com.vlotar.demo.web.controller; 2 | 3 | import com.vlotar.demo.domain.User; 4 | import com.vlotar.demo.service.UserService; 5 | import com.vlotar.demo.service.converter.UserResourceConverter; 6 | import org.junit.Before; 7 | import org.junit.Test; 8 | import org.mockito.Mockito; 9 | import org.springframework.http.MediaType; 10 | import org.springframework.test.web.servlet.MockMvc; 11 | import org.springframework.test.web.servlet.setup.MockMvcBuilders; 12 | 13 | import java.nio.charset.Charset; 14 | import java.util.ArrayList; 15 | 16 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; 17 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; 18 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; 19 | 20 | /** 21 | * @author vlotar 22 | */ 23 | public class UserControllerTest { 24 | 25 | private static final MediaType APPLICATION_JSON_UTF8 = new MediaType(MediaType.APPLICATION_JSON.getType(), MediaType.APPLICATION_JSON.getSubtype(), 26 | Charset.forName("utf8")); 27 | 28 | private MockMvc mockMvc; 29 | 30 | private UserService userService; 31 | 32 | @Before 33 | public void setUp() throws Exception { 34 | UserController userController = new UserController(); 35 | ErrorHandler errorHandler = new ErrorHandler(); 36 | 37 | this.userService = Mockito.mock(UserService.class); 38 | userController.setUserService(this.userService); 39 | 40 | final UserResourceConverter converter = new UserResourceConverter(); 41 | userController.setConverter(converter); 42 | 43 | this.mockMvc = MockMvcBuilders.standaloneSetup(userController, errorHandler).build(); 44 | } 45 | 46 | @Test 47 | public void getUser() throws Exception { 48 | User user = new User(); 49 | user.setCountry("UA"); 50 | user.setFirstName("Mickey"); 51 | user.setLastName("Mouse"); 52 | Mockito.when(this.userService.getUser(1L)).thenReturn(user); 53 | this.mockMvc.perform(get("/users/1")).andExpect(status().isOk()) 54 | .andExpect(content().string("{\"firstName\":\"Mickey\",\"lastName\":\"Mouse\",\"country\":\"UA\"}")); 55 | } 56 | 57 | @Test 58 | public void getAllUsers() throws Exception { 59 | Mockito.when(this.userService.getAllUsers()).thenReturn(new ArrayList<>()); 60 | this.mockMvc.perform(get("/users")).andExpect(status().isOk()).andExpect(content().string("[]")); 61 | } 62 | 63 | @Test 64 | public void createUser() throws Exception { 65 | Mockito.when(this.userService.createUser(Mockito.any())).thenReturn(1L); 66 | this.mockMvc.perform(post("/users").contentType(APPLICATION_JSON_UTF8).content("{\"firstName\":\"Mickey\", \"lastName\":\"Mouse\", \"country\":\"UA\"}")) 67 | .andExpect(status().isOk()) 68 | .andExpect(content().string("{\"id\":1}")); 69 | } 70 | 71 | @Test 72 | public void updateUser() throws Exception { 73 | this.userService.updateUser(Mockito.any()); 74 | this.mockMvc.perform( 75 | put("/users/1").contentType(APPLICATION_JSON_UTF8).content("{\"firstName\":\"Mickey\", \"lastName\":\"Mouse\", \"country\":\"UA\"}")) 76 | .andExpect(status().isOk()) 77 | .andExpect(content().string("{\"status\":\"success\"}")); 78 | } 79 | 80 | @Test 81 | public void deleteUser() throws Exception { 82 | this.userService.deleteUser(1L); 83 | this.mockMvc.perform(delete("/users/1")) 84 | .andExpect(status().isOk()) 85 | .andExpect(content().string("{\"status\":\"success\"}")); 86 | } 87 | } --------------------------------------------------------------------------------