Search code examples
javalayoutlogback

Logback custom layout with complex attributes


So, I am creating a custom logback layout based:

public class MyCustomLayout extends LayoutBase<ILoggingEvent> {
    private String mySimpleArg;

    public void setMySimpleArg(String mySimpleArg) { 
        this.mySimpleArg = mySimpleArg;
    }

    public String doLayout(IlogginEvent iLoggingEvent) {
        //generate log in my format and use mySimpleArg
    }
}

and in my logback.xml I have

<appender ...>
    <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
        <layout class="path.to.MyCustomLayout">
            <mySimpleArg>some text</mySimpleArg>
        </layout>
    </encoder>
 ...
</appender>

Now, the problem is, I also need to read more complex arguments from my XML conf and I cannot find any info in logback docs as well as on Google. To be specific, I need something like

public class MyCustomLayout extends LayoutBase<ILoggingEvent> {
    private String mySimpleArg;
    //the Pair here is just to show I need something key -> value based
    private Pair<String, Object>[] myComplexArray;

    public void setMySimpleArg(String mySimpleArg) { 
        this.mySimpleArg = mySimpleArg;
    }

    public void setMyComplexArray(Pair<String, Object>[] myComplexArray) {
        this.myComplexArray = myComplexArray;
    }

    public String doLayout(IlogginEvent iLoggingEvent) {
        //generate log in my format and use mySimpleArg and myComplexArray
    }
}

and in logback.xml

<appender ...>
    <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
        <layout class="path.to.MyCustomLayout">
            <mySimpleArg>some text</mySimpleArg>
            <myComplexArray>
                <myComplexElement key="key1" value="value1"/>
                <myComplexElement key="key2" value=5/>
            </myComplexArray>
        </layout>
    </encoder>
 ...
</appender>

Solution

  • In order for Logback to populate a complex, collection type on a custom object in its domain the following must hold true:

    • Logback must be able to discover the complex object using a simple XML element name -> class name mapping
    • The complex object must expose an 'adder method' e.g. if the complex type is called Foo and it contains a collection of Bar then Foo must expose a public addBar(Bar bar){} method.

    With this in mind, the following config ...

    <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
        <layout class="path.to.MyCustomLayout">
            <mySimpleArg>some text</mySimpleArg>
            <myComplexArray>
                <myComplexElement>
                    <key>key1</key>
                    <value>value1</value>
                </myComplexElement>
                <myComplexElement>
                    <key>key2</key>
                    <value>value2</value>
                </myComplexElement>
            </myComplexArray>
        </layout>
    </encoder>
    

    ... will work as long as you declare MyCustomLayout like this ...

    public class MyCustomLayout extends LayoutBase<ILoggingEvent> {
        private String mySimpleArg;
        private MyComplexArray myComplexArray;
    
        public void setMySimpleArg(String mySimpleArg) {
            this.mySimpleArg = mySimpleArg;
        }
    
        public void setMyComplexArray(MyComplexArray myComplexArray) {
            this.myComplexArray = myComplexArray;
        }
    
        public String doLayout(ILoggingEvent iLoggingEvent) {
            // generate log in my format and use mySimpleArg and myComplexArray
            return "...";
        }
    }
    

    ... and you provide the following class definitions for MyComplexArray and MyComplexElement (note: the names of these classes must match the element names you use in logback.xml):

    public class MyComplexArray {
    
        private List<MyComplexElement> myComplexElements = new ArrayList<>();
    
        public MyComplexArray() {
        }
    
        public void addMyComplexElement(MyComplexElement myComplexElement) {
            myComplexElements.add(myComplexElement);
        }
    }
    
    public class MyComplexElement {
    
        private String key;
        private String value;
    
        public MyComplexElement() {
        }
    
        public String getKey() {
            return key;
        }
    
        public void setKey(String key) {
            this.key = key;
        }
    
        public String getValue() {
            return value;
        }
    
        public void setValue(String value) {
            this.value = value;
        }
    }
    

    If that's all too awkward then you may want to fall back to a simple String property (just like <mySimpleArg>) and pass in an encoded string from which you derive a 'complex object' inside MyCustomLayout. For example:

    <myComplexArg>foo=bar,bas=1|x=y,z=2</myComplexArg>