logger - spring 수업노트

-LOGGER

  • System.out.println 사용 금지

-why?

system.out.println 을 사용을 하면 아래의 메소드가 소환이 되는데,

public void println(String x ) {

synchronized (this) { → 동기화작업을 한다.

print(x);

newLine();

}

}

→ 계속 여기서 머물고 있을 수 있다.

→ 서버이용자가 많으면 서버가 다운이 될 수 있다.

-동기화. → 동시에 접속하지 못하도록 하는것. lock 을 거는 것

  • 보충개념 : synchronized

    synchronized 함수를 호출 하고 있기 때문에, 제어가 동시에 처리되지 않고 한개가 끝난

    후에 그 다음 것이 실행된다. 즉 a(), b() 두개를 모두 다 빠르게 계속해서 처리할

    수 없고 제어가 a 가 전부 끝나고 b 처리 b가 전부 끝나고 a 또는 b 처리 이런식으로

    되기 때문에 속도가 느려질 수 밖에 없을 거라고 보인다. multi thread 처리가

    되지 못하기 때문에.

  • 대신 logger 를 사용할 것이다.

-logging 사용 이유 : 개발시 의도한대로 동작하고 있는지 검사

ex : 웹 브라우저를 통해 server로 요청 ( localhost/basicServlet) 을 할때 doGet메서드가 호출되는지 로그를 통해 확인

파라미터가 의도한대로 서블릿으로 전달이 되었는지 확인

*logging 과 비슷한 목적으로 사용하는 것 : 디버깅

-필요할 떄만 로그를 보고, 삭제후 다시 필요할 수 있다.

-배포를 하게 된다. → console 에 찍고 file, mail 로 보낼 수 있다.

-종류 :

log4j , slf4j (simple logging facade for java )

-1세대 라이브러리 → 서로 호환이 안됬다. → slf4j 는 인터페이스형식이라 구현부분이랑 선언부분을 구분을 해놓아서 호환문제를 해결할 수 있다.

  • Command line argument: -

Dwtp.deploy=D:\A_TeachingMaterial\6.JspSpring\workspace\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\wtpwebapps

pom.xml에서 Dependency Hierachy에 들어가면

logback-classic , javax.servlet-api 에 따라서 logback-core, slf4j-api 가 딸려나온다.

  • pom.xml 에서

<scope>provided</scope>

<scope>test</scope>

두개를 주석처리 해보자.

<dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.0.1</version> <!-- <scope>provided</scope> --> </dependency>

<dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.3</version> <!-- <scope>test</scope> --> </dependency>

scope 생략시(default) compile → 컴파일 과정에서 배포되는 과정까지 해당 라이브러리가 필요함을 의미

-test : test 과정 까지만 필요, 배포시(packaging) 시 제외 됨

-provided(제공이 되어짐) : 운영환경에서 해당 라이브러리가 제공이 된다. (배포할 떄 빠진다. )

  1. <scope>provided</scope>를 주석처리하면

logback-classic.jar, logback-core.jar , slf4j.jar 세가지가 생긴다.

  1. <scope>test</scope>를 주석처리하면

javax.servlet-api .jar 가 생긴다.

http://logback.qos.ch/manual/configuration.html

  • logback.xml 파일 생성한다. → 운영환경에서도 사용할것이다.

: Java Resources/ src/main/resources 아래 생성

-logback.xml 에 형식 선언되어 있다.

-Logger 로그를 작성, appender 에게 전달


<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- encoders are assigned the type
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
 
 <logger name="kr.or.ddit" level="error">
	<appender-ref ref="STDOUT" />
 </logger>

<!--   <root level="debug">
<appender-ref ref="STDOUT" />
</root> -->
</configuration>

  • 개발자가 java 에서 private static final Logger logger = LoggerFactory.getLogger(BasicServlet.class); 설정하고 logger.debug("BasicServlet.doGet"); 요렇게 해주면, ? -→ logback.xml 에서 level을 debug 로 설정해주면 (서버에 접속할때 ) 콘솔에 나온다. 그렇지만 level을 error 로 설정해주면, logger.debug("BasicServlet.doGet"); 같이 그 밑에 레벨인 trace, debug, info, warm, 일때는 콘솔에 출력하지 않는다.

-logging level

trace: debug 보다 상세한 정보를 출력

debug : 프로그램을 디버깅 하기 위한 정보를 출력

info : 상태변경과 같은 정보성 메세지 출력시

warm : 향후 시스템에 문제가 발생할 가능성이 있는 메세지 출력시

error : 에러가 발생했을떄( ex) catch )

-log level 설정에 따른 logger 메소드 동작여부 정리

-만약 log level 을 debug 보다 높은 레벨로 설정할 경우 logger.debug() 는 출력되지 않지만즉 로그를 생성하지 않지만, 메소드 인자인 문자열 + 문자열 ==> 문자열 결합 연산은 발생한다. 

