Expand my Community achievements bar.

Adobe Summit 2025: AEM Session Recordings Are Live! Missed a session or want to revisit your favorites? Watch the latest recordings now.
SOLVED

How to add a custom Log Appender?

Avatar

Level 5

I am trying to wire an Application Insights instance into my AEMaaCS installation.  The idea is that I want to be able to view my logs from application insights, rather than the native AEMaaCS mechanisms.

 

I have installed App Insights and configured the telemetry client.  This code works and I can see the message written to app insights:

 

TelemetryClient client = new TelemetryClient();

client.trackTrace("This is a test...");
 
However, the rest of my application is using the sl4j logger, via code like this:
 
private final Logger _log = LoggerFactory.getLogger(Foo.class);

// ...

_log.info("Hello world");
How can I hook the telemetry client from App Insights into the sl4j logging framework?  I don't see where I can add any appenders via configuration.  Has anyone tried to do this before?
1 Accepted Solution

Avatar

Correct answer by
Level 5

For anyone who stumbles upon this in the future, here's how I have done it:

 

First, you need an Appender created.

 

import java.util.Dictionary;
import java.util.Hashtable;

import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.propertytypes.ServiceRanking;

import com.microsoft.applicationinsights.TelemetryClient;
import com.microsoft.applicationinsights.TelemetryConfiguration;

import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.Appender;
import ch.qos.logback.core.AppenderBase;

@Component(immediate = true)
@ServiceRanking(Integer.MIN_VALUE)
public class AppInsightsAppender extends AppenderBase<ILoggingEvent>{
    TelemetryClient client = new TelemetryClient();

    private static String INSTRUMENTATION_KEY = "<your instrumentation key here>";
    
    private ServiceRegistration sr;

    public AppInsightsAppender() {
        setName("AppInsightsAppender");
    }

    @Override
    protected void append(ILoggingEvent e) {
        // todo: handle all types of events in a more graceful way
        client.trackTrace(e.getMessage());
    }

    @activate
    private void activate(BundleContext bundleContext){
        
        TelemetryConfiguration.getActive().setInstrumentationKey(INSTRUMENTATION_KEY);
        TelemetryConfiguration.getActive().setTrackingIsDisabled(false);

        Dictionary<String,Object> props = new Hashtable<String, Object>();

        String[] loggers = {
            "<your root package name here>"
        };

        props.put("loggers",loggers);
        sr = bundleContext.registerService(Appender.class.getName(),this,props);
    }

    @deactivate
    private void deactivate(){
        if(sr != null){
            sr.unregister();
        }
    }
}

 
The idea behind this appender is that the `append` method is invoked any time a log message is written.

 

Secondarily, if you want to track http calls, you can set up something like this:

 

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.propertytypes.ServiceRanking;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.microsoft.applicationinsights.TelemetryConfiguration;
import com.microsoft.applicationinsights.extensibility.TelemetryInitializer;
import com.microsoft.applicationinsights.extensibility.TelemetryModule;
import com.microsoft.applicationinsights.web.extensibility.initializers.WebOperationIdTelemetryInitializer;
import com.microsoft.applicationinsights.web.extensibility.initializers.WebOperationNameTelemetryInitializer;
import com.microsoft.applicationinsights.web.extensibility.initializers.WebSessionTelemetryInitializer;
import com.microsoft.applicationinsights.web.extensibility.initializers.WebUserAgentTelemetryInitializer;
import com.microsoft.applicationinsights.web.extensibility.initializers.WebUserTelemetryInitializer;
import com.microsoft.applicationinsights.web.extensibility.modules.WebRequestTrackingTelemetryModule;
import com.microsoft.applicationinsights.web.internal.WebRequestTrackingFilter;

@Component(immediate = true, service = { Filter.class },
           property = { "sling.filter.scope=request" })
@ServiceRanking(Integer.MIN_VALUE)
public class AppInsightsFilter implements Filter {

    private static final Logger LOG = LoggerFactory.getLogger(AppInsightsFilter.class);

