diff --git a/.github/resources/deploy.sh b/.github/resources/deploy.sh index 1f5badfd..ab427e1c 100644 --- a/.github/resources/deploy.sh +++ b/.github/resources/deploy.sh @@ -106,7 +106,6 @@ send_slack ">>> Nginx 트래픽 전환 ($TARGET_CONTAINER)..." echo "set \$service_url http://$TARGET_CONTAINER:8080;" > ./nginx/conf.d/service-url.inc IS_NGINX_RUNNING=$(docker ps | grep nginx) - if [ -z "$IS_NGINX_RUNNING" ]; then send_slack ">>> Nginx가 실행 중이지 않습니다. Nginx 시작..." docker compose up -d nginx @@ -133,7 +132,8 @@ if [ -n "$CURRENT_PROFILE" ]; then send_slack ">>> 🛑 구 버전 컨테이너 중지 완료: ${STOP_DURATION}초 소요 ($STOP_MSG)" fi -send_slack ">>> 사용하지 않는 Docker 이미지 정리(Prune)..." +send_slack ">>> 사용하지 않는 Docker 이미지 정리..." +docker images qasker/api --format "{{.Repository}}:{{.Tag}}" | grep -v ":latest" | xargs docker rmi docker image prune -f TOTAL_END_TIME=$(date +%s) diff --git a/.github/resources/docker-compose.yml b/.github/resources/docker-compose.yml index e05767eb..ca658772 100644 --- a/.github/resources/docker-compose.yml +++ b/.github/resources/docker-compose.yml @@ -21,4 +21,4 @@ services: environment: - SPRING_PROFILES_ACTIVE=prod,green ports: - - "${GREEN_PORT}:8080" + - "${GREEN_PORT}:8080" \ No newline at end of file diff --git a/.github/workflows/prod_deploy.yml b/.github/workflows/prod_deploy.yml index 807500c0..59ea13e8 100644 --- a/.github/workflows/prod_deploy.yml +++ b/.github/workflows/prod_deploy.yml @@ -2,7 +2,7 @@ name: Docker Hub Push & 배포 서버 EC2 배포 on: push: branches: - - main + - ICC-242-scouter workflow_dispatch: jobs: @@ -20,7 +20,6 @@ jobs: - name: 환경변수들 등록 run: | - echo "${{ env.NEWRELIC_YML }}" > app/newrelic/newrelic.yml echo "${{ env.APPLICATION_PROD_YML }}" > app/src/main/resources/application-prod.yml echo "${{ env.GRADLE_PROPERTIES }}" > app/gradle.properties @@ -34,7 +33,7 @@ jobs: uses: gradle/actions/setup-gradle@v4 - name: Jib로 Docker 이미지 빌드 및 푸시 - run: ./gradlew jib -PPROFILE=prod --build-cache + run: ./gradlew jib --build-cache deploy: name: EC2 배포 @@ -46,7 +45,6 @@ jobs: EC2_KEY: ${{ secrets.EC2_KEY }} BLUE_PORT: 8001 GREEN_PORT: 8002 - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} steps: - name: gradle.properties 값 환경 변수로 등록 및 마스킹 @@ -67,6 +65,10 @@ jobs: # DOCKER_CONTAINER_NAME 생성, 등록 DOCKER_CONTAINER_NAME_VALUE="${DOCKER_ID_VALUE}-${DOCKER_IMAGE_NAME_VALUE}" echo "DOCKER_CONTAINER_NAME=$DOCKER_CONTAINER_NAME_VALUE" >> $GITHUB_ENV + + # SLACK_WEBHOOK_URL 추출, 마스킹, 환경 변수 등록 + SLACK_WEBHOOK_URL_VALUE=$(echo "${{ secrets.GRADLE_PROPERTIES }}" | grep '^SLACK_WEBHOOK_URL=' | cut -d'=' -f2-) + echo "SLACK_WEBHOOK_URL=$SLACK_WEBHOOK_URL_VALUE" >> $GITHUB_ENV - name: 코드 체크아웃 uses: actions/checkout@v4 diff --git a/.gitignore b/.gitignore index 2e8973b1..fc7cd1c5 100644 --- a/.gitignore +++ b/.gitignore @@ -42,5 +42,5 @@ out/ .env app/gradle.properties app/newrelic/newrelic.yml -/heapdump +**/heapdump monitor_downtime.sh \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 84fe7760..2887f88c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -14,7 +14,6 @@ dependencies { implementation project(':aws:aws-impl') implementation project(':quiz:quiz-impl') - implementation "org.springframework.boot:spring-boot-starter-actuator" annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor' } @@ -37,23 +36,55 @@ jib { extraDirectories { paths { path { - setFrom(file("newrelic").toPath()) - into = "/app/newrelic" + setFrom(file("./scouter").toPath()) + into = "/app/scouter" } } } container { def jvmHeapSize = project.property("JVM_HEAP_SIZE") + // 기본 JVM 플래그 설정 jvmFlags = ["-Xms${jvmHeapSize}", "-Xmx${jvmHeapSize}"] - def newRelicConfig = project.file("newrelic/newrelic.yml") - def newRelicJar = project.file("newrelic/newrelic.jar") - if (newRelicConfig.exists() && newRelicJar.exists()) { - jvmFlags = jvmFlags + [ - "-Dnewrelic.config.file=/app/newrelic/newrelic.yml", - "-javaagent:/app/newrelic/newrelic.jar", - ] + def scouterJar = project.file("./scouter/agent.java/scouter.agent.jar") + + if (!scouterJar.exists()) { + throw new GradleException("Scouter agent file not found: ${scouterJar.absolutePath}") + } + + // 1. 필수값 검증 (값이 없으면 여기서 빌드 실패함 🛑) + def requiredProps = ["SCOUTER_IP", "SCOUTER_PORT", "SCOUTER_OBJ_NAME"] + requiredProps.each { prop -> + if (!project.hasProperty(prop)) { + throw new GradleException("❌ [빌드 실패] ${prop} 프로퍼티가 누락되었습니다. (-P${prop}=값 필요)") + } } + + // 2. 값 할당 (검증 통과했으므로 안전하게 가져옴) + def collectorIp = project.property("SCOUTER_IP") + def objName = project.property("SCOUTER_OBJ_NAME") + def collectorPort = project.property("SCOUTER_PORT") + + jvmFlags = jvmFlags + [ + // [필수] 에이전트 로드 + "-javaagent:/app/scouter/agent.java/scouter.agent.jar", + + // [필수] Java 16+ 대응을 위한 모듈 접근 허용 옵션 + "--add-opens=java.base/java.lang=ALL-UNNAMED", + "--add-opens=java.base/java.util=ALL-UNNAMED", + + // [필수] conf 파일 대신 직접 설정 주입 + "-Dnet_collector_ip=${collectorIp}", + "-Dnet_collector_udp_port=${collectorPort}", + "-Dnet_collector_tcp_port=${collectorPort}", + + // [보안 설정: 권장] 클라이언트에서 제어 기능(HeapDump, Method Patch 등) 비활성화 + "-Denable_mgr_agent=false", + + // [권장] 어플리케이션 이름 + "-Dobj_name=${objName}" + + ] } } \ No newline at end of file diff --git a/app/newrelic/newrelic.jar b/app/newrelic/newrelic.jar deleted file mode 100644 index 4632fad4..00000000 Binary files a/app/newrelic/newrelic.jar and /dev/null differ diff --git a/app/scouter/agent.java/conf/scouter.conf b/app/scouter/agent.java/conf/scouter.conf new file mode 100644 index 00000000..27aec279 --- /dev/null +++ b/app/scouter/agent.java/conf/scouter.conf @@ -0,0 +1,12 @@ +### scouter java agent configuration sample +#obj_name=WAS-01 +#net_collector_ip=127.0.0.1 +#net_collector_udp_port=6100 +#net_collector_tcp_port=6100 +#hook_method_patterns=sample.mybiz.*Biz.*,sample.service.*Service.* +#trace_http_client_ip_header_key=X-Forwarded-For +#profile_spring_controller_method_parameter_enabled=false +#hook_exception_class_patterns=my.exception.TypedException +#profile_fullstack_hooked_exception_enabled=true +#hook_exception_handler_method_patterns=my.AbstractAPIController.fallbackHandler,my.ApiExceptionLoggingFilter.handleNotFoundErrorResponse +#hook_exception_hanlder_exclude_class_patterns=exception.BizException diff --git a/app/scouter/agent.java/conf/testcase-scouter.conf b/app/scouter/agent.java/conf/testcase-scouter.conf new file mode 100644 index 00000000..5f168d14 --- /dev/null +++ b/app/scouter/agent.java/conf/testcase-scouter.conf @@ -0,0 +1 @@ +objName=test-case-scouter diff --git a/app/scouter/agent.java/plugin/capture.plug b/app/scouter/agent.java/plugin/capture.plug new file mode 100644 index 00000000..9a6cf848 --- /dev/null +++ b/app/scouter/agent.java/plugin/capture.plug @@ -0,0 +1,9 @@ +[args] +// void capArgs(WrContext $ctx, HookArgs $hook) + +[return] +// void capReturn(WrContext $ctx, HookReturn $hook) + + +[this] +// void capThis(WrContext $ctx, String $class, String $desc, Object $this) diff --git a/app/scouter/agent.java/plugin/counter.plug b/app/scouter/agent.java/plugin/counter.plug new file mode 100644 index 00000000..b77b4aff --- /dev/null +++ b/app/scouter/agent.java/plugin/counter.plug @@ -0,0 +1,2 @@ +[counter] +//public void counter(scouter.lang.pack.PerfCounterPack $pack) diff --git a/app/scouter/agent.java/plugin/httpcall.plug b/app/scouter/agent.java/plugin/httpcall.plug new file mode 100644 index 00000000..3215bb34 --- /dev/null +++ b/app/scouter/agent.java/plugin/httpcall.plug @@ -0,0 +1,2 @@ +[call] +// void call(WrContext $ctx, WrHttpCallRequest $req) diff --git a/app/scouter/agent.java/plugin/httpservice.plug b/app/scouter/agent.java/plugin/httpservice.plug new file mode 100644 index 00000000..739ba8e1 --- /dev/null +++ b/app/scouter/agent.java/plugin/httpservice.plug @@ -0,0 +1,12 @@ +[start] +// void start(WrContext $ctx, WrRequest $req, WrResponse $res) + + + +[end] +// void end(WrContext $ctx, WrRequest $req, WrResponse $res) + + +[reject] +// boolean reject(WrContext $ctx, WrRequest $req, WrResponse $res) +return false; \ No newline at end of file diff --git a/app/scouter/agent.java/plugin/jdbcpool.plug b/app/scouter/agent.java/plugin/jdbcpool.plug new file mode 100644 index 00000000..91641720 --- /dev/null +++ b/app/scouter/agent.java/plugin/jdbcpool.plug @@ -0,0 +1,4 @@ +[url] +// String url(WrContext $ctx, String $msg, Object $pool) + +return null; \ No newline at end of file diff --git a/app/scouter/agent.java/plugin/readme.md b/app/scouter/agent.java/plugin/readme.md new file mode 100644 index 00000000..6c3b2ee3 --- /dev/null +++ b/app/scouter/agent.java/plugin/readme.md @@ -0,0 +1,138 @@ +## Javaagent Plugin + - Default File Location : ${directory of scouter.agent.jar}/plugin + - Dynamic application + - By java code + - Plugin 종류 + - Http-service + - Service + - HttpCall + - Capture + - JDBC-Pool + +### Http-service Plugin(httpservice.plug) + +1. void start(WrContext $ctx, WrRequest $req, WrResponse $res) : Http Service 시작 시점 +2. void end(WrContext $ctx, WrRequest $req, WrResponse $res) : Http Service 종료 시점 +3. boolean reject(WrContext $ctx, WrRequest $req, WrResponse $res) : Http Service 시작 시점에 reject 조건 (default : false) + +### Service Plugin(service.plug) + **추가적인 hooking 설정을 통해서만 동작** + +1. void start(WrContext $ctx, HookArgs $hook) : Service 시작 시점 +2. void end(WrContext $ctx) : Service 종료 시점 + +### HttpCall Plugin(httpcall.plug) + +1. void call(WrContext $ctx, WrHttpCallRequest $req) : Http Call 요청 시점 + +### Capture Plugin(capture.plug) + **추가적인 hooking 설정을 통해서만 동작** + +1. void capArgs(WrContext $ctx, HookArgs $hook) : Method 시작 시점 +2. void capReturn(WrContext $ctx, HookReturn $hook) : Method Return 시점 +3. void capThis(WrContext $ctx, String $class, String $desc, Object $this) : Constructor 생성 시점 + +### JDBC-Pool Plugin(jdbcpool.plug) + +1. String url(WrContext $ctx, String $msg, Object $pool) + : DB Connection URL 요청 시점 + + +## API + +### Common API + - void log(Object c) : Logger를 통한 log + - void println(Object c) : System.out를 통한 log + - Object field(Object o, String field) : Object의 filed 값을 가져옴 + - Object method(Object o, String method) : Object의 method를 강제invoke 함 + - Object method1(Object o, String method) : Object의 method를 invoke 함 + - Object method(Object o, String method, String param) : Object의 method를 String 파라미터와 함께 invoke 함 + - String toString(Object o) : Object 를 toString 하여 반환 + - String toString(Object o, String def) : Object 를 toString 하여 반환, null 이면 default string 반환 + - void alert(char level, String title, String message) : Alert 을 보냄 + - int syshash(Object o) : Object 의 identityHash 값 반환 + - int syshash(HookArgs hook, int x) : Arguments의 i 인덱스의 identyHash 값 반환 + - int syshash(HookArgs hook) : This 의 identyHash 값 반환 + - void forward(WrContext wctx, int uuid) : Async Thread 를 App service로 연결 + - void forwardThread(WrContext wctx, int uuid) : Async Thread 를 Background service로 연결 + - void receive(WrContext ctx, int uuid) : 앞서 등록된 Service가 있으면 연결 + + +### WrContext class API + - String service() : Service Name 을 반환 + - void service(String name) : Service Name 을 set + - int serviceHash() : Service Hash 값을 반환 + - void remoteIp(String ip) : Remote IP 을 set + - String remoteIp() : Remote IP를 반환 + - void error(String err) : 임의의 error 를 주입 + - boolean isError() : 에러 체크 + - void group(String group) : 임의의 group을 set + - String group() : Group을 반환 + - void login(String id) : 임의의 사용자 ID 를 set + - String login() : 사용자 ID를 반환 + - void desc(String desc) : 임의의 Desc를 set + - String desc() : Desc를 반환 + - String httpMethod() : Http Method를 반환 + - String httpQuery() : Http Query를 반환 + - String httpContentType() : Http Content-type을 반환 + - String userAgent() : User-Agent를 반환 + - void profile(String msg) : Msg 를 profile에 기록 + - long txid() : txid 를 반환 + - long gxid() : gxid 를 반환 + - TraceContext inner() : context를 반환 + +### WrRequest class API + - String getCookie(String key) : Cookie 값을 반환 + - String getRequestURI() : Request URI를 반환 + - String getRemoteAddr() : Remote Address를 반환 + - String getMethod() : Method 를 반환 + - String getQueryString() : Query String을 반환 + - String getParameter(String key) : Parameter를 반환 + - Object getAttribute(String key) : Attribute를 반환 + - String getHeader(String key) : Header값을 반환 + - Enumeration getParameterNames() : Parameter 값들을 반환 + - Enumeration getHeaderNames() : HeaderName들을 반환 + - WrSession getSession() : WrSession객체를 반환 + - Set getSessionNames() : Session Name들을 반환 + - Object getSessionAttribute(String key) : Session 값을 반환 + - Object inner() : Request Object를 반환 + - boolean isOk() : Plugin 상태 확인 + - Throwable error() : Error 확인 + +### WrResponse class API + - PrintWriter getWriter() : Writer를 반환 + - String getContentType() : Content-type을 반환 + - String getCharacterEncoding() : Character-encoding을 반환 + - Object inner() : Response Object를 반환 + - boolean isOk() : Plugin 상태 확인 + - Throwable error() : Error 확인 + +### WrSession class API + - getAttribute(String key) : Attribute를 반환 + - Enumeration getAttributeNames() : Attribute Names를 반환 + - Object inner() : Session Object를 반환 + - boolean isOk() : Plugin 상태 확인 + - Throwable error() : Error 확인 + +### WrHttpCallRequest class API + - void header(Object key, Object value) : Header값 추가 + - Object inner() : Request Object를 반환 + - boolean isOk() : Plugin 상태 확인 + - Throwable error() : Error 확인 + +### HookArgs class API + - String getClassName() : Class 이름 반환 + - String getMethodName() : Method 이름 반환 + - String getMethodDesc() : Method 의 Desc 반환 + - Object getThis() : this object 반환 + - Object[] getArgs() : Arguments 반환 + - int getArgCount() : Argument 갯수 반환 + +### HookReturn class API + - String getClassName() : Class 이름 반환 + - String getMethodName() : Method 이름 반환 + - String getMethodDesc() : Method 의 Desc 반환 + - Object getThis() : this object 반환 + - Object getReturn() : Return 값 반환 + + diff --git a/app/scouter/agent.java/plugin/readme_kr.md b/app/scouter/agent.java/plugin/readme_kr.md new file mode 100644 index 00000000..e7bed28d --- /dev/null +++ b/app/scouter/agent.java/plugin/readme_kr.md @@ -0,0 +1,138 @@ +## Javaagent Plugin + - Default File Location : ${${directory of scouter.agent.jar}/plugin}/plugin + - Dynamic application + - By java code + - Plugin 종류 + - Http-service + - Service + - HttpCall + - Capture + - JDBC-Pool + +### Http-service Plugin(httpservice.plug) + +1. void start(WrContext $ctx, WrRequest $req, WrResponse $res) : Http Service 시작 시점 +2. void end(WrContext $ctx, WrRequest $req, WrResponse $res) : Http Service 종료 시점 +3. boolean reject(WrContext $ctx, WrRequest $req, WrResponse $res) : Http Service 시작 시점에 reject 조건 (default : false) + +### Service Plugin(service.plug) + **추가적인 hooking 설정을 통해서만 동작** + +1. void start(WrContext $ctx, HookArgs $hook) : Service 시작 시점 +2. void end(WrContext $ctx) : Service 종료 시점 + +### HttpCall Plugin(httpcall.plug) + +1. void call(WrContext $ctx, WrHttpCallRequest $req) : Http Call 요청 시점 + +### Capture Plugin(capture.plug) + **추가적인 hooking 설정을 통해서만 동작** + +1. void capArgs(WrContext $ctx, HookArgs $hook) : Method 시작 시점 +2. void capReturn(WrContext $ctx, HookReturn $hook) : Method Return 시점 +3. void capThis(WrContext $ctx, String $class, String $desc, Object $this) : Constructor 생성 시점 + +### JDBC-Pool Plugin(jdbcpool.plug) + +1. String url(WrContext $ctx, String $msg, Object $pool) + : DB Connection URL 요청 시점 + + +## API + +### Common API + - void log(Object c) : Logger를 통한 log + - void println(Object c) : System.out를 통한 log + - Object field(Object o, String field) : Object의 filed 값을 가져옴 + - Object method(Object o, String method) : Object의 method를 강제invoke 함 + - Object method1(Object o, String method) : Object의 method를 invoke 함 + - Object method(Object o, String method, String param) : Object의 method를 String 파라미터와 함께 invoke 함 + - String toString(Object o) : Object 를 toString 하여 반환 + - String toString(Object o, String def) : Object 를 toString 하여 반환, null 이면 default string 반환 + - void alert(char level, String title, String message) : Alert 을 보냄 + - int syshash(Object o) : Object 의 identityHash 값 반환 + - int syshash(HookArgs hook, int x) : Arguments의 i 인덱스의 identyHash 값 반환 + - int syshash(HookArgs hook) : This 의 identyHash 값 반환 + - void forward(WrContext wctx, int uuid) : Async Thread 를 App service로 연결 + - void forwardThread(WrContext wctx, int uuid) : Async Thread 를 Background service로 연결 + - void receive(WrContext ctx, int uuid) : 앞서 등록된 Service가 있으면 연결 + + +### WrContext class API + - String service() : Service Name 을 반환 + - void service(String name) : Service Name 을 set + - int serviceHash() : Service Hash 값을 반환 + - void remoteIp(String ip) : Remote IP 을 set + - String remoteIp() : Remote IP를 반환 + - void error(String err) : 임의의 error 를 주입 + - boolean isError() : 에러 체크 + - void group(String group) : 임의의 group을 set + - String group() : Group을 반환 + - void login(String id) : 임의의 사용자 ID 를 set + - String login() : 사용자 ID를 반환 + - void desc(String desc) : 임의의 Desc를 set + - String desc() : Desc를 반환 + - String httpMethod() : Http Method를 반환 + - String httpQuery() : Http Query를 반환 + - String httpContentType() : Http Content-type을 반환 + - String userAgent() : User-Agent를 반환 + - void profile(String msg) : Msg 를 profile에 기록 + - long txid() : txid 를 반환 + - long gxid() : gxid 를 반환 + - TraceContext inner() : context를 반환 + +### WrRequest class API + - String getCookie(String key) : Cookie 값을 반환 + - String getRequestURI() : Request URI를 반환 + - String getRemoteAddr() : Remote Address를 반환 + - String getMethod() : Method 를 반환 + - String getQueryString() : Query String을 반환 + - String getParameter(String key) : Parameter를 반환 + - Object getAttribute(String key) : Attribute를 반환 + - String getHeader(String key) : Header값을 반환 + - Enumeration getParameterNames() : Parameter 값들을 반환 + - Enumeration getHeaderNames() : HeaderName들을 반환 + - WrSession getSession() : WrSession객체를 반환 + - Set getSessionNames() : Session Name들을 반환 + - Object getSessionAttribute(String key) : Session 값을 반환 + - Object inner() : Request Object를 반환 + - boolean isOk() : Plugin 상태 확인 + - Throwable error() : Error 확인 + +### WrResponse class API + - PrintWriter getWriter() : Writer를 반환 + - String getContentType() : Content-type을 반환 + - String getCharacterEncoding() : Character-encoding을 반환 + - Object inner() : Response Object를 반환 + - boolean isOk() : Plugin 상태 확인 + - Throwable error() : Error 확인 + +### WrSession class API + - getAttribute(String key) : Attribute를 반환 + - Enumeration getAttributeNames() : Attribute Names를 반환 + - Object inner() : Session Object를 반환 + - boolean isOk() : Plugin 상태 확인 + - Throwable error() : Error 확인 + +### WrHttpCallRequest class API + - void header(Object key, Object value) : Header값 추가 + - Object inner() : Request Object를 반환 + - boolean isOk() : Plugin 상태 확인 + - Throwable error() : Error 확인 + +### HookArgs class API + - String getClassName() : Class 이름 반환 + - String getMethodName() : Method 이름 반환 + - String getMethodDesc() : Method 의 Desc 반환 + - Object getThis() : this object 반환 + - Object[] getArgs() : Arguments 반환 + - int getArgCount() : Argument 갯수 반환 + +### HookReturn class API + - String getClassName() : Class 이름 반환 + - String getMethodName() : Method 이름 반환 + - String getMethodDesc() : Method 의 Desc 반환 + - Object getThis() : this object 반환 + - Object getReturn() : Return 값 반환 + + diff --git a/app/scouter/agent.java/plugin/service.plug b/app/scouter/agent.java/plugin/service.plug new file mode 100644 index 00000000..8b30426c --- /dev/null +++ b/app/scouter/agent.java/plugin/service.plug @@ -0,0 +1,6 @@ +[start] +// void start(WrContext $ctx, HookArgs $hook) + + +[end] +// void end(WrContext $ctx) diff --git a/app/scouter/agent.java/plugin/springControllerCapture.plug b/app/scouter/agent.java/plugin/springControllerCapture.plug new file mode 100644 index 00000000..f11e5b32 --- /dev/null +++ b/app/scouter/agent.java/plugin/springControllerCapture.plug @@ -0,0 +1,11 @@ +[args] +// void capArgs(WrContext $ctx, HookArgs $hook) + + + +[return] +// unused + + +[this] +// unused diff --git a/app/scouter/agent.java/scouter-agent-java-2.21.2.jar b/app/scouter/agent.java/scouter-agent-java-2.21.2.jar new file mode 100644 index 00000000..71fd072a Binary files /dev/null and b/app/scouter/agent.java/scouter-agent-java-2.21.2.jar differ diff --git a/app/scouter/agent.java/scouter.agent.jar b/app/scouter/agent.java/scouter.agent.jar new file mode 100644 index 00000000..685d3c68 Binary files /dev/null and b/app/scouter/agent.java/scouter.agent.jar differ diff --git a/build.gradle b/build.gradle index 5a2bcd39..dcd0b523 100644 --- a/build.gradle +++ b/build.gradle @@ -38,11 +38,6 @@ subprojects { testImplementation "org.springframework.boot:spring-boot-starter-test" testRuntimeOnly "org.junit.platform:junit-platform-launcher" - // ──────────────────────────────── - // APM - // ──────────────────────────────── - implementation "com.newrelic.agent.java:newrelic-api:8.11.0" - // ──────────────────────────────── // 스프링부트 web과 JPA // ──────────────────────────────── diff --git a/docker-compose.yml b/docker-compose.yml index ca5db939..6a099280 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -15,17 +15,22 @@ services: volumes: - q-asker-db:/var/lib/mysql - q-asker-redis: - image: redis:latest - container_name: q-asker-redis + scouter-server: + image: scouterapm/scouter-server:2.17.1 + container_name: scouter-server restart: no ports: - - "6379:6379" + - "6100:6100/tcp" + - "6100:6100/udp" + environment: + - JAVA_OPTS=-Xmx512m volumes: - - q-asker-redis:/data - command: > - redis-server --requirepass ${REDIS_PASSWORD} + - scouter-server-data:/home/scouter-server/database + deploy: + resources: + limits: + memory: 1024m volumes: q-asker-db: - q-asker-redis: \ No newline at end of file + scouter-server-data: \ No newline at end of file diff --git a/modules/global/src/main/java/com/icc/qasker/global/error/GlobalExceptionHandler.java b/modules/global/src/main/java/com/icc/qasker/global/error/GlobalExceptionHandler.java index 7a187e85..d2545bbb 100644 --- a/modules/global/src/main/java/com/icc/qasker/global/error/GlobalExceptionHandler.java +++ b/modules/global/src/main/java/com/icc/qasker/global/error/GlobalExceptionHandler.java @@ -6,11 +6,17 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.servlet.resource.NoResourceFoundException; @Slf4j @ControllerAdvice public class GlobalExceptionHandler { + @ExceptionHandler(NoResourceFoundException.class) + public ResponseEntity handleNoResourceFoundException(NoResourceFoundException e) { + return ResponseEntity.notFound().build(); + } + @ExceptionHandler(CustomException.class) public ResponseEntity handleCustomException( CustomException customException) {