1. build.gradle (또는 pom.xml)에 logback 라이브러리 추가

compile("ch.qos.logback:logback-classic:1.1.2")
compile("ch.qos.logback:logback-core:1.1.2")
compile("org.slf4j:slf4j-api:1.7.6")



2. logback-spring.xml 정의

<?xml version="1.0" encoding="UTF-8"?>
<configuration>

<include resource="org/springframework/boot/logging/logback/base.xml"/>

<logger name="org.springframework.web" level="info"/>
<logger name="com.kakao.story.gateway" level="debug" />
<springProfile name="production">
<logger name="com.kakao.story.gateway" level="info" />
</springProfile>



3. application.yml 에 logging.config 추가
가장 중요함. 

logging:
config: classpath:logback.xml




Posted by '김용환'
,

spring boot에서는 templates 디렉토리이 비어 있거나 templates 디렉토리가 없으면 에러가 발생한다.


FreeMakerAutoConfiguration 클래스에서 초기화때 체크하는 부분때문에 발생한다.


Caused by: java.lang.IllegalArgumentException: Cannot find template location(s): [classpath:/templates/] (please add some templates, check your FreeMarker configuration, or set spring.freemarker.checkTemplateLocation=false)

at org.springframework.util.Assert.notNull(Assert.java:115)

at org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration.checkTemplateLocationExists(FreeMarkerAutoConfiguration.java:80)

at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleElement.invoke(InitDestroyAnnotationBeanPostProcessor.java:354) ~[spring-beans-4.2.2.BUILD-SNAPSHOT.jar:4.2.2.BUILD-SNAPSHOT]



spring.freemarker.checkTemplateLocation=false 로 설정하면 더 이상 이슈가 발생하지 않는다.

spring:
  freemarker:
checkTemplateLocation: false


Posted by '김용환'
,


개발 단계(alpha, beta, production)마다 파일을 따로 두면 관리도 힘들다. 잘 보이지 않는 단점이 있다. 이를 한 번에 처리할 수 있는 spring boot logback이 있다. 


logback-alpha.xml

logback-beta.xml

logback-production.xml



logback 파일에 springProfile을 적용할 수 있도록 할 수 있다. 다음은 logback 파일이다.

<configuration>

<include resource="org/springframework/boot/logging/logback/base.xml"/>

<logger name="org.springframework.web" level="info"/>
<logger name="com.google.gateway" level="debug" />
<springProfile name="production">
<logger name="com.google.gateway" level="info" />
</springProfile>

...



logback.xml 파일에 springProfile을 그대로 설정하면, 에러가 발생한다.


19:52:20.008 [restartedMain] ERROR o.s.b.SpringApplication - Application startup failed

java.lang.IllegalStateException: java.lang.IllegalStateException: Logback configuration error detected:

ERROR in ch.qos.logback.core.joran.spi.Interpreter@8:38 - no applicable action for [springProfile], current ElementPath  is [[configuration][springProfile]]

ERROR in ch.qos.logback.core.joran.spi.Interpreter@8:38 - no applicable action for [springProfile], current ElementPath  is [[configuration][springProfile]]

ERROR in ch.qos.logback.core.joran.spi.Interpreter@9:63 - no applicable action for [logger], current ElementPath  is [[configuration][springProfile][logger]]

ERROR in ch.qos.logback.core.joran.spi.Interpreter@9:63 - no applicable action for [logger], current ElementPath  is [[configuration][springProfile][logger]]

at org.springframework.boot.logging.LoggingApplicationListener.initializeSystem(LoggingApplicationListener.java:238) ~[spring-boot-1.3.0.BUILD-SNAPSHOT.jar:1.3.0.BUILD-SNAPSHOT]


spring boot가 logback 스탠다드 파일인 logback.xml을 어떻게 할 수 없기 때문에 발생한다.

이를 해결하려면, logback-spring.xml 이라는 파일로 복사해서 쓰도록 하면 에러가 발생하지 않는다.