- 문자열 결합은 생각보다 큰 퍼포몬스를 일으킨다. String buffer 클래스로 바꿔버린다.


아래 코드처럼 변경한다. 


logger.debug("BasicServlet.doGet() userid parameter : " + req.getParameter("userid"));

--------------------------------> 

logger.debug("BasicServlet.doGet() userid parameter : {} {} " ,  req.getParameter("userid"),

req.getParameter("password"));




-servlet : java 에 html 코드를 삽입

생기는 문제점

  1. html 코드들이 문자 취급 되기 때문에 오타를 발견 하기가 어렵다.

  2. 협업의 문제점

java - 프로그래머

html - 디자이너

  1. 개발자가 코드를 리딩하는데 가독성의 문제

⇒ jsp 의 탄생 배경


jsp : html 코드에 java 코드를 삽입

문제 1) 문자열 결합 피하는 이유 : 문자열 결합은 → String + 연산은 메모리 문제와 퍼포먼스 문제 야기

String 은 class 이다. → heap 메모리를 차지한다.즉 String 객체 생성은 메모리를 사용한다.

문자열 + 문자열은 → 새로운 메모리 주소를 할당하게 된다. Object 수도 함께 늘어나고, memory 할당도 늘어난다.

#추가적인 자바 지식 : 그럼 대신

StringBuilder 나 StringBuffer Class 사용해볼까? → 기존 메모리 주소의 크기 확장

StringBuilder s = new StringBuilder("su");

s.append("jeong");

sb.append("awesome");

이것의 문제 : StringBuilder 객체 관리, 코드의 복잡성

  • StringBulder 는 thread-safe x , StringBuffer 는 thread-safe (performance측면에서 불리)
  • StringBuilder 속도 더 빠름

Compiler 기능 : String + 연산 → Compiler 에서 자동으로 StringBuffer 와 StringBuilder 로 바꾼다.

간단한것들 좋은 성능 내도록 내부적으로 변경

#추가적인 자바지식 : Garbage Collection 또는 Garbege Collector

자동으로 메모리를 관리해준다. (heavy , 예측100프로 불가. 정해진 로직 따름)

OutOfMemoryError 도 초래

c나 c++ 에서는 malloc, free 사용해서 메모리 관련포인터 역할

모든 객체의 생명주기 for GC

  1. Created(생성) → 2. In use or reahable(사용중) → 3. Invisible(사용중 , 접근 불가) → 4. Unreachable(사용 되지 않음 ) → 5. Collected(GC대상이 됨 ) → 6. Finalized(Finalize 를 거친 상태 ) → Deallocated( 메모리 해제 된 상태 )

설명 덧붙이기 :

  1. 생성 Created : 객체를 위한 메모리 공간 Heap 에 할당.

  2. In use or Reachable 사용중 : 객체 생성, 다른 객체에 의해 참조. Strongly referenced

  3. Invisible 사용중 but 접근 불가. Strongly referenced 되어 있지만 직접적 접근 불가. Invisible 객체가 항상 바로 Gc 대상이 되지는 않는다.

public void run() {

Object s = new Object();

s.dodo();

while(true) {


}

}

위의 코드에서 s는 run() 함수가 return 될 때까지 String referenced 를 가진다. 다시 접근할 수 없는 상태지만

while 문이 끝날때까지는 계속 메모리가 유지된다. 

→ memory leak 을 유발하 수 있고 null 로 만들어주는 것이 좋다. null 이면 String reference 가 해지되면서 Unreachable 상태로 되기 때문

4. Unreachable 사용되지 않음

Strong reference 가 존재하지 않을 경우이며 Unreachable 상태의 객체는 Gc의 후보가 된다. 바로 사라지지 않음. GC의 루트가 가지는 체인에 참조가 되는 형태. GC가 수행될 때 이 루트의 체인을 따라가며 메모리를 해제 

5. Collected GC 대상 

GC 가 해당 객체의 finalize() function 이 정의되어 있는지 판단 finalize 가 있다면 finalizer 라는 queue 에 넣고 없으면 바로 Finalized 상태로 전환

6. Finalized Finalize 를 거친 상태

Finalizer 에 의해 finalize 가 실행된 후의 상태 

해당객체가 언제 finalize 가 call되는지는 보장되지 않음

jvm 에 따라 수행시간도 다르다. '

객체에 finalize 가 구현되어 있다면 객체의 반환시간은 그만큼 더 늦어진다. finalize 를 위한 객체의 메모리도 증가. 재생 현상 발생할 수 있다. 

& Resurrection

finalize 는 한 객체에 한번만 실행되어야 한다. 여러번 실행→ 불안정한 상태가 됨 

7. Deallocated 메모리가 해제된 상태

메모리의 반환이 끝난 상태. 마무리 

#GC 를 강제로 부를 수도 있다. 권장되지 않는다. 절대 강제 호출하지 말길. System.gc() 나 Runtime.getRuntime().gc() 

