microsoft / ApplicationInsights-Java

Application Insights for Java
http://aka.ms/application-insights
Other
297 stars 199 forks source link

Support for restlet framework #3331

Closed avdhut1990 closed 11 months ago

avdhut1990 commented 1 year ago

Currently applications using restlet framework https://github.com/restlet/restlet-framework-java are not auto-instrumented when using the app insights java sdk. We see the application in the app insights application map, but inbound requests are not collected.

Open Telemetry sdk supports restlet framework instrumentation since v1.0 https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/docs/supported-libraries.md#libraries--frameworks

It would be great to add support to app insights sdk as well.

jeanbisutti commented 1 year ago

Hi @avdhut1990!

You can use the Restlet instrumentation libraries with the Application Insights Java agent

https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/docs/supported-libraries.md#libraries--frameworks

image

avdhut1990 commented 1 year ago

Hi @jeanbisutti, I have added the opentelemetry restlet dependencies in application in addition to the existing auto-instrumentation using app insights agent. Do I need perform any additional steps?

Thanks.

jeanbisutti commented 1 year ago

Hi @jeanbisutti, I have added the opentelemetry restlet dependencies in application in addition to the existing auto-instrumentation using app insights agent. Do I need perform any additional steps?

Thanks.

You can have a look at the examples of the test folder, for example for Restlet 2: https://github.com/open-telemetry/opentelemetry-java-instrumentation/tree/main/instrumentation/restlet/restlet-2.0/library/src/test/groovy/io/opentelemetry/instrumentation/restlet/v2_0

avdhut1990 commented 1 year ago

Hi @jeanbisutti ,

I have added the RestletTelemetryBuilder and StatusFilter code to the following method as per the example, however still not getting the requests captured in app insights. Do note that the application is already instrumented using app insights java agent and is able to capture traces and dependencies. Only requests are not getting captured.

package com.msci.esg.dataservice.restlet;

import java.io.File;
import java.io.IOException;
import java.util.Properties;
import java.util.logging.Level;

import com.msci.esg.dataservice.collaborators.usermanagement.UserManagementClientFactory;
import com.msci.esg.dataservice.screening.ChangeReportGenerator;
import com.azure.identity.DefaultAzureCredentialBuilder;
import com.azure.security.keyvault.secrets.SecretClientBuilder;
import com.azure.storage.blob.BlobServiceClientBuilder;
import com.msci.esg.dataservice.CollaboratorClient;
import com.msci.esg.dataservice.azure.appconfig.AzureAppConfigInfo;
import com.msci.esg.dataservice.azure.appconfig.AzureAppConfigStorer;
import com.msci.esg.dataservice.azure.storage.AdlsStorage;
import com.msci.esg.dataservice.collaborators.wsdata.WSDataClient;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.instrumentation.restlet.v2_0.RestletTelemetry;
import org.apache.commons.lang3.SystemUtils;
import org.restlet.Application;
import org.restlet.Component;
import org.restlet.Restlet;
import org.restlet.Server;
import org.restlet.data.Protocol;
import org.restlet.data.Reference;
import org.restlet.engine.application.StatusFilter;
import org.restlet.resource.Directory;
import org.restlet.routing.Redirector;
import org.restlet.routing.Router;

import com.msci.esg.dataservice.collaborators.capture.CaptureClientFactory;
import com.msci.esg.dataservice.collaborators.princexml.PrinceXMLClientFactory;
import com.msci.esg.dataservice.collaborators.wsdata.WSDataClientFactory;
import com.msci.esg.dataservice.config.ConfigurationManager;
import com.msci.esg.dataservice.config.GlobalConfigCache;
import com.msci.esg.dataservice.types.ESGDataServicesConfig;
import com.msci.esg.dataservice.types.ServerLocation;
import com.msci.esg.dataservice.utils.LogRecorder;
import org.restlet.service.StatusService;

public class ESGDataServiceApplication extends Application {
    private static final LogRecorder auditer; static { auditer = LogRecorder.logger(); }

    private static Component component = null;
    private final String serviceHost;
    private final int servicePort;
    private final boolean omitPort;
    private boolean streamResults;
    public static final String RESTLET_HTTP_HEADERS= "org.restlet.http.headers";

