【问题标题】:How to configure java.util.logging on Android?如何在 Android 上配置 java.util.logging?
【发布时间】:2010-12-30 09:11:43
【问题描述】:

我想在 Android 上使用 java.util.logging。我想用 logging.properties 配置日志系统。但是我怎样才能告诉 Android 使用特定的配置文件呢?例如,我将 logging.properties 放在应用程序的类路径根目录中。 Android 如何知道 logging.properties 的位置。

谢谢

【问题讨论】:

    标签: android logging


    【解决方案1】:

    现在这是我的一个项目的常见问题解答,希望更多人会在这里找到它:java.util.logging 在 Android 上运行良好。请不要在代码中使用任何其他内容,日志框架就像 Java 世界中的害虫。

    Android 附带的默认日志处理程序被破坏了,它会忽略任何级别高于 INFO 的日志消息。您看不到 DEBUG 等消息。

    原因是AndroidHandler.java中对Log.isLoggable()的调用:

    https://github.com/android/platform_frameworks_base/blob/master/core/java/com/android/internal/logging/AndroidHandler.java

    以下是解决方法:

    import android.util.Log;
    import java.util.logging.*;
    
    /**
     * Make JUL work on Android.
     */
    public class AndroidLoggingHandler extends Handler {
    
        public static void reset(Handler rootHandler) {
            Logger rootLogger = LogManager.getLogManager().getLogger("");
            Handler[] handlers = rootLogger.getHandlers();
            for (Handler handler : handlers) {
                rootLogger.removeHandler(handler);
            }
            rootLogger.addHandler(rootHandler);
        }
    
        @Override
        public void close() {
        }
    
        @Override
        public void flush() {
        }
    
        @Override
        public void publish(LogRecord record) {
            if (!super.isLoggable(record))
                return;
    
            String name = record.getLoggerName();
            int maxLength = 30;
            String tag = name.length() > maxLength ? name.substring(name.length() - maxLength) : name;
    
            try {
                int level = getAndroidLevel(record.getLevel());
                Log.println(level, tag, record.getMessage());
                if (record.getThrown() != null) {
                    Log.println(level, tag, Log.getStackTraceString(record.getThrown()));
                }
            } catch (RuntimeException e) {
                Log.e("AndroidLoggingHandler", "Error logging message.", e);
            }
        }
    
        static int getAndroidLevel(Level level) {
            int value = level.intValue();
    
            if (value >= Level.SEVERE.intValue()) {
                return Log.ERROR;
            } else if (value >= Level.WARNING.intValue()) {
                return Log.WARN;
            } else if (value >= Level.INFO.intValue()) {
                return Log.INFO;
            } else {
                return Log.DEBUG;
            }
        }
    }
    

    在您的应用程序的主要活动/初始化代码中:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    
        AndroidLoggingHandler.reset(new AndroidLoggingHandler());
        java.util.logging.Logger.getLogger("my.category").setLevel(Level.FINEST);
    ...
    

    TL;DR: 是的,你可以使用一些神奇的属性,或 adb shell 命令,甚至了解愚蠢的内置日志处理程序的 DalvikLogging.loggerNameToTag 如何将类别名称转换为标签(你必须为那些神奇的属性和shell命令做),但为什么要麻烦呢?伐木还不够痛苦吗?

    【讨论】:

    • 通过Android生命周期,调用这个处理程序的最佳位置是从Application的子类中调用onCreate(),并在manifest文件中声明这个类。因为如果由于某种原因主 Activity 没有出现(来自共享、服务等),代码仍然运行。
    • @Renascienza 不,你不能。因为在应用程序开始时您将设置级别 Level.FINEST ,但是当您第一次调用 MyClass 的实例时 private static final Logger logger 它将具有级别 == Level.INFO
    【解决方案2】:

    一般使用android.util.Log 登录Android。使用该记录器有一些关键优势,例如能够使用adb logcat 查看发送到这些日志的日志输出。

    您可以尝试将 logging.properties 放入 assets/res/raw/。如果 Android 没有在那里选择这些,那么可以使用java.util.logging.LogManager.readConfiguration(java.io.InputStream) 强制加载它。 (您可以使用 Resources 和 AssetManager 类将文件作为 InputStream 获取)。

    【讨论】:

    • 非常感谢。这种方法效果很好。现在我不得不努力将日志写入 sdcard。
    • @tangjie 不确定您的最终目标是什么,但我会考虑在您的应用中缓冲日志,然后将它们发送到您实际有权访问的服务器
    【解决方案3】:

    至少在 Android 4.3 上,如果你使用 Handler 并没有改变

    adb shell setprop log.tag.YOURTAG DEBUG
    

    您可以在 logcat 中查看使用 Level.FINE 记录的消息。我还没有找到记录更高级别的方法,但这对我来说已经足够了。

    【讨论】:

      【解决方案4】:

      如果你们中的一个人只想从第三方库路由 java.util.logging 输出,这可以通过 SLF4J 及其jul-to-slf4j bridge 轻松实现。这也适用于FINEFINEST 日志语句,顺便说一句。

      这里是maven依赖

      <dependency>
          <groupId>org.slf4j</groupId>
          <artifactId>jul-to-slf4j</artifactId>
          <version>${slf4j.version}</version>
      </dependency>
      

      要引导它,请将以下内容放入您的 Application 类、Roboguice 模块或在您的第一个日志语句之前执行的其他位置。 (不幸的是,在 assets/logging.properties 中配置这个似乎不起作用)。

      /*
       * add SLF4JBridgeHandler to j.u.l's root logger, should be done once
       * during the initialization phase of your application
       */
      SLF4JBridgeHandler.install();
      

      然后你可以

      • 配置来自assets/logback.xml 的所有日志语句(使用logback-android)。有关日志级别的映射,请参阅 here

      • 或者只是使用SLF4J-android 将日志语句转发到logcat。为此,只需将以下依赖项放入您的类路径,无需进一步配置:

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-android</artifactId>
            <version>${slf4j.version}</version>
        </dependency>
        

      我使用

      成功实现了这个
      <properties>
          <slf4j.version>1.7.6</slf4j.version>
      </properties>
      

      注意

      • 请仔细阅读jul-to-slf4j文档中与性能相关的部分!
      • 如果您使用LogcatAppender,请务必记住JUL 日志消息(除了级别比INFO 更精细的消息)由android 路由到logcat。因此,请务必应用适当的过滤器以避免重复。

      【讨论】:

      • 它可以工作,但对我来说,比 INFO 更精细的日志消息没有显示在 adb logcat (slf4j 1.7.21) 中。
      【解决方案5】:

      这里修改answer of Christian Bauer

      如果在任何第三方库中的某个地方,您会看到如下内容:

         final public class AnyClass {        
              private static final Logger logger = Logger.getLogger(AnyClass.class.getName());
               ...
                  if (logger.isLoggable(Level.FINE)) {
                      logger.fine("any log event");
                  }
               ...
           }
      

      在创建AnyClass.class 的第一个实例后添加到您的代码中:

      SetupLogger.setup(Arrays.asList(
                      new Pair<>(AnyClass.class.getName(), Level.FINEST),//AnyClass.class.getName() is because instance `logger` has his name
                      new Pair<>(AnyOther.class.getName(), Level.FINEST)
              ));
      

      SetupLogger .class

      public class SetupLogger {
      
          static public void setup(List<Pair<String, Level>> loggerNames) {
              // suppress the logging output to the console
              AndroidLoggingHandler rootHandler = new AndroidLoggingHandler();
              AndroidLoggingHandler.reset(rootHandler );
              for (Pair<String, Level> pair : loggerNames) {
                  Logger logger = Logger.getLogger(pair.first);
                  logger.setLevel(pair.second);
              }
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-09-15
        • 1970-01-01
        • 2014-06-27
        • 1970-01-01
        • 2015-07-31
        • 1970-01-01
        相关资源
        最近更新 更多