Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Android: possibility to change severity Level at runtime ? #102

Closed
Nutriz opened this issue May 20, 2019 · 7 comments
Closed

Android: possibility to change severity Level at runtime ? #102

Nutriz opened this issue May 20, 2019 · 7 comments
Labels

Comments

@Nutriz
Copy link

Nutriz commented May 20, 2019

Hello,

I use tinylog 2 in my app. In the preferences I want to allow the user to change the severity level of logs.

I see in the documentation that the configuration is immutable:

The configuration of tinylog is immutable, and as soon as the first log entry is issued, further configuration changes will be ignored

So, what solution can I use to achieve what I want ? A solution is possible ? This one is very important for my application.

@pmwmedia
Copy link
Member

You could write your custom logging provider. It is not difficult.

Here is an example that already almost does what you want: #100 (comment)

@Nutriz
Copy link
Author

Nutriz commented May 20, 2019

Thanks for your quick answer.

In Android I can't access the tinylog.properties at runtime, but I guess I can do that:

Map map = (Map) method.invoke(null);
map.put("level", newLogLevel);
Configuration.replace(map);

But for the part registering provider as a service I don't know how I can manage this on Android.

I don't understand how you define that my custom profider is named "reconfigurable" to use it in the tinylog.properties file.

@pmwmedia
Copy link
Member

In Android I can't access the tinylog.properties at runtime, but I guess I can do that:

    Map map = (Map) method.invoke(null);
    map.put("level", newLogLevel);
    Configuration.replace(map);

Yes, this should work!

But for the part registering provider as a service I don't know how I can manage this on Android.

  1. Ensure that the folders src/main/resources exist in your project
  2. Create the folders META-INF/services
  3. Create a new text file with the name org.tinylog.provider.LoggingProvider
  4. Add the class name org.sample.ReconfigurableLoggingProvider as a line in the new text file

I don't understand how you define that my custom profider is named "reconfigurable" to use it in the tinylog.properties file.

tinylog reads all org.tinylog.provider.LoggingProvider files at startup. So it knows that you have registered org.sample.ReconfigurableLoggingProvider in your file.

reconfigurable is just a shortcut for org.sample.ReconfigurableLoggingProvider. The package org.sample and the interface name LoggingProvider as suffix are optional in tinylog.properties for readability reasons and be be cut off.

The mechanism is explained in detail on the basis of custom writers and policies in https://tinylog.org/v2/extending/ . However, custom logging providers work in the same way.

@Nutriz
Copy link
Author

Nutriz commented May 21, 2019

Hi, thanks a lot for yours quick answers. I'm almost done.

One problem since I use my custom LoggingProvider is that the log format {class-name}.{method} don't work correctly, it's always printing Logger.debug() instead of the real class and method names where the logger is called:

Format mask:

writer.format= {{level}|min-size=5} {[{thread}]|min-size=26} {{class-name}.{method}()|min-size=60}-> {message}

Output with my custom provider:

DEBUG [main]                     Logger.debug()                                              ->  Log test

Output with default profider:

DEBUG [main]                     Activity.onCreate()                                              ->  Log test

Maybe I have missed something, here is my complete custom Provider class:

public class ReconfigurableLoggingProvider implements LoggingProvider {

    private TinylogLoggingProvider realProvider = new TinylogLoggingProvider();
    private String logLevel;

    public void reload(String filePath) throws InterruptedException, ReflectiveOperationException {
        realProvider.shutdown();

        Method method = Configuration.class.getDeclaredMethod("load");
        method.setAccessible(true);
        Map map = (Map) method.invoke(null);
        map.put("level", logLevel.toLowerCase());
        map.put("writer2.file", filePath);
        Configuration.replace(map);
        realProvider = new TinylogLoggingProvider();
    }

    @Override
    public ContextProvider getContextProvider() {
        return realProvider.getContextProvider();
    }

    @Override
    public Level getMinimumLevel() {
        return Level.TRACE;
    }

    @Override
    public Level getMinimumLevel(final String tag) {
        return Level.TRACE;
    }

    @Override
    public boolean isEnabled(final int depth, final String tag, final Level level) {
        return realProvider.isEnabled(depth, tag, level);
    }

    @Override
    public void log(final int depth, final String tag, final Level level, final Throwable exception, final Object obj, final Object... arguments) {
        realProvider.log(depth, tag, level, exception, obj, arguments);
    }

    @Override
    public void log(final String loggerClassName, final String tag, final Level level, final Throwable exception, final Object obj, final Object... arguments) {
        realProvider.log(loggerClassName, tag, level, exception, obj, arguments);

    }

    @Override
    public void shutdown() throws InterruptedException {
        realProvider.shutdown();
    }

    public void setLogLevel(String logLevel) {
        this.logLevel = logLevel;
    }
}

@pmwmedia
Copy link
Member

You can fix the class name issue easily. Just add "1" to the stack trace depth:

@Override
public boolean isEnabled(final int depth, final String tag, final Level level) {
    return realProvider.isEnabled(depth + 1, tag, level);
}

@Override
public void log(final int depth, final String tag, final Level level, final Throwable exception, final Object obj, final Object... arguments) {
    realProvider.log(depth + 1, tag, level, exception, obj, arguments);
}

@Nutriz
Copy link
Author

Nutriz commented May 21, 2019

Auto-answer 😂 :
I looked at the code of TinylogLoggingProvider and I see that we get a snapshot of the stacktrace, and because I have added one level of class hierarchy, I must add one to depth in my custom provider like that:

@Override
    public void log(final int depth, final String tag, final Level level, final Throwable exception, final Object obj, final Object... arguments) {
        realProvider.log(depth /* -->*/  + 1 /*<--*/ , tag, level, exception, obj, arguments);
}

@github-actions
Copy link

github-actions bot commented Oct 9, 2022

This closed issue has been locked automatically. However, please feel free to file a new issue.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Oct 9, 2022
# for free to subscribe to this conversation on GitHub. Already have an account? #.
Labels
Development

No branches or pull requests

2 participants