├── .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 | }
--------------------------------------------------------------------------------