logback.xml -> logback-spring.xml 




참고

http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/html/boot-features-logging.html

You cannot use extensions in the standard logback.xml configuration file since it’s loaded too early. You need to either use logback-spring.xml or define a logging.config property.




Posted by '김용환'
,


spring boot 1.2.x 버전을 사용하고 있을 때만 되어도, spring-loaded(hot swapping)를 쓸만하다고 생각했다.


java8의 일부 기능(method  reference)이 되지 않아 이슈를 작성해도 근 한달동안 넘게 방치되어 있다. (그래서 프로젝트에서 더이상 쓰지 않을 것이다.)


https://github.com/spring-projects/spring-loaded/issues


더딘 spring loaded 의 발전은 특별한 일 없으면 정리될지도 모르겠다. 





최근에 spring boot 1.3.0 에서 devtools(cool swapping)라는 모듈을 배포했고, 이를 사용하면 spring-loaded의 효과를 볼 수 있다. 


spring-loaded는 bytecode reloading이 주 목적이라면, devtools는 재시작에 더 중점을 두고 있다. 현재 devtools을 써보고 있지만, 확실히 play보다는 빨라 쓸만한 것 같고, 큰 문제가 없어서 계속 같이 갈 것 같다.



spring boot + gradle을 사용하면 좋은 조합인 것 같다. 


gradle의 daemon 설정과 gradle의 dependency 설정으로 

compile("org.springframework.boot:spring-boot-devtools")

 'gradlew clean bootRun' 명령어로 쉽게 수정/컴파일/실행하니 생산성이 늘어난다. 




https://spring.io/blog/2015/06/17/devtools-in-spring-boot-1-3


http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/html/using-boot-devtools.html

Posted by '김용환'
,


spring boot에서는 resource 디렉토리 밑의 정적 자원의 파일 캐시 기능을 사용할 수 있다.


spring.resources.cache-period=3600



해당 프로퍼티를 적용하면, 3600초동안 캐시하고, HTTP 상태코드는 200이 아닌 304를 리턴한다. 




참고로 1.3.0 부터 사용할 수 있는 devtools(cool swapping tool)을 사용하면 cache는 무조건 0으로 설정된다. 즉 사용하지 않도록 설정되어 있다.


properties.put("spring.resources.cache-period", "0");

https://github.com/spring-projects/spring-boot/blob/4e410681aa77f44b61f27542ec29308008412d42/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/env/DevToolsPropertyDefaultsPostProcessor.java

https://github.com/spring-projects/spring-boot/commit/4e410681aa77f44b61f27542ec29308008412d42



resource cache 뿐 아니라. 캐쉬는 모두 사용하지 않도록 되어 있다. 

