Useful Android Platform Logging Framework Slog

Posted by ade234uk on Mon, 24 Jun 2019 19:02:35 +0200

Slog

GitHub project address: https://github.com/shenbibo/Slog

Summary

Slog is a lightweight Android platform's log Library Based on a combination and extension of the current open source logging framework, Logger and Timber.It has great scalability and the following new features compared to native Android Log.

  • Supports formatted typesetting output of logs for clearer display and easier viewing.
  • Stack and current thread information supporting the output print log method.
  • Supports printing objects, supports custom object parsers, provides parsing of arrays, collections, etc. by default.
  • Multiple custom log adapters are supported to determine how logs are processed, and the LogcatTree adapter is provided by default.
  • Supports customizing the log output configuration before each log print to achieve different log output effects.

Reference Method

compile 'com.sky.slog:slog:0.3.0'

Example usage

Initialize Slog

Similar code is typically invoked in the onCreate() method of the applied Application class.

Slog.init(new LogcatTree());

At least one log adapter needs to be passed in for initialization, but we can also add multiple adapters, which will be explained later.

These are the simplest ways to initialize, and we can also configure the log output globally at the time of initialization, as follows.

Slog.init(new LogcatTree())     // Initialize, set adapter
    .showThreadInfo(true)       // Information on whether to print logs for threads
    .prefixTag("test")          // Set prefix for global log
    .logPriority(Slog.FULL)     // Set log output level
    .methodCount(2)             // Displays the number of methods on the stack, calculated by default from the method calling the log interface down to stack
    .methodOffset(1)            // Displays the number of offsets calculated down the stack from the method calling the log print interface
    .simpleMode(false);         // Setting simple mode, no format, is equivalent to calling logcat, defaulting to false.

The function and default value of the above methods.

Method Default value Effect
prefixTag "Android" Set the global log prefix.
logPriority Slog.FULL The output level of the log, FULL means that any level of log can be output, NONE means no log can be output.
methodCount 1 Sets the number of methods in the display stack to the final assembled log, calculated by default from the method calling the log interface down to stack.
methodOffset 0 Sets the number of offsets down the stack from the method that calls the log print interface.
showThreadInfo false Information about the thread that sets whether to print the log.
simpleMode false Setting simple mode, no format, no thread information, method calls, is equivalent to calling logcat.

Note that in the table above, if simpleMode is true, methodCount, methodOffset, showThreadInfo will be invalid

We can also modify the global Log output configuration item at any time after we get the Setting object through Slog.getSetting().

Basic Use

Initialize Slog in the following way.

Slog.init(LogcatTree()).perfixTag("TestSlog").showThreadInfo(true);

Print Normal Log

// Print Normal Log
Slog.d("sky debug");
Slog.i("sky info");

// Print Formatted String
Slog.d("this is a format string log, str1 = %s, int value2 = %d, boolean3 = %b", "string1", 2, true);

Print error log

// Print throwable
Slog.e(new Throwable());
Slog.w(new RuntimeException(), "test log with warn priority = %d", Slog.WARN);

Print json and xml

Both json and xml strings use a log level of Debug.

Print json string

String jsonEmpty = "";
String jsonEmpty2 = "{}";
String jsonNull = null;
Slog.json(jsonEmpty);
Slog.json(jsonEmpty2);
Slog.json(jsonNull);

String json = "{'xyy1':[{'test1':'test1'},{'test2':'test2'}],'xyy2':{'test3':'test3','test4':'test4'}}";

Slog.json(json);

String jsonArray =
        "{ 'employees': [ {'firstName':'John', 'lastName':'Doe'}, {'firstName':'Anna', 'lastName':'Smith'}, "
                + "{'firstName':'Peter', 'lastName':'Jones'}]}";
Slog.json(jsonArray);


Print xml string

