Java Agent

2023. 5. 17. 14:01Spring Framework/Spring AOP

Java Agent는 Java 애플리케이션의 실행 중 동작을 변경하거나 모니터링할 수 있는 특별한 프로그램입니다. Java Agent는 JVM(Java Virtual Machine)과 상호 작용하여 클래스 로딩 시점에 바이트코드를 조작하거나, 이미 로드된 클래스의 동작을 런타임에 수정할 수 있습니다. 이러한 기능은 성능 모니터링, 디버깅, 프로파일링, 코드 커버리지 분석, 그리고 AOP(Aspect-Oriented Programming) 구현 등 다양한 목적에 사용됩니다.

Java Agent의 주요 특징

  1. Premain 메서드:
    • Java Agent는 JVM이 시작될 때 premain 메서드를 통해 실행됩니다. 이 메서드는 JVM이 메인 애플리케이션을 실행하기 전에 호출됩니다.
    • premain 메서드는 두 가지 형태로 정의될 수 있습니다:
      • public static void premain(String agentArgs, Instrumentation inst)
      • public static void premain(String agentArgs)
  2. Instrumentation API 사용:
    • Java Agent는 Instrumentation API를 통해 클래스 로딩 과정에 개입할 수 있습니다. 이 API는 바이트코드를 변환하거나, 재정의할 수 있는 메서드들을 제공하며, 이를 통해 Java Agent는 런타임에 애플리케이션의 동작을 변경할 수 있습니다.
    • 주요 메서드로는 addTransformer(클래스 로딩 시 바이트코드 변환), redefineClasses(이미 로드된 클래스 재정의) 등이 있습니다.
  3. Java Agent 등록 및 실행:
    • Java Agent는 JVM 시작 시 -javaagent 옵션을 통해 등록됩니다. 예를 들어:
    • java -javaagent:/path/to/agent.jar -jar myapp.jar
    • 이 옵션은 JVM이 시작될 때 지정된 JAR 파일을 Java Agent로 인식하고, 해당 에이전트의 premain 메서드를 호출합니다.
  4. JAR 파일 구조:
    • Java Agent는 JAR 파일 형태로 패키징되며, META-INF/MANIFEST.MF 파일에 Premain-Class라는 속성이 포함되어야 합니다. 이 속성은 JVM에 어떤 클래스의 premain 메서드를 호출할지를 지정합니다.
    • 예:
    • Premain-Class: com.example.MyAgent

Java Agent의 일반적인 사용 사례

  1. 성능 모니터링:
    • 애플리케이션의 실행 중 성능 관련 데이터를 수집하기 위해 Java Agent를 사용하여 메서드 실행 시간, 메모리 사용량 등을 추적할 수 있습니다.
  2. 프로파일링 도구:
    • Java Agent는 애플리케이션의 실행을 프로파일링하는 도구로 사용되어, 실행 중인 애플리케이션의 메모리 사용, 스레드 상태, CPU 사용 등을 분석할 수 있습니다.
  3. 코드 커버리지 분석:
    • Java Agent는 테스트 실행 중 어떤 코드가 실행되었는지를 추적하여 코드 커버리지 정보를 수집하고, 이를 바탕으로 커버리지 보고서를 생성할 수 있습니다.
  4. AOP(Aspect-Oriented Programming):
    • AspectJ와 같은 AOP 프레임워크에서 Java Agent를 사용하여 런타임에 클래스 파일을 변환하고, 특정 메서드 호출 전후에 코드를 삽입하는 등의 기능을 구현할 수 있습니다.
  5. 디버깅 및 로깅:
    • Java Agent를 사용하여 애플리케이션의 실행 중 특정 이벤트나 상태를 실시간으로 로깅하거나, 디버깅 정보를 수집할 수 있습니다.

Java Agent 예시 코드

다음은 간단한 Java Agent의 예시입니다. 이 에이전트는 모든 클래스가 로드될 때마다 그 클래스 이름을 출력합니다.

import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;

public class SimpleAgent {
    public static void premain(String agentArgs, Instrumentation inst) {
        inst.addTransformer(new SimpleTransformer());
    }
}