public class DevToolsPropertyDefaultsPostProcessor implements EnvironmentPostProcessor {


private static final Map<String, Object> PROPERTIES;

static {

Map<String, Object> properties = new HashMap<String, Object>();

properties.put("spring.thymeleaf.cache", "false");

properties.put("spring.freemarker.cache", "false");

properties.put("spring.groovy.template.cache", "false");

properties.put("spring.velocity.cache", "false");

properties.put("spring.mustache.cache", "false");

properties.put("server.session.persistent", "true");

properties.put("spring.h2.console.enabled", "true");

properties.put("spring.resources.cache-period", "0");

PROPERTIES = Collections.unmodifiableMap(properties);

}



Posted by '김용환'
,


spring boot 1.3.0에서 undertow 서블릿 컨테이너를 실행하면 아래와 같이 websocket관련 warn로그가 발생한다.



[2015-09-30 16:27:23.266] boot - 33938  WARN [restartedMain] --- jsr: UT026009: XNIO worker was not set on WebSocketDeploymentInfo, the default worker will be used

[2015-09-30 16:27:23.369] boot - 33938  WARN [restartedMain] --- jsr: UT026010: Buffer pool was not set on WebSocketDeploymentInfo, the default pool will be used




해당 로그를 발생하게 하는 클래스가 JsrWebSocketLogger임을 확인한다.

http://grepcode.com/file/repo1.maven.org/maven2/io.undertow/undertow-websockets-jsr/1.2.7.Final/io/undertow/websockets/jsr/JsrWebSocketLogger.java#JsrWebSocketLogger

@LogMessage(level = ..)
@Message(id = 26009, value = "XNIO worker was not set on WebSocketDeploymentInfo, the default worker will be used")



spring-boot의 undertow starter에 websocket jsr 라이브러리를 dependency하고 있다.

<dependency>
<groupId>io.undertow</groupId>
<artifactId>undertow-websockets-jsr</artifactId>
</dependency>


websocket jsr을 dependency lib 에서 exclude하니. 더 이상 warn로그는 발생하지 않았다.

compile("org.springframework.boot:spring-boot-starter-undertow") {
exclude group: 'io.undertow', module: 'undertow-websockets-jsr'
}


Posted by '김용환'
,


Spring Boot에서 Phase(alpha, beta, production....) 정보를 테스트할 때 과거에는 조금 힘들었는데, 


Spring Boot의 @ActiveProfiles을 사용하면 쉽게 Integration 테스트를 진행할 수 있다. 


Environment 객체의 getActiveProfiles()는 @ActiveProfiles 값을 리턴하여 정상적으로 동작하는지 테스트 가능하다. 

runtime 테스트를 줄일 수 있다. 

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(EnvironmentTest.class)
@ActiveProfiles("development")
public class EnvironmentTest {
@Autowired
private Environment environment; .. environment.getActiveProfiles();

}




Posted by '김용환'
,



spring boot에서 @Async 어노테이션을 사용할 수 있다. 다만 application context에 task:annotation-driven을 두지 말아야 한다. 즉 아래와 같은 코드를 사용하면 에러가 발생한다.

<!--For @Async-->
<bean id="asyncExecuter" class="java.util.concurrent.Executors" factory-method="newFixedThreadPool">
<constructor-arg value="100" />
</bean>

<task:executor id="threadPoolTaskExecutor" pool-size="5-200" queue-capacity="1000"/>

<task:annotation-driven executor="asyncExecuter" />


spring boot 자체적으로 이미 Async 기능을 제공하기 때문이다.

xml 설정에도 포함해서 실행한다면, 아래와 같은 에러가 발생할 수 있다. xml 설정을 삭제한다면, 에러는 발생하지 않는다.



configuration problem: Only one AsyncAnnotationBeanPostProcessor may exist within the context.



참고자료

https://spring.io/guides/gs/async-method/

Posted by '김용환'
,



아래와 같은 에러가 발생하면 application-context.xml (또는 관련 파일)에서 task를 제대로 정의했는지 했으면, xsi schema 문서는 정확히 쌍을 이루어서 선언되었는지를 확인한다.



cvc-complex-type.2.4.c: 일치하는 와일드 카드 문자가 엄격하게 적용되지만 'task:executor' 요소에 대한 선언을 찾을 수 없습니다.


xmlns:task="http://www.springframework.org/schema/task"



xsi:schemaLocation="http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd"


Posted by '김용환'
,



gradle의 gradle dameon을 사용하니, 획기적으로 컴파일 속도가 빨라진다.


특히 Spring Boot + gradle 속도가 엄청 빨라진다. (1/4로 줄어듬)


org.gradle.daemon=true


설정 방법은 두가지가 있다. 


터미널에서 사용시  ~/.gradle/gradle.properties 에 위의 옵션 추가한다.


intellij idea에서는 Preference -> Compiler 창에서 Compiler independent modules in parrallel 체크박스에 on 시킨다.




(참조 자료)


http://kwonnam.pe.kr/wiki/gradle

http://anton46.com/improving-gradle-build-performance/

Posted by '김용환'
,