메모리가 다 찼을 경우 ( 가상메모리 사용, OutofMemoryError) 

: 가상메모리 성능저하. Ram 과 HardDisk 사이의 memory swap 은 큰 운영

 Android 의 경우 메모리 확보를 위해 중요도가 낮은 process 를 강제로 죽여버림. 

GC 무거운 OPERATION 이다. 

문제 2) 로깅 문자열 결합을 피하기 위한 방법? 

내가 생각해봤던 문제 : LEVEL 수준을 다르게 하면 조금 상황이 나아지지 않을까? 

출처 : [<http://taewan.kim/post/log4j_perf/>](<http://taewan.kim/post/log4j_perf/>)

Log4j 를 debug 모드로 운영 중이기 때문에 문제가 발생했다 → warning 이나 error 모드로 운영하면 문제가 적어질까? → 실제적으로 Lof4j 가 실행되는 비용을 계산해보면 운영모드변경이 큰 영향이 되지 않는다. 

로깅 문자열 생성이나 로그 레벨 체크의 비용은 발생한다. 

로깅 문자열 생성비용이 매우 심각힐 수 있다. 

temporary object 를 양산. 

문제 해결 방법 

if(logger.isDebugEnabled() {

logger.debug("Entry nymber : " + i + " is " + String.valueOf(entry[i])); 

}

log4j 의 isDebugEnabled() 메서드 이용 → 로깅 문자열을 생성하기 전에 로깅 레벨을 점검하는 isDebugEnabled 메소드 실행 

단점 : logger 모드일땐 logger 실행 레벨 체크를 두번함 ( debug () 메소드로 다시 점검 ) 

→loop 문안에 있을때 늘 문제.. 

참고 : 

if (logger.isInfoEnabled()) {

logger.info("user input : " + dateFormatter.format(userInput)); 

}

문제 3) log level 설정에 따른 logger 메소드 동작여부?  

로그 레벨 : Trace> Debug > Info > Warn > Error 

로그level 의 상위 레벨 ( 오른쪽으로 가는) 의 로그들은 전부 찍히게 된다. 

log level 설정해준다

(log4j 예시) 

<?xml version="1.0" encoding="UTF-8"?> <Configuration> <Appenders> <Console name="console" target="SYSTEM_OUT"> <PatternLayout pattern="%d %5p [%c] %m%n" /> </Console> </Appenders> <Loggers> <Logger name="egovframework.ECALL_O.main.web" level="INFO" additivity="FALSE"> <AppenderRef ref="console" /> </Logger> </Loggers> </Configuration>

출처:

[<https://haenny.tistory.com/3>](<https://haenny.tistory.com/3>)

[Haenny]

<?xml version="1.0" encoding="UTF-8"?> <Configuration> <properties> <property name="name">n_mo</property> <property name="pattern">%d{yyyy-MM-dd HH:mm:ss.SSS} || %msg%n</property> <property name="path">c:/logs</property> </properties> <Appenders> <Console name="console" target="SYSTEM_OUT"> <PatternLayout pattern="${pattern}"/> </Console>

*<!-- 로그파일 남길시 사용 -->*

<RollingFile name="file" fileName="${path}/${name}.log" filePattern="${path}/${name}_%d{yyyyMMdd}.log"> <PatternLayout> <pattern>${pattern}</pattern> </PatternLayout> <Policies> <TimeBasedTriggeringPolicy/>

*<!-- Rotated everyday -->*

</Policies> </RollingFile> </Appenders> <Loggers> <Logger name="egovframework.ECALL_O.main.web" level="INFO" additivity="FALSE"> <AppenderRef ref="console" /> </Logger> </Loggers> </Configuration>

출처:

[<https://haenny.tistory.com/3>](<https://haenny.tistory.com/3>)






<!-- logger는 패키지 계층 구조로 얻어온다.
		LoggerFactory.getLogger(BasicServlet.class) 
		BasicServlet.class는 kr.or.ddit.servelet.basic 패키지에 속한다.
		패키지의 계층 구조와 일치하는 로거 이름이 있는지 찾아 해당 로거를 사용하게 된다. 
	ref = reference			
	원래 있던것 : 루트 로거 매칭되지 않을때 찍는다. 기본적으로 주석처리를 많이 한다. 
	패키지로 name 에 구분을 해서 작업을 한다. 
	나중에 spring framework 에서 관리하는 로거랑 구분해서 로그를 남길것이다. 
	error 로 level 을 바꾸면 (다른 것들로 변경하고 하위 level 을 로거로 사용하면 됨 
	필요할 때만 로그를 보고 싶을 때 사용할 수 있다.  
 	: trace, debug, info, warm, error(ex : catch , try catch 에서는 error 
레벨로 해주어서 경각심을 줌.!)
 	오른쪽으로 갈 수록 심각함. 

 	
 -->
[Haenny]

댓글