class SimpleTransformer implements ClassFileTransformer {
    @Override
    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, 
                            ProtectionDomain protectionDomain, byte[] classfileBuffer) {
        System.out.println("Transforming class: " + className);
        // 바이트코드를 수정하지 않는 경우 원본 바이트코드를 반환
        return classfileBuffer;
    }
}

Java Agent는 JVM의 클래스 로딩 과정에 개입하여 애플리케이션의 동작을 실시간으로 조작할 수 있는 도구입니다. 주로 Instrumentation API를 사용하여 바이트코드 수준에서의 수정 작업을 수행하며, 성능 모니터링, 프로파일링, 코드 커버리지 분석, AOP 구현 등 다양한 용도로 사용됩니다. Java Agent는 JVM 시작 시 -javaagent 옵션을 통해 로드되며, 애플리케이션의 실행 전반에 걸쳐 중요한 역할을 할 수 있습니다.

Java Agents

대표적인 Java Agent들은 다양한 목적에 맞춰 개발된 도구들로, 주로 성능 모니터링, 프로파일링, 코드 커버리지 분석, AOP 구현 등을 위해 사용됩니다. 아래는 널리 사용되는 몇 가지 대표적인 Java Agent입니다:

1. Java Flight Recorder (JFR)

  • 용도: 성능 모니터링 및 프로파일링
  • 설명: JFR은 JDK에 내장된 성능 모니터링 도구로, Java 애플리케이션의 실행 중 발생하는 다양한 이벤트를 기록하고 분석하는 데 사용됩니다. JFR은 매우 낮은 오버헤드로 성능 데이터를 수집할 수 있어, 프로덕션 환경에서 성능 문제를 분석하는 데 매우 유용합니다.
  • 특징: JFR은 JDK에 내장되어 있으며, 별도의 설치가 필요하지 않고, -XX:StartFlightRecording 옵션으로 활성화할 수 있습니다.

2. JaCoCo (Java Code Coverage Library)

  • 용도: 코드 커버리지 분석
  • 설명: JaCoCo는 Java 애플리케이션의 테스트 커버리지를 측정하기 위한 도구입니다. JaCoCo의 Java Agent는 테스트 실행 중 어떤 코드가 실행되었는지를 추적하여, 코드 커버리지 보고서를 생성합니다.
  • 특징: Maven, Gradle 등 빌드 도구와 쉽게 통합되며, CI/CD 파이프라인에서 코드 커버리지를 자동으로 측정하고 보고할 수 있습니다.

3. New Relic

  • 용도: 애플리케이션 성능 모니터링(APM)
  • 설명: New Relic은 클라우드 기반의 애플리케이션 성능 모니터링 도구로, Java Agent를 통해 애플리케이션의 성능을 모니터링하고 문제를 진단할 수 있습니다. New Relic의 Java Agent는 메서드 호출, 데이터베이스 쿼리, 외부 API 호출 등을 모니터링하여 실시간 성능 데이터를 수집합니다.
  • 특징: 다양한 대시보드와 실시간 성능 모니터링 기능을 제공하며, 클라우드 서비스와 통합이 용이합니다.

4. AspectJ

  • 용도: AOP(Aspect-Oriented Programming)
  • 설명: AspectJ는 Java에서 AOP를 구현하는 프레임워크로, Java Agent를 통해 런타임에 클래스 파일을 위빙(Weaving)할 수 있습니다. 이를 통해 특정 포인트컷(Pointcut)에 따라 메서드 호출 전후에 코드를 삽입하는 등의 작업을 수행할 수 있습니다.
  • 특징: 로드 타임 위빙(LTW)을 지원하며, 복잡한 비즈니스 로직에서 횡단 관심사를 분리하여 관리할 수 있습니다.

5. Byte Buddy

  • 용도: 런타임 바이트코드 조작
  • 설명: Byte Buddy는 Java 클래스의 바이트코드를 런타임에 동적으로 생성하거나 변환하는 라이브러리입니다. Java Agent를 통해 클래스 로딩 시점에서 바이트코드를 변환하는 작업을 쉽게 수행할 수 있습니다.
  • 특징: 다양한 프레임워크와 통합이 용이하며, 바이트코드를 조작하는 데 필요한 강력한 API를 제공합니다.

