GitHub

Extending

You can edit this page on GitHub

tinylog is extendable, and it is possible to develop custom writers as well as custom policies. How this works, what has to be considered, and what are the possibilities, are explained by means of examples.

Custom Writer

A custom writer simply has to implement the interface Writer with its four methods. Potential properties from the configuration are passed as Map<String, String>. Therefore, every writer has to provide a public constructor that accepts a string map. The example below shows a simple writer that just outputs all log entries via System.out.

package org.tinylog.example;

import java.util.Collection;
import java.util.EnumSet;
import java.util.Map;

import org.tinylog.core.LogEntry;
import org.tinylog.core.LogEntryValue;
import org.tinylog.writers.Writer;

public class SystemOutWriter implements Writer {

    private final String delimiter;

    public SystemOutWriter(Map<String, String> properties) {
        delimiter = properties.getOrDefault("delimiter", "-");
    }

    @Override
    public Collection<LogEntryValue> getRequiredLogEntryValues() {
        return EnumSet.of(LogEntryValue.LEVEL, LogEntryValue.MESSAGE);
    }

    @Override
    public void write(LogEntry logEntry) {
        System.out.println(logEntry.getLevel() + " " + delimiter + " " + logEntry.getMessage());
    }

    @Override
    public void flush() {
        System.out.flush();
    }

    @Override
    public void close() {
        // System.out doesn't have to be closed
    }

}

Writers have to be registered as a service so that tinylog can find them at runtime. If it does not yet exist, create the directory META-INF/services and insert a text file with the name org.tinylog.writers.Writer. In this text file, all custom writers can be input line by line with their fully-qualified class names.

For the writer, META-INF/services/org.tinylog.writers.Writer would look like this:

org.tinylog.example.SystemOutWriter

Now the writer is ready to be used. To use it in a configuration, the name of the writer is derived from the class name. Spaces are inserted between the words, and "Writer" disappears at the end. Hence, SystemOutWriter becomes system out.

Configuration of the new custom writer in tinylog.properties:

writer           = system out
writer.delimiter = ::

Instead of only defining a delimiter for the output of log entries, the output should usually be completely freely configurable as a format pattern. The class AbstractFormatPatternWriter provides this functionality out of the box, and also computes all required log entry values automatically. This simplifies the implementation of the writer with support for format patterns.

package org.tinylog.example;

import java.util.Map;

import org.tinylog.core.LogEntry;
import org.tinylog.writers.AbstractFormatPatternWriter;

public class SystemOutWriter extends AbstractFormatPatternWriter {

    public SystemOutWriter(final Map<String, String> properties) {
        super(properties);
    }

    @Override
    public void write(LogEntry logEntry) {
        System.out.println(render(logEntry));
    }

    @Override
    public void flush() {
        System.out.flush();
    }

    @Override
    public void close() {
        // System.out doesn't have to be closed
    }

}
writer           = system out
writer.format    = {level} :: {message}

The class AbstractFormatPatternWriter also provides utility methods to write log entries to files. A good example is the FileWriter for understanding how to write log entries to files in a simple and safe way, by using the tinylog framework.

Custom Policy

A custom policy for the rolling file writer simply has to implement the interface Policy with its three methods. A potential argument from the configuration is passed as a string. Therefore, every policy has to provide a public constructor that accepts a string. The example below illustrates a simple policy that randomly decides whether the current log file should be continued, or a new one started. The method continueExistingFile() is called to determine whether an already existing log file should be continued, and continueCurrentFile() to determine whether a new log entry can be still written to the current log file. After starting a new log file by any policy, the third method reset() is called.

package org.tinylog.example;

import java.util.Random;

import org.tinylog.policies.Policy;

public class RandomPolicy implements Policy {

    private final Random random;

    public RandomPolicy(String argument) {
        if (argument == null || argument.isEmpty()) {
            random = new Random();
        } else {
            random =  new Random(Long.parseLong(argument));
        }
    }

    @Override
    public boolean continueExistingFile(String path) {
        return random.nextBoolean();
    }

    @Override
    public boolean continueCurrentFile(byte[] entry) {
        return random.nextBoolean();
    }

    @Override
    public void reset() {
        // Nothing to do
    }

}

Just like writers, policies have to be registered as a service so that tinylog can find them at runtime. If it does not yet exist, create the directory META-INF/services and insert a text file with the name org.tinylog.policies.Policy. In this text file, all custom policies can be input line by line with their fully-qualified class names.

For the policy, META-INF/services/org.tinylog.policies.Policy would look like this:

org.tinylog.example.RandomPolicy

Now the policy is ready to be used. To use it in a configuration, the name of the policy is derived from the class name. Spaces are inserted between the words and "Policy" disappears at the end. Hence, RandomPolicy becomes just random.

Configuration of the new custom policy in tinylog.properties:

writer           = rolling file
writer.file      = log.txt
writer.policies  = random: 42