    @Override
    public synchronized Restlet createInboundRoot() {
        final Router router = new Router(getContext());
        router.attach("/query", streamResults ? StreamingQueryResource.class : QueryResource.class);
        router.attach("/report", ReportResource.class);
        router.attach("/home", HomeResource.class);
        router.attach("/systemInfo", SystemInfoResource.class);
        router.attach("/post", PostResource.class);
        router.attach("/post/{name}", PostResource.class);
        router.attach("/query/{name}", streamResults ? StreamingPostPageQueryResource.class : PostPageQueryResource.class);
        router.attach("/itemImport", ItemImportResource.class);
        router.attach("/stream", StreamResource.class);

        RestletTelemetry telemetry = RestletTelemetry.builder(GlobalOpenTelemetry.get()).build();
        StatusFilter statusFilter = new StatusFilter(component.getContext(), new StatusService());
        telemetry.newFilter("/query").setNext(statusFilter);
        telemetry.newFilter("/report").setNext(statusFilter);
        telemetry.newFilter("/home").setNext(statusFilter);
        telemetry.newFilter("/systemInfo").setNext(statusFilter);
        telemetry.newFilter("/post").setNext(statusFilter);
        telemetry.newFilter("/post/{name}").setNext(statusFilter);
        telemetry.newFilter("/query/{name}").setNext(statusFilter);
        telemetry.newFilter("/itemImport").setNext(statusFilter);
        telemetry.newFilter("/stream").setNext(statusFilter);
        statusFilter.setNext(router);

        String rootPath;
        try {
            File file = new File(".");
            rootPath = file.getCanonicalPath();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        if(SystemUtils.IS_OS_WINDOWS) {
            Directory directory = new Directory(getContext(), new Reference("file:\\\\" + rootPath + "\\images"));
            router.attach("/images", directory);
        } else {
            Directory directory = new Directory(getContext(), new Reference("file://" + rootPath + "/images"));
            router.attach("/images", directory);
        }
        String hostUriForClients = serviceHost + (omitPort ? "" : ":" + servicePort);
        final String target = hostUriForClients + "/home";
        final Redirector redirector = new Redirector(getContext(), target, Redirector.MODE_CLIENT_TEMPORARY);
        router.attach("/", redirector);

        return router;
    }
}
jeanbisutti commented 1 year ago

Hi @avdhut1990

In the Restlet examples I can see that the result of telemetry.newFilter is returned (see this example and this example). Could you try in this way?

avdhut1990 commented 12 months ago

Hi @jeanbisutti ,

Following code worked:

private synchronized void attachRoutes(RestletTelemetry telemetry, Router router, String path,
                                           Class<? extends ServerResource> targetClass) {
        Filter routeTelemetryFilter = telemetry.newFilter(path);
        StatusFilter statusFilter = new StatusFilter(component.getContext(), new StatusService());
        statusFilter.setNext(targetClass);
        routeTelemetryFilter.setNext(statusFilter);
        router.attach(path, routeTelemetryFilter);
    }

@Override
    public synchronized Restlet createInboundRoot() {
        final Router router = new Router(getContext());
        RestletTelemetry telemetry = RestletTelemetry.builder(GlobalOpenTelemetry.get()).build();

        attachRoutes(telemetry, router, "/query", streamResults ? StreamingQueryResource.class : QueryResource.class);
        attachRoutes(telemetry, router, "/report", ReportResource.class);
        attachRoutes(telemetry, router, "/home", HomeResource.class);
        attachRoutes(telemetry, router, "/systemInfo", SystemInfoResource.class);
        attachRoutes(telemetry, router, "/post", PostResource.class);
        attachRoutes(telemetry, router, "/post/{name}", PostResource.class);
        attachRoutes(telemetry, router, "/query/{name}", streamResults ? StreamingPostPageQueryResource.class : PostPageQueryResource.class);
        attachRoutes(telemetry, router, "/itemImport", ItemImportResource.class);
        attachRoutes(telemetry, router, "/stream", StreamResource.class);

        String rootPath;
        try {
            File file = new File(".");
            rootPath = file.getCanonicalPath();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        if(SystemUtils.IS_OS_WINDOWS) {
            Directory directory = new Directory(getContext(), new Reference("file:\\\\" + rootPath + "\\images"));
            router.attach("/images", directory);
        } else {
            Directory directory = new Directory(getContext(), new Reference("file://" + rootPath + "/images"));
            router.attach("/images", directory);
        }
        String hostUriForClients = serviceHost + (omitPort ? "" : ":" + servicePort);
        final String target = hostUriForClients + "/home";
        final Redirector redirector = new Redirector(getContext(), target, Redirector.MODE_CLIENT_TEMPORARY);
        router.attach("/", redirector);

        return router;
    }

Is it possible to capture the request and response body as well within restlet and app insights?

Thanks.

jeanbisutti commented 12 months ago

@avdhut1990 Could you please create a PR here for a minimal reproducible example with explanations on how to run the example?

jeanbisutti commented 11 months ago

@avdhut1990 Perhaps you mean that you can see the HTTP requests on Application Insights UI but not their body?

avdhut1990 commented 11 months ago

Hi @jeanbisutti, thanks for your help. The requirement to capture request payload has been deprioritized. Will reach out when it gets prioritized again. Closing this ticket for now. Thanks.