String androidXml = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
        "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
        "    package=\"com.sky.tools\" >\n" +
        "\n" +
        "    <application\n" +
        "        android:name=\".application.MainApplication\"\n" +
        "        android:allowBackup=\"true\"\n" +
        "        android:icon=\"@mipmap/ic_launcher\"\n" +
        "        android:label=\"@string/app_name\"\n" +
        "        android:roundIcon=\"@mipmap/ic_launcher_round\"\n" +
        "        android:supportsRtl=\"true\"\n" +
        "        android:theme=\"@style/AppTheme\" >\n" +
        "        <activity android:name=\".main.MainActivity\" >\n" +
        "            <intent-filter>\n" +
        "                <action android:name=\"android.intent.action.MAIN\" />\n" +
        "\n" +
        "                <category android:name=\"android.intent.category.LAUNCHER\" />\n" +
        "            </intent-filter>\n" +
        "        </activity>\n" +
        "    </application>\n" +
        "\n" +
        "</manifest>";

Slog.xml(androidXml);

Print Object

Slog supports object printing by adding a corresponding object parser to each different object type, providing parsing for arrays, collections, and so on by default, and supporting custom object parsers.

Print null object

Slog.i(null);
Slog.i("");

Print Array Object

// Global Multidimensional Object Array
private static Object[] objectsArray = new Object[]{
        new boolean[]{false, true, true, false},
        new String[][]{
                new String[]{"22", "23", "24"},
                new String[]{"123", "456", "789"}},
        new int[][][]{new int[][]{
                new int[]{666, 555, 444},
                new int[]{111, 222, 333, 444}},
                new int[][]{
                        new int[]{1, 2, 3, 4, 5, 6},
                        new int[]{7878, 6565, 84155, 7542, 0}}}};


// Print object array
Object[] objectArray = new Object[1024];
for (int i = 0; i < objectArray.length; i++) {
        objectArray[i] = i;
}

Slog.i(objectArray);

// Print String
String[] stringArray = new String[1024];
for (int i = 1024; i < stringArray.length + 1024; i++) {
        stringArray[i - 1024] = "is " + i;
}
Slog.i(stringArray);

// Print an int array
int[] intArray = new int[1024];
for (int i = 2048; i < intArray.length + 2048; i++) {
        intArray[i - 2048] = i;
}
Slog.i(intArray);

// Print multidimensional arrays
Slog.i(objectsArray);

Print Objects for Custom Object Parser

There is the following Student class.

public class Student {
    private int number;
    private int age;
    private String name;
    private boolean isBoy;

    public Student(int number, int age, String name, boolean isBoy){
        this.number = number;
        this.age = age;
        this.name = name;
        this.isBoy = isBoy;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public boolean isBoy() {
        return isBoy;
    }

    public int getNumber() {
        return number;
    }
}

Customize a Student object parser, all of which must implement the Parser interface.

public class StudentParser implements Parser<Student> {
    @Override
    public Class<Student> getParseType() {
        return Student.class;
    }

