메모. 개인적으로 사용하는 logback file appender 설정

 

  • 서비스를 운영시 파일로 log를 남길때는 아래 사항을 꼭 고려해야함
    • 로그 파일 용량이 너무 커서 vi로 오픈이 힘든걸 막아야함
    • 로그파일을 삭제 없이 계속 저장하면 disk full로 서비스 장애가 발생하니 주의
    • 적당한 보관기간으로 압축해서 롤링

logback.properties 설정

#로그파일 저장 디렉토리(유저/logs 디렉토리 하위에 서비스코드별로 디렉토리 구분)
log.file.dir=/home/svc_user/logs/svc_cd

#로그 파일 명(prefix에 환경코드 추가, 1개 서버에 blue/green 배포하는 경우를 위해서 was port별로 파일명 구분)
log.file.name=real_svc_cd_${server.port}.log

log.file.full=${log.file.dir}/${log.file.name}

#백업
log.file.backup.fileNamePattern=${log.file.dir}/backup/${log.file.name}_%d{yyyy-MM-dd}_%i.zip

#로그 패턴
log.pattern=%d{yyyy-MM-dd HH:mm:ss} [${HOSTNAME}] [%-5level] %logger{35}:%L - %msg%n

 

logback-spring.xml 설정(참고 링크)

 

  • 로그파일 1개는 20M로 유지
  • 최대 50의 로그파일을 유지
  • 최대 1000MB
  • 압축해서 롤링
 <!-- 외부 설정파일을 사용 -->
    <property resource="properties/logback.properties"/>

    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <File>${log.file.full}</File>
        <encoder>
            <pattern>${log.pattern}</pattern>
        </encoder>
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>${log.file.backup.fileNamePattern}</fileNamePattern>
            <maxFileSize>20MB</maxFileSize>
            <maxHistory>50</maxHistory>
            <totalSizeCap>1000MB</totalSizeCap>
        </rollingPolicy>
    </appender>

 

참고

 - site map 설명 링크

 

 

코드

 - 참고 링크

@RestController
public class TestController {

    @Autowired
    private RequestMappingHandlerMapping re;

    @GetMapping("/sitemap.xml")
    public String getSitemap() {
        Map<RequestMappingInfo, HandlerMethod> handlerMethods = re.getHandlerMethods();
        List<String> urls = new ArrayList<>();
        for (Entry<RequestMappingInfo, HandlerMethod> entry : handlerMethods.entrySet()) {
            urls.addAll((entry.getKey().getPatternsCondition().getPatterns()));
        }
        // Construct XML response from urls and return it
    }

}

spring webflux의 websocket관련 소스 분석

 

 

 

  1. WebSocketSession

    1. 인터페이스

    2. 용도/목적

  2. WebSocketHandler

 

 

netty 스레드 확인

 

 

  1. 구성 후 서버(netty)를 시작 후 thread를 확인해보면 reactor-http-nio-x 스레드를 확인할 수 있음

    1. netty 기본 설정에 의해 스레드 갯수는 CPU코어 갯수만큼 생김

    2. 아래 그림은 JMC(Oracle Java Mission Control)로 확인한 내용

       

 

 

  1. 아래 그림은 로컬PC 의 CPU 코어 갯수

 

 

 

webflux 기반으로 고성능 어플리케이션 작업할게 있는데 model mapper관련 성능 메모

 - 참고로, 기본 리플렉션을 이용한 맵퍼는 CPU를 많이 사용함

 - https://www.baeldung.com/java-performance-mapping-frameworks를 참고해서 선정 필요

메모 목적의 글입니다.

