saas-logging


Extension of log4j for logging in SaaS services

Introduction

When developing SaaS you (hopefully) have many customers accessing your service. Suppose one customer calls in and says that in a specific flow he has a problem (for example, when he asks for the lists of the assets he manages he gets HTTP error 500). He's the only customer that complains about this flow, so it's something unique to this customer. You look at the logs and you don't have enough information to figure out what went wrong (no surprise, you see your "ThisCanNeverHappenException"). The obvious thing to do is to change the log level to DEBUG and let the customer retry his flow. The side effect is frightening, your single log file is gigantic and you can't find your arms and legs anywhere. Debugging never looked this bad.

Details

This project is an extension for the log4j framework allowing logging messages from different customers to different log files, with different logging levels for each customer.

Using a custom appender you can log messages for customer a to a.log in DEBUG, customer b to b.log in TRACE and all the rest of your customers to all.log in INFO. You need to use a single access point to your code (e.g. use a Filter), set the customer context in MDC, update your log4j.xml file (has to be XML for using filters) and you are good to go. ``` package org.oded.logging;

import java.io.IOException;

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.apache.log4j.MDC;

public class MyFilter implements Filter { @Override public void destroy() { }

@Override
public void doFilter( ServletRequest request, ServletResponse response, FilterChain chain ) throws IOException, ServletException
{
    String customerName = ...; // retrieve the customer name from the request
    MDC.put( org.oded.logging.CustomerFileAppender.MDC_CUSTOMER_KEY, customerName );
    chain.doFilter( request, response );
}

@Override
public void init( FilterConfig filterConfig ) throws ServletException
{
}

} ```

```

http://jakarta.apache.org/log4j/'>

<appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
    <layout class="org.apache.log4j.PatternLayout">
        <param name="ConversionPattern" value="%d %p [%c] - &lt;%m&gt;%n"/>
    </layout>
</appender>

<appender name="LOGFILE" class="org.oded.logging.CustomerFileAppender">
    <param name="File" value="${user.home}/##customerName##.log" />        
    <param name="Append" value="true" />        
    <param name="BufferedIO" value="false" />       
    <param name="ImmediateFlush" value="true" />
    <param name="Threshold" value="ALL" />
    <layout class="org.apache.log4j.PatternLayout">
        <param name="ConversionPattern" value="%d %p [%c] - &lt;%m&gt;%n"/>
    </layout>
    <!-- below is an example of how to set a different logging level for 
    different customers, according to customer id.
    in the exmaple the default threshold is INFO, cust1 override it to use ERROR and cust2 use DEBUG.
    This means that only cust1 won't log INFO messages, and only cust2 will log DEBUG messages.
    Note that for cust2 to log DEBUG messages the logger level must allow DEBUG messages as well.  -->
    <filter class="org.oded.logging.CustomerFilter">
        <param name="threshold" value="cust1=ERROR;cust2=DEBUG" />
        <param name="defaultThreshold" value="INFO" />
    </filter>
</appender>

<root>
    <level value="INFO"/>
    <appender-ref ref="CONSOLE"/> 
    <appender-ref ref="LOGFILE"/> 
</root>

```

Project Information

Labels:
SaaS logging