6. BTrace

  • 용도: 런타임 디버깅 및 트레이싱
  • 설명: BTrace는 Java 애플리케이션의 런타임 동작을 실시간으로 추적할 수 있는 도구입니다. BTrace의 Java Agent는 애플리케이션 코드의 변경 없이 다양한 이벤트를 실시간으로 추적할 수 있습니다.
  • 특징: 매우 가볍고, 애플리케이션의 성능에 최소한의 영향을 주며, 실시간 모니터링에 적합합니다.

7. Glowroot

  • 용도: 애플리케이션 성능 모니터링(APM)
  • 설명: Glowroot는 오픈 소스 APM 도구로, Java Agent를 통해 애플리케이션의 성능을 모니터링합니다. Glowroot는 스레드 프로파일링, 트랜잭션 추적, SQL 분석 등 다양한 기능을 제공합니다.
  • 특징: 설치와 설정이 간편하며, 경량화된 APM 솔루션으로 많은 개발자와 팀에서 애용됩니다.

Java Agent는 JVM에서 실행되는 Java 애플리케이션의 동작을 실시간으로 모니터링하거나 수정할 수 있는 매우 강력한 도구입니다. 위에서 소개한 대표적인 Java Agent들은 각각 다른 목적에 맞게 설계되어 있으며, 성능 모니터링, 코드 커버리지 분석, AOP 구현, 디버깅 등 다양한 작업을 수행할 수 있습니다.

위의 Java Agent 코드는 JVM이 시작될 때 로드되어, 모든 클래스가 로드될 때마다 해당 클래스의 이름을 출력하고, 바이트코드를 수정하지 않고 원본 그대로 반환하는 간단한 Java Agent입니다. 이 Java Agent를 사용하는 코드는 다음과 같습니다.

1. Java Agent 컴파일 및 JAR 파일 생성

먼저, 위의 Java Agent 코드를 컴파일하고, JAR 파일로 패키징해야 합니다. JAR 파일에는 META-INF/MANIFEST.MF 파일에 Premain-Class 속성을 설정해야 합니다.

1.1. SimpleAgent.java 파일로 저장

위 코드를 SimpleAgent.java 파일로 저장합니다.

1.2. 컴파일

javac SimpleAgent.java

1.3. JAR 파일 생성

JAR 파일을 생성할 때, META-INF/MANIFEST.MF 파일에 Premain-Class를 지정합니다.

MANIFEST.MF 파일:

Premain-Class: SimpleAgent

JAR 파일 생성 명령:

jar cmf MANIFEST.MF simple-agent.jar SimpleAgent.class SimpleTransformer.class

2. Java Agent를 사용하는 애플리케이션 실행

이제, 생성된 Java Agent JAR 파일(simple-agent.jar)을 애플리케이션과 함께 실행합니다. 아래는 Java Agent를 사용하는 애플리케이션 실행 예제입니다.

2.1. 간단한 Java 애플리케이션 작성

public class MyApplication {
    public static void main(String[] args) {
        System.out.println("Hello, World!");
    }
}

이 코드를 MyApplication.java로 저장하고 컴파일합니다.

javac MyApplication.java

2.2. Java Agent와 함께 애플리케이션 실행

Java Agent를 사용하려면 -javaagent 옵션을 사용하여 JVM을 실행할 때 에이전트를 로드합니다.

java -javaagent:/path/to/simple-agent.jar -cp . MyApplication

3. 실행 결과

애플리케이션이 실행될 때, SimpleAgent가 로드되고, MyApplication 클래스를 포함한 모든 클래스가 로드될 때마다 클래스 이름이 출력됩니다.

Transforming class: MyApplication
Hello, World!

요약

  1. SimpleAgent.java 코드를 컴파일하고 JAR 파일로 패키징합니다.
  2. -javaagent 옵션을 사용하여 Java Agent JAR 파일을 로드하고 애플리케이션을 실행합니다.
  3. 애플리케이션이 실행되는 동안 로드되는 모든 클래스 이름이 출력됩니다.

이 예제는 Java Agent의 기본적인 사용 방법을 보여주며, 실제 애플리케이션에서 다양한 목적을 위해 바이트코드 조작을 적용할 수 있습니다.