19년 9월 기준으로 기존에 자주 사용하던 apache http 컴포넌트에서는 http 2를 정식으로 지원하지 않는 것 같음
현재 개발 기술 스택인 java+spring 환경에서, 애플 APNS와 통신시 http 2로 통신해야해서 통신 모듈을 변경함(java도 가능하면 11로 올리세요. 8에서는 문제 있음)


  1. okhttp 디펜더시 추가
        <dependency>
               <groupId>com.squareup.okhttp3</groupId>
               <artifactId>okhttp</artifactId>
               <version>4.1.0</version>
          </dependency>

  1. Spring restTemplate에 셋팅
     
    private RestTemplate restTemplate = new RestTemplate();
     
     @PostConstruct //init 시점 및 방법은 선호하는 방법에 따라서 셋팅
     private void init() {
          // @formatter:off
          OkHttpClient client = new OkHttpClient.Builder()
               .readTimeout(5, TimeUnit.SECONDS)
               .connectTimeout(7, TimeUnit.SECONDS)
               .connectionPool(new ConnectionPool(30, 10, TimeUnit.MINUTES)) //커넥션풀 적용
               .build();
          // @formatter:on
          OkHttp3ClientHttpRequestFactory crf = new OkHttp3ClientHttpRequestFactory(client);
          restTemplate.setRequestFactory(crf);
     }


  1. 사용하는 소스 예(일부분)

               HttpHeaders headers = new HttpHeaders();
               headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
               //ex) Bearer test.test.test
               headers.set("Authorization", authorization);
               headers.set("apns-id", apnsId); //A canonical UUID that identifies the notification. example UUID is as follows:123e4567-e89b-12d3-a456-42665544000
              
             //If the value is 0, APNs treats the notification as if it expires immediately and does not store the notification or attempt to redeliver it.
               headers.set("apns-expiration", "0");
               headers.set("apns-priority", "10"); //The priority of the notification. 10은 즉시, 5는 배터리상태 고려해서 발송
               headers.set("apns-topic", apnsTopic);
               log.debug("IOS APNS푸시 요청headers\n{}", headers);
              
             Map<String, Object> bodyMap = new HashMap<String, Object>();
               bodyMap.put("aps", aPNSPayloadAPS);
               String bodyJson = OM.writeValueAsString(bodyMap);
               log.debug("IOS APNS푸시 요청bodyJson\n{}", bodyJson);
               
            //http 200 응답이면 API요청 성공
               ResponseEntity<String> rslt = restTemplate.exchange(apiURL, HttpMethod.POST, new HttpEntity<String>(bodyJson, headers), String.class);
               log.debug("IOS APNS 푸시발송 요청 리턴 결과\n{}", rslt);

  • 주의
    • 해당 설정은 개인 프로젝트의 디렉토리 경로 등에 디펜더시되어 있으며, 서비스 환경에서 로그 출력시 성능이 저하될 수 있으니 개발 환경에서 디버깅 용으로만 사용

  1. maven 라이브러리 추가
        <!-- sql로그를 남기기 위한 라이브러리 -->
        <dependency>
               <groupId>org.bgee.log4jdbc-log4j2</groupId>
                <artifactId>log4jdbc-log4j2-jdbc4.1</artifactId>
               <version>1.16</version>
          </dependency>

  1. jdbc driverClassName을 "net.sf.log4jdbc.sql.jdbcapi.DriverSpy"로 변경
  2. resources 디렉토리에 "log4jdbc.log4j2.properties" 파일 추가 후 아래 내용 작성
log4jdbc.spylogdelegator.name =  net.sf.log4jdbc.log.slf4j.Slf4jSpyLogDelegator
#sql문을 최대 몇 라인까지 출력할지 여부: 0은 제한 없음
log4jdbc.dump.sql.maxlinelength=0

  1. logback  설정에 출력 및 미출력할 로그 어펜더 정의(샘플)
<logger name="jdbc" level="OFF"/>
    <logger name="jdbc.audit" level="OFF"/>
    <logger name="jdbc.connection" level="OFF"/>
    
    <!-- SQL문과 해당 SQL을 실행시키는데 수행된 시간  정보(milliseconds)를 포함한다. -->  
    <logger name="jdbc.sqltiming" additivity="false">
        <level value="DEBUG" />
        <appender-ref ref="CONSOLE" />
    </logger>
    
      <!-- SQL 결과 조회된 데이터의 table을 로그로 남긴다 -->
     <logger name="jdbc.resultsettable" additivity="false">
        <level value="DEBUG" />
        <appender-ref ref="CONSOLE" />
    </logger>
    
        <!-- SQL문만을 로그로 남기며, PreparedStatement일 경우  관련된 argument 값으로 대체된 SQL문이 보여진다 -->
<!--      <logger name="jdbc.sqlonly" additivity="false">  -->
<!--         <level value="DEBUG" /> -->
<!--         <appender-ref ref="CONSOLE" /> -->
<!--     </logger> -->
    <!-- ResultSet을 포함한 모든 JDBC 호출 정보를 로그로 남기므로  매우 방대한 양의 로그가 생성된다 -->
<!--      <logger name="jdbc.resultset" additivity="false">  -->
<!--         <level value="DEBUG" /> -->
<!--         <appender-ref ref="CONSOLE" /> -->
<!--     </logger> -->



Spring API 개발시 예외(에러) 처리 방법을 메모합니다. 실제로 회사 등의 프로젝트에서 제가 선호하는 방식입니다.

 -  특정 케이스, 레거시의 하위호환성을 유지해야하는 경우는 어쩔수 없지만ㅠ (ex. 갑에서 에러도 200으로 응답해달라고 요청)


[참고 링크]

1. 트위터 : https://developer.twitter.com/en/docs/basics/response-codes

2. 카카오 : https://developers.kakao.com/docs/restapi/quick-reference#응답-코드

3. 기타

  - 어떤분이 내가 사용하는 방법과 유사하게 Spring 기반으로 정리해두신 링크입니다.

  - 다른점은, 저는 다른 개발자와 커뮤니케이션& 개발자가 정의서를 따로 보지 않도록 하기 위해서 에러코드를 숫자보다 문자로 처리하는걸 선호합니다.

  - https://cheese10yun.github.io/spring-guide-exception/#undefined



+ Recent posts