    @Override
    public String parseToString(Student student) {
        return student.getName() + " is a " + student.getAge() + " years old " + (student.isBoy() ? "boy" : "girl");
    }
}

The parseToString above is called when the object is parsed and returns the string content that the parsed object ultimately represents.

// Before adding parser
Student s = new Student(12345, 54, "sky", true);
Slog.d(s);

// After adding the parser
Slog.addObjectParser(new StudentParser());
Slog.d(s);

Print Collection Object

Here's an example of Map.

// empty map
Map<Integer, Student> map = new HashMap<>();
Slog.d(map);

// int map
Map<Integer, Integer> intMap = new ConcurrentHashMap<>();
intMap.put(1, 2);
intMap.put(1543, 2745867);
intMap.put(17687, 27678);
intMap.put(76781, 27678);
intMap.put(1786768, 26786);
Slog.d(intMap);

// Object Map
Map<Object, String> objectStringMap = new LinkedHashMap<>();
objectStringMap.put(new Object(), "11223786");
objectStringMap.put(new Object(), "475775486");
objectStringMap.put(new Object(), "7856874757");
Slog.d(objectStringMap);

// student Map
map.put(12345, new Student(12345, 54, "sky", true));
map.put(123456, new Student(123456, 56, "sky2", true));
map.put(1234567, new Student(1234567, 15, "sky3", true));
map.put(12345678, new Student(12345678, 25, "sky4", true));
map.put(1234555, new Student(1234555, 35, "sky5", true));
map.put(12345444, new Student(12345444, 45, "sky6", true));
Slog.d(map);

// map itself
Map map1 = new Hashtable<>();
//noinspection CollectionAddedToSelf,unchecked
map1.put(map1, map1);
Slog.d(map1);

Specify temporary configuration items to print logs

The previous examples are logs printed without modifying the output configuration items, but many times we may need to use different log output configurations depending on the scenario.

In addition to modifying the global output configuration after the Setting object has been obtained using the Slog.getSetting() method mentioned earlier, the Slog framework also supports specifying a temporary output configuration, which changes the log output effect only once the next time the current thread prints the log.

This includes the following methods.

Slog.t()        // Specifies the tag for the next time the current thread prints the log, and the Tag combination for the final output log is: prefixTag-tag
Slog.th()       // Specifies whether the next current thread print log displays thread information
Slog.m()        // Specifies the number of methods in the call stack displayed in the next current thread print log
Slog.o()        // Specifies the offset value for the next time the current thread prints the log to display the stack method
Slog.s()        // Specifies whether the next current thread prints logs in simple mode

The above configuration methods can be used individually or in combination.

Single use

Slog.t("custom22").i("set tag to custom");
Slog.th(false).i("hide the threadInfo");
Slog.m(0).i("test 0 method count print, so hide track");
Slog.m(3).i("test three method count println");
Slog.o(1).i("method offset 1");
Slog.s(true).i("set to simple mode");

Joint use

Slog.s(false).t("fiveSetting").th(true).m(5).o(2).i("this time set five temp setting for test");

Add Log Adapter

The Slog framework currently only provides an implemented log adapter, LogcatTree, which supports custom log adapters. All log adapters must inherit the Tree abstract class or its subclasses. To ensure adequate scalability, we can process raw log data in Tree's interface in addition to receiving encapsulated logs.

Customize a FileTree.

public class FileTree extends Tree {

    // ... There are other ways to copy as needed

    // Processing logs of object types, note that this interface method can also be customized based on the original `originalObject` parameter
    @Override
    protected void prepareObjectLog(int priority, String tag, String[] compoundMessages, @Nullable Object originalObject) {
        super.prepareObjectLog(priority, tag, compoundMessages, originalObject);
    }

    // Processing logs of type String, note that this interface method can also be customized based on the original `originalMessage` parameter
    @Override
    protected void prepareStringLog(int priority, String tag, Throwable t, String[] compoundMessages, @Nullable String originalMessages, @Nullable Object... args) {
        super.prepareStringLog(priority, tag, t, compoundMessages, originalMessages, args);
    }

    // This method is a parent abstract method that must be implemented
    @Override
    protected void log(int priority, String tag, String message) {
        // ... Omit the code to save the log to a file
    }
}

Add it to the list of log adapters and you'll be able to use it again.


Slog.plantTree(new FileTree());

Note: In each log adapter, we can finalize on our own what to do with the assembled or original logs as needed.

SlogTest Test Test Case Set

For more usage, you can refer to slog/src/androidTest/java/com.sky.slog/SlogTest.java.

Overview of structure

The basic process for Slog printing logs can be summarized in the following steps.
-Print the log and call the corresponding Slog interface.
- Based on the current log global configuration, determine if the log is required to be output (currently only the log level Priority that allows output).
- Assemble the original log in combination with the global log configuration and the log configuration specified each time (single priority is higher than global).
- Distribute the assembled log and original log data to each log adapter through a log distributor.
- Each log adapter eventually processes the log based on its own implementation.

Simple flowchart.

Simple class diagram.

  • LogAssembler, an abstract class of the log assembler, is responsible for assembling the log and calling the distributor to distribute the assembled log.
  • LogDispatcher, the log distributor interface.
  • LogController, which implements TreeManger and LogDispatcher interfaces respectively, distributes logs to the log adapters it manages through its log distribution function.

Thank

The library was finally formed by referencing the following three libraries. The design of this library draws on their design ideas and code implementation. Thank you very much.

Logger : https://github.com/orhanobut/logger

Timber : https://github.com/JakeWharton/timber

ViseLog : https://github.com/xiaoyaoyou1212/ViseLog

Topics: Android JSON github xml