    private WebRequestTrackingFilter webRequestTrackingFilter;

    private static String INSTRUMENTATION_KEY = "<your instrumentation key here>";

    @Override
    public void destroy() {
        webRequestTrackingFilter.destroy();

    }

    @Override
    public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException {
        webRequestTrackingFilter.doFilter(servletRequest, servletResponse, filterChain);
    }

    @Override
    public void init(final FilterConfig arg0) throws ServletException {
        webRequestTrackingFilter = new WebRequestTrackingFilter("<your package name here>");
        TelemetryConfiguration.getActive().setInstrumentationKey(INSTRUMENTATION_KEY);
        TelemetryConfiguration.getActive().setTrackingIsDisabled(false);
        
        List<TelemetryInitializer> initializers = new ArrayList<>();
        initializers.add(new WebOperationIdTelemetryInitializer());
        initializers.add(new WebOperationNameTelemetryInitializer());
        initializers.add(new WebSessionTelemetryInitializer());
        initializers.add(new WebUserTelemetryInitializer());
        initializers.add(new WebUserAgentTelemetryInitializer()); 

        TelemetryConfiguration.getActive().getTelemetryInitializers().addAll(initializers);
        TelemetryModule module = new WebRequestTrackingTelemetryModule();
        module.initialize(TelemetryConfiguration.getActive());
        
        TelemetryConfiguration.getActive().getTelemetryModules().add(module);
        
        webRequestTrackingFilter.init(arg0);
    }
}


Both of these classes present within the code appears to be enough.  Update your pom.xml dependencies:

 

        <dependency>
            <groupId>com.microsoft.azure</groupId>
            <artifactId>applicationinsights-web</artifactId>
			<version>2.5.0</version>
			<scope>compile</scope>
        </dependency>

View solution in original post

6 Replies

Avatar

Community Advisor

Hi @dylanmccurry 
Checkout Jatin's answer at https://stackoverflow.com/questions/67730720/azure-function-app-logs-into-app-insights-using-slf4j

He has used the logback dependency for the same solution.

Avatar

Level 5

Yes, in the referenced examples it states to add an appender to your XML config file for log4j.  Does this exist in AEMaaCS?  How do I add the telemetry client as an appender in AEMaaCS?

Avatar

Community Advisor

hi @dylanmccurry  I haven't tried with telemetry client but once try understanding below article on how to integrate and monitor logs using splunk. Hope this is useful

https://blog.arborydigital.com/splunk-setup-aem-cloud-service-aemaacs

 

Avatar

Level 5

Hi @Jagadeesh_Prakash - this article explains how to configure a splunk dashboard.  This is not what I am asking.

 

How do I modify the logging framework within AEM so that I can add my own custom appender? It's log4j under the hood, right? I am hoping for an approach that involves config files and/or java files that I can store in source control.

Avatar

Correct answer by
Level 5

For anyone who stumbles upon this in the future, here's how I have done it:

 

First, you need an Appender created.

 

import java.util.Dictionary;
import java.util.Hashtable;

import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.propertytypes.ServiceRanking;

import com.microsoft.applicationinsights.TelemetryClient;
import com.microsoft.applicationinsights.TelemetryConfiguration;

import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.Appender;
import ch.qos.logback.core.AppenderBase;

@Component(immediate = true)
@ServiceRanking(Integer.MIN_VALUE)
public class AppInsightsAppender extends AppenderBase<ILoggingEvent>{
    TelemetryClient client = new TelemetryClient();

    private static String INSTRUMENTATION_KEY = "<your instrumentation key here>";
    
    private ServiceRegistration sr;

    public AppInsightsAppender() {
        setName("AppInsightsAppender");
    }

    @Override
    protected void append(ILoggingEvent e) {
        // todo: handle all types of events in a more graceful way
        client.trackTrace(e.getMessage());
    }

