JProfiler 도움말

임베디드 프로브

프로브의 대상이 되는 소프트웨어 컴포넌트의 소스 코드를 직접 제어할 수 있다면, 주입된 프로브 대신 임베디드 프로브를 작성해야 합니다.

주입된 프로브를 작성할 때 초기에 가장 많은 노력이 들어가는 부분은 가로챌 메서드를 지정하고 핸들러 메서드의 메서드 파라미터로 주입된 객체를 선택하는 것입니다. 임베디드 프로브를 사용하면 monitor되는 메서드에서 직접 임베디드 프로브 API를 호출할 수 있으므로 이러한 작업이 필요하지 않습니다. 임베디드 프로브의 또 다른 장점은 배포가 자동으로 이루어진다는 점입니다. 프로브는 소프트웨어와 함께 제공되며 애플리케이션이 프로파일될 때 JProfiler UI에 나타납니다. 함께 제공해야 하는 유일한 종속성은 프로파일링 에이전트를 위한 훅 역할을 하는 빈 메서드 본문으로 주로 구성된, Apache 2.0 라이선스가 적용된 작은 JAR 파일뿐입니다.

개발 환경

개발 환경은 주입된 프로브와 동일하지만, 아티팩트 이름이 jprofiler-probe-injected 대신 jprofiler-probe-embedded라는 점과 프로브를 별도의 프로젝트에서 개발하는 대신 애플리케이션과 함께 JAR 파일을 제공한다는 점이 다릅니다. 소프트웨어 컴포넌트에 임베디드 프로브를 추가하는 데 필요한 프로브 API는 단일 JAR 아티팩트에 포함되어 있습니다. API를 살펴볼 때는 javadoc에서 com.jprofiler.api.probe.embedded의 패키지 개요부터 시작하십시오.

주입된 프로브와 마찬가지로 임베디드 프로브에도 두 가지 예제가 있습니다. api/samples/simple-embedded-probe에는 임베디드 프로브 작성을 시작하는 데 도움이 되는 예제가 있습니다. 해당 디렉터리에서 ../gradlew를 실행하여 컴파일 및 실행하고, gradle 빌드 파일인 build.gradle을 살펴보고 실행 환경을 이해하십시오. 제어 객체를 비롯한 추가 기능에 대해서는 api/samples/advanced-embedded-probe의 예제를 참조하십시오.

페이로드 프로브

주입된 프로브와 유사하게, 설정을 위해 여전히 프로브 클래스가 필요합니다. 프로브 클래스는 프로브가 페이로드를 수집하는지 또는 호출 트리를 분할하는지에 따라 com.jprofiler.api.probe.embedded.PayloadProbe 또는 com.jprofiler.api.probe.embedded.SplitProbe를 상속해야 합니다. 주입된 프로브 API에서는 페이로드 수집 및 분할을 위해 핸들러 메서드에 서로 다른 어노테이션을 사용합니다. 반면 임베디드 프로브 API에는 핸들러 메서드가 없으므로 이러한 설정을 프로브 클래스 자체로 옮겨야 합니다.

public class FooPayloadProbe extends PayloadProbe {
    @Override
    public String getName() {
        return "Foo queries";
    }

    @Override
    public String getDescription() {
        return "Records foo queries";
    }
}

주입된 프로브가 설정을 위해 어노테이션을 사용하는 것과 달리, 임베디드 프로브는 프로브의 기본 클래스에 있는 메서드를 오버라이드하여 설정합니다. 페이로드 프로브의 유일한 추상 메서드는 getName()이며, 다른 모든 메서드에는 필요한 경우 오버라이드할 수 있는 기본 구현이 있습니다. 예를 들어 오버헤드를 줄이기 위해 이벤트 뷰를 비활성화하려면 isEvents()를 오버라이드하여 false를 반환하도록 할 수 있습니다.

페이로드를 수집하고 관련 타이밍을 측정하려면 Payload.enter() 및 Payload.exit() 호출 쌍을 사용합니다.

public void measuredCall(String query) {
    Payload.enter(FooPayloadProbe.class);
    try {
        performWork();
    } finally {
        Payload.exit(query);
    }
}

Payload.enter() 호출은 프로브 클래스를 인수로 받으므로 프로파일링 에이전트가 어떤 프로브가 호출 대상인지 알 수 있으며, Payload.exit() 호출은 동일한 프로브와 자동으로 연결되고 페이로드 문자열을 인수로 받습니다. exit 호출이 누락되면 호출 트리가 손상되므로, 이는 항상 try 블록의 finally 절에서 수행되어야 합니다.

측정된 코드 블록이 아무런 값도 생성하지 않는 경우, 페이로드 문자열과 Runnable을 받는 Payload.execute 메서드를 대신 호출할 수 있습니다. Java 8 이상에서는 람다나 메서드 참조를 통해 이 메서드 호출을 매우 간결하게 작성할 수 있습니다.

public void measuredCall(String query) {
  Payload.execute(FooPayloadProbe.class, query, this::performWork);
}

이 경우 페이로드 문자열을 미리 알고 있어야 합니다. Callable을 받는 execute 버전도 있습니다.

public QueryResult measuredCall(String query) throws Exception {
    return Payload.execute(FooPayloadProbe.class, query, () -> query.execute());
}

Callable을 받는 시그니처의 문제점은 Callable.call()이 검사 Exception을 던지므로 이를 catch하거나 이를 포함하는 메서드에 선언해야 한다는 것입니다.

제어 객체

페이로드 프로브는 Payload 클래스의 적절한 메서드를 호출하여 제어 객체를 열고 닫을 수 있습니다. 제어 객체와 커스텀 이벤트 타입을 받는 Payload.enter() 또는 Payload.execute() 메서드 버전에 제어 객체를 전달함으로써 프로브 이벤트와 연결됩니다.

public void measuredCall(String query, Connection connection) {
    Payload.enter(FooPayloadProbe.class, connection, MyEventTypes.QUERY);
    try {
        performWork();
    } finally {
        Payload.exit(query);
    }
}

제어 객체 뷰는 프로브 설정에서 명시적으로 활성화해야 하며, 커스텀 이벤트 타입도 프로브 클래스에 등록되어야 합니다.

public class FooPayloadProbe extends PayloadProbe {
    @Override
    public String getName() {
        return "Foo queries";
    }

    @Override
    public String getDescription() {
        return "Records foo queries";
    }

    @Override
    public boolean isControlObjects() {
        return true;
    }

    @Override
    public Class<? extends Enum> getCustomTypes() {
        return MyEventTypes.class;
    }
}

제어 객체를 명시적으로 열고 닫지 않는 경우, 모든 제어 객체의 표시 이름을 확인하기 위해 프로브 클래스에서 getControlObjectName을 오버라이드해야 합니다.

분할 프로브

분할 프로브 기본 클래스에는 프로브 뷰를 추가하지 않고 호출 트리만 분할하는 데 사용될 수 있으므로 추상 메서드가 없습니다. 이러한 경우 최소 프로브 정의는 다음과 같습니다.

public class FooSplitProbe extends SplitProbe {}

분할 프로브의 중요한 설정 중 하나는 재진입 가능 여부입니다. 기본적으로는 최상위 호출만 분할됩니다. 재귀 호출도 분할하려면 isReentrant()를 오버라이드하여 true를 반환하도록 하십시오. 분할 프로브는 프로브 클래스에서 isPayloads()가 true를 반환하도록 오버라이드하면 프로브 뷰를 생성하고 분할 문자열을 페이로드로 게시할 수도 있습니다.

분할을 수행하려면 Split.enter() 및 Split.exit() 호출 쌍을 사용하십시오.

public void splitMethod(String parameter) {
    Split.enter(FooSplitProbe.class, parameter);
    try {
        performWork(parameter);
    } finally {
        Split.exit();
    }
}

페이로드 수집과 달리, 분할 문자열은 프로브 클래스와 함께 Split.enter() 메서드에 전달되어야 합니다. 여기서도 Split.exit()가 안정적으로 호출되는 것이 중요하므로 try 블록의 finally 절에 있어야 합니다. Split은 단일 호출로 분할을 수행하는 Runnable 및 Callable 인수를 갖는 execute() 메서드도 제공합니다.

텔레메트리

동일한 클래스패스에 존재하므로 애플리케이션의 모든 static 메서드에 직접 접근할 수 있어, 임베디드 프로브의 텔레메트리를 게시하는 것이 특히 편리합니다. 주입된 프로브와 마찬가지로 프로브 설정 클래스의 static public 메서드에 @Telemetry 어노테이션을 지정하고 숫자 값을 반환하십시오. 자세한 내용은 프로브 개념 장을 참조하십시오. 임베디드 및 주입된 프로브 API의 @Telemetry 어노테이션은 서로 패키지만 다를 뿐 동일합니다.

임베디드 프로브 API와 주입된 프로브 API 간의 또 다른 유사한 기능은 ThreadState 클래스를 사용하여 스레드 상태를 수정하는 기능입니다. 이 클래스 역시 서로 다른 패키지로 두 API 모두에 존재합니다.

배포

JProfiler UI로 프로파일링할 때 임베디드 프로브를 활성화하기 위해 필요한 특별한 단계는 없습니다. 그러나 프로브는 Payload 또는 Split에 대한 첫 번째 호출이 이루어질 때만 등록됩니다. 해당 시점이 되어서야 연결된 프로브 뷰가 JProfiler에 생성됩니다. 기본 제공 프로브 및 주입된 프로브처럼 프로브 뷰가 처음부터 표시되도록 하려면, 페이로드 프로브의 경우

Payload.register(FooPayloadProbe.class);

를 호출하고 분할 프로브의 경우

Split.register(FooSplitProbe.class);

를 호출할 수 있습니다.

오버헤드를 최소화하기 위해 명령줄 스위치 등으로 제어하여 Payload 및 Split의 메서드를 조건부로 호출해야 할지 고민할 수도 있습니다. 하지만 메서드 본문이 비어 있으므로 일반적으로 이는 필요하지 않습니다. 프로파일링 에이전트가 attach되지 않은 상태에서는 페이로드 문자열 생성을 제외하고는 어떠한 오버헤드도 발생하지 않습니다. 프로브 이벤트는 극도로 미세한 규모로 생성되어서는 안 된다는 점을 감안하면 비교적 드물게 생성되므로, 페이로드 문자열을 생성하는 작업은 비교적 미미한 수준입니다.

컨테이너에 대한 또 다른 우려는 클래스패스에 외부 종속성을 노출하고 싶지 않다는 점일 수 있습니다. 컨테이너 사용자가 임베디드 프로브 API를 사용할 수도 있어 충돌이 발생할 수 있습니다. 이러한 경우 임베디드 프로브 API를 자체 패키지로 shade할 수 있습니다. JProfiler는 여전히 shade된 패키지를 인식하고 API 클래스를 올바르게 계측합니다. 빌드 시점의 셰이딩이 실용적이지 않은 경우, 소스 아카이브의 압축을 풀고 해당 클래스를 프로젝트의 일부로 포함할 수 있습니다.