    @activate
    private void activate(BundleContext bundleContext){
        
        TelemetryConfiguration.getActive().setInstrumentationKey(INSTRUMENTATION_KEY);
        TelemetryConfiguration.getActive().setTrackingIsDisabled(false);

        Dictionary<String,Object> props = new Hashtable<String, Object>();

        String[] loggers = {
            "<your root package name here>"
        };

        props.put("loggers",loggers);
        sr = bundleContext.registerService(Appender.class.getName(),this,props);
    }

    @deactivate
    private void deactivate(){
        if(sr != null){
            sr.unregister();
        }
    }
}

 
The idea behind this appender is that the `append` method is invoked any time a log message is written.

 

Secondarily, if you want to track http calls, you can set up something like this:

 

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.propertytypes.ServiceRanking;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.microsoft.applicationinsights.TelemetryConfiguration;
import com.microsoft.applicationinsights.extensibility.TelemetryInitializer;
import com.microsoft.applicationinsights.extensibility.TelemetryModule;
import com.microsoft.applicationinsights.web.extensibility.initializers.WebOperationIdTelemetryInitializer;
import com.microsoft.applicationinsights.web.extensibility.initializers.WebOperationNameTelemetryInitializer;
import com.microsoft.applicationinsights.web.extensibility.initializers.WebSessionTelemetryInitializer;
import com.microsoft.applicationinsights.web.extensibility.initializers.WebUserAgentTelemetryInitializer;
import com.microsoft.applicationinsights.web.extensibility.initializers.WebUserTelemetryInitializer;
import com.microsoft.applicationinsights.web.extensibility.modules.WebRequestTrackingTelemetryModule;
import com.microsoft.applicationinsights.web.internal.WebRequestTrackingFilter;

@Component(immediate = true, service = { Filter.class },
           property = { "sling.filter.scope=request" })
@ServiceRanking(Integer.MIN_VALUE)
public class AppInsightsFilter implements Filter {

    private static final Logger LOG = LoggerFactory.getLogger(AppInsightsFilter.class);

    private WebRequestTrackingFilter webRequestTrackingFilter;

    private static String INSTRUMENTATION_KEY = "<your instrumentation key here>";

    @Override
    public void destroy() {
        webRequestTrackingFilter.destroy();

    }

    @Override
    public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException {
        webRequestTrackingFilter.doFilter(servletRequest, servletResponse, filterChain);
    }

    @Override
    public void init(final FilterConfig arg0) throws ServletException {
        webRequestTrackingFilter = new WebRequestTrackingFilter("<your package name here>");
        TelemetryConfiguration.getActive().setInstrumentationKey(INSTRUMENTATION_KEY);
        TelemetryConfiguration.getActive().setTrackingIsDisabled(false);
        
        List<TelemetryInitializer> initializers = new ArrayList<>();
        initializers.add(new WebOperationIdTelemetryInitializer());
        initializers.add(new WebOperationNameTelemetryInitializer());
        initializers.add(new WebSessionTelemetryInitializer());
        initializers.add(new WebUserTelemetryInitializer());
        initializers.add(new WebUserAgentTelemetryInitializer()); 

        TelemetryConfiguration.getActive().getTelemetryInitializers().addAll(initializers);
        TelemetryModule module = new WebRequestTrackingTelemetryModule();
        module.initialize(TelemetryConfiguration.getActive());
        
        TelemetryConfiguration.getActive().getTelemetryModules().add(module);
        
        webRequestTrackingFilter.init(arg0);
    }
}


Both of these classes present within the code appears to be enough.  Update your pom.xml dependencies:

 

        <dependency>
            <groupId>com.microsoft.azure</groupId>
            <artifactId>applicationinsights-web</artifactId>
			<version>2.5.0</version>
			<scope>compile</scope>
        </dependency>

Avatar

Administrator

@dylanmccurry Did you find the suggestions from users helpful? Please let us know if more information is required. Otherwise, please mark the answer as correct for posterity. If you have found out solution yourself, please share it with the community.



Kautuk Sahni