¿Cómo habilito / deshabilito los niveles de registro en Android?

Estoy teniendo muchas declaraciones de registro para depurar, por ejemplo.

Log.v(TAG, "Message here"); Log.w(TAG, " WARNING HERE"); 

Al implementar esta aplicación en el teléfono del dispositivo, quiero desactivar el registro detallado desde donde puedo activar / desactivar el registro.

Una forma común es crear un nivel de registro llamado int, y definir su nivel de depuración en función del nivel de registro.

 public static int LOGLEVEL = 2; public static boolean ERROR = LOGLEVEL > 0; public static boolean WARN = LOGLEVEL > 1; ... public static boolean VERBOSE = LOGLEVEL > 4; if (VERBOSE) Log.v(TAG, "Message here"); // Won't be shown if (WARN) Log.w(TAG, "WARNING HERE"); // Still goes through 

Más tarde, puede simplemente cambiar el LOGLEVEL para todos los niveles de salida de depuración.

La documentación de Android dice lo siguiente sobre niveles de registro :

Verbose nunca se debe comstackr en una aplicación, excepto durante el desarrollo. Los registros de depuración se comstackn pero se eliminan en tiempo de ejecución. Los registros de error, advertencia e información siempre se guardan.

Por lo tanto, es posible que desee considerar desmantelar el registro Declaraciones de registro detallado, posiblemente utilizando ProGuard como se sugiere en otra respuesta .

De acuerdo con la documentación, puede configurar el registro en un dispositivo de desarrollo usando Propiedades del sistema. La propiedad para establecer es log.tag. y debe establecerse en uno de los siguientes valores: VERBOSE , DEBUG , INFO , WARN , ERROR , ASSERT o SUPPRESS . Más información sobre esto está disponible en la documentación para el método isLoggable() .

Puede establecer propiedades temporalmente utilizando el comando setprop . Por ejemplo:

 C:\android>adb shell setprop log.tag.MyAppTag WARN C:\android>adb shell getprop log.tag.MyAppTag WARN 

Alternativamente, puede especificarlos en el archivo ‘/data/local.prop’ de la siguiente manera:

 log.tag.MyAppTag=WARN 

Las versiones posteriores de Android parecen requerir que /data/local.prop sea de solo lectura . Este archivo se lee en el momento del arranque, por lo que deberá reiniciar después de actualizarlo. Si /data/local.prop es escribible en todo el mundo, es probable que se ignore.

Finalmente, puede configurarlos mediante progtwigción utilizando el método System.setProperty() .

La forma más fácil es, probablemente, ejecutar su JAR comstackdo a través de ProGuard antes de la implementación, con una configuración como:

 -assumenosideeffects class android.util.Log { public static int v(...); } 

Eso, aparte de todas las demás optimizaciones de ProGuard, eliminará cualquier statement de registro detallado directamente desde el bytecode.

Tomé una ruta simple, creando una clase contenedora que también hace uso de listas de parámetros variables.

  public class Log{ public static int LEVEL = android.util.Log.WARN; static public void d(String tag, String msgFormat, Object...args) { if (LEVEL< =android.util.Log.DEBUG) { android.util.Log.d(tag, String.format(msgFormat, args)); } } static public void d(String tag, Throwable t, String msgFormat, Object...args) { if (LEVEL<=android.util.Log.DEBUG) { android.util.Log.d(tag, String.format(msgFormat, args), t); } } //...other level logging functions snipped 

La mejor manera es usar SLF4J API + parte de su implementación.

Para aplicaciones de Android puede usar lo siguiente:

  1. Android Logger es la implementación SLF4J ligera pero fácil de configurar (<50 Kb).
  2. LOGBack es la implementación más potente y optimizada, pero su tamaño es de aproximadamente 1 Mb.
  3. Cualquier otro por su gusto: slf4j-android, slf4android.

Deberías usar

  if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "my log message"); } 

Eliminar el registro con proguard (ver respuesta de @Christopher) fue fácil y rápido, pero causó que los rastros de stack de producción no coincidan con el origen si hubo algún registro de depuración en el archivo.

En cambio, esta es una técnica que usa diferentes niveles de registro en desarrollo vs. producción, asumiendo que proguard se usa solo en producción. Reconoce la producción al ver si proguard ha renombrado un nombre de clase dado (en el ejemplo, yo uso “com.foo.Bar” – usted reemplazaría esto con un nombre de clase completamente calificado que sabe que será renombrado por proguard).

Esta técnica hace uso del registro de commons.

 private void initLogging() { Level level = Level.WARNING; try { // in production, the shrinker/obfuscator proguard will change the // name of this class (and many others) so in development, this // class WILL exist as named, and we will have debug level Class.forName("com.foo.Bar"); level = Level.FINE; } catch (Throwable t) { // no problem, we are in production mode } Handler[] handlers = Logger.getLogger("").getHandlers(); for (Handler handler : handlers) { Log.d("log init", "handler: " + handler.getClass().getName()); handler.setLevel(level); } } 

Log4j o slf4j también se pueden usar como marcos de registro en Android junto con logcat. Vea el proyecto android-logging-log4j o log4j support en android

Hay un pequeño reemplazo directo para la clase de registro estándar de Android – https://github.com/zserge/log

Básicamente, todo lo que tiene que hacer es reemplazar importaciones de android.util.Log a trikita.log.Log . Luego, en su Application.onCreate() o en algún initalizer estático, compruebe si BuilConfig.DEBUG o cualquier otro indicador y use Log.level(Log.D) o Log.level(Log.E) para cambiar el nivel de registro mínimo. Puede usar Log.useLog(false) para deshabilitar el registro.

Puede ser que pueda ver esta clase de extensión de registro: https://github.com/dbauduin/Android-Tools/tree/master/logs .

Le permite tener un control preciso en los registros. Por ejemplo, puede deshabilitar todos los registros o solo los registros de algunos paquetes o clases.

Además, agrega algunas funcionalidades útiles (por ejemplo, no tiene que pasar una etiqueta para cada registro).

Creé un Utility / Wrapper que resuelve este problema + otros problemas comunes alrededor de Logging.

Una utilidad de depuración con las siguientes características:

  • Las características habituales proporcionadas por la clase Log envueltas por LogMode s.
  • Registro de entrada-salida de método: se puede desactivar con un interruptor
  • Depuración selectiva: depura clases específicas.
  • Método de ejecución-Medición del tiempo: Mida el tiempo de ejecución de los métodos individuales, así como el tiempo colectivo empleado en todos los métodos de una clase.

¿Cómo utilizar?

  • Incluye la clase en tu proyecto.
  • Úselo como si usara los métodos android.util.Log, para empezar.
  • Utilice la función de registros de entrada y salida haciendo llamadas a los métodos entry_log () – exit_log () al comienzo y al final de los métodos en su aplicación.

He intentado hacer que la documentación sea autosuficiente.

Sugerencias para mejorar esta Utilidad son bienvenidas.

Gratis para usar / compartir.

Descárguelo de GitHub .

Aquí hay una solución más compleja. Obtendrá traza de stack completa y se llamará al método toString () solo si es necesario (Rendimiento). El atributo BuildConfig.DEBUG será falso en el modo de producción, por lo que se eliminarán todos los registros de rastreo y depuración. El comstackdor de puntos calientes tiene la posibilidad de eliminar las llamadas debido a las propiedades estáticas finales.

 import java.io.ByteArrayOutputStream; import java.io.PrintStream; import android.util.Log; public class Logger { public enum Level { error, warn, info, debug, trace } private static final String DEFAULT_TAG = "Project"; private static final Level CURRENT_LEVEL = BuildConfig.DEBUG ? Level.trace : Level.info; private static boolean isEnabled(Level l) { return CURRENT_LEVEL.compareTo(l) >= 0; } static { Log.i(DEFAULT_TAG, "log level: " + CURRENT_LEVEL.name()); } private String classname = DEFAULT_TAG; public void setClassName(Class< ?> c) { classname = c.getSimpleName(); } public String getClassname() { return classname; } public boolean isError() { return isEnabled(Level.error); } public boolean isWarn() { return isEnabled(Level.warn); } public boolean isInfo() { return isEnabled(Level.info); } public boolean isDebug() { return isEnabled(Level.debug); } public boolean isTrace() { return isEnabled(Level.trace); } public void error(Object... args) { if (isError()) Log.e(buildTag(), build(args)); } public void warn(Object... args) { if (isWarn()) Log.w(buildTag(), build(args)); } public void info(Object... args) { if (isInfo()) Log.i(buildTag(), build(args)); } public void debug(Object... args) { if (isDebug()) Log.d(buildTag(), build(args)); } public void trace(Object... args) { if (isTrace()) Log.v(buildTag(), build(args)); } public void error(String msg, Throwable t) { if (isError()) error(buildTag(), msg, stackToString(t)); } public void warn(String msg, Throwable t) { if (isWarn()) warn(buildTag(), msg, stackToString(t)); } public void info(String msg, Throwable t) { if (isInfo()) info(buildTag(), msg, stackToString(t)); } public void debug(String msg, Throwable t) { if (isDebug()) debug(buildTag(), msg, stackToString(t)); } public void trace(String msg, Throwable t) { if (isTrace()) trace(buildTag(), msg, stackToString(t)); } private String buildTag() { String tag ; if (BuildConfig.DEBUG) { StringBuilder b = new StringBuilder(20); b.append(getClassname()); StackTraceElement stackEntry = Thread.currentThread().getStackTrace()[4]; if (stackEntry != null) { b.append('.'); b.append(stackEntry.getMethodName()); b.append(':'); b.append(stackEntry.getLineNumber()); } tag = b.toString(); } else { tag = DEFAULT_TAG; } } private String build(Object... args) { if (args == null) { return "null"; } else { StringBuilder b = new StringBuilder(args.length * 10); for (Object arg : args) { if (arg == null) { b.append("null"); } else { b.append(arg); } } return b.toString(); } } private String stackToString(Throwable t) { ByteArrayOutputStream baos = new ByteArrayOutputStream(500); baos.toString(); t.printStackTrace(new PrintStream(baos)); return baos.toString(); } } 

usar de esta manera:

 Loggor log = new Logger(); Map foo = ... List bar = ... log.error("Foo:", foo, "bar:", bar); // bad example (avoid something like this) // log.error("Foo:" + " foo.toString() + "bar:" + bar); 

En un escenario de registro muy simple, donde literalmente intenta escribir en la consola durante el desarrollo para fines de depuración, puede ser más fácil realizar una búsqueda y reemplazar antes de su comstackción de producción y comentar todas las llamadas a Log o System. out.println.

Por ejemplo, suponiendo que no usaste el “Registro”. en cualquier lugar fuera de una llamada a Log.d o Log.e, etc., simplemente podría buscar y reemplazar toda la solución para reemplazar “Log”. con “// Log” para comentar todas sus llamadas de registro, o en mi caso solo estoy usando System.out.println en todas partes, así que antes de pasar a producción simplemente haré una búsqueda completa y lo reemplazaré por “System.out.println” y lo reemplazaré por “//System.out.println”.

Sé que esto no es ideal, y sería bueno si la capacidad de buscar y comentar llamadas a Log y System.out.println estuviese integrada en Eclipse, pero hasta que eso suceda la forma más fácil, rápida y mejor de hacerlo es comentar por búsqueda y reemplazo. Si hace esto, no tiene que preocuparse por no coincidir con los números de línea de seguimiento de stack, porque está editando su código fuente y no está agregando ninguna sobrecarga al verificar alguna configuración de nivel de registro, etc.

En mis aplicaciones, tengo una clase que envuelve la clase Log que tiene una var booleana estática llamada “estado”. A lo largo de mi código verifico el valor de la variable “estado” usando un método estático antes de escribir en el registro. Luego tengo un método estático para establecer la variable “estado”, que garantiza que el valor sea común en todas las instancias creadas por la aplicación. Esto significa que puedo habilitar o deshabilitar todo el registro para la aplicación en una sola llamada, incluso cuando la aplicación se está ejecutando. Útil para llamadas de soporte … Significa que tienes que aferrarte a tus armas cuando estás depurando y no retroceder a usar la clase Log estándar aunque …

También es útil (conveniente) que Java interprete una variable booleana como falsa si no se le ha asignado un valor, lo que significa que puede dejarse como falsa hasta que tenga que activar el registro 🙂

Podemos usar la clase Log en nuestro componente local y defina los métodos como v / i / e / d. En función de la necesidad de que podamos hacer una llamada más.
ejemplo se muestra a continuación.

  public class Log{ private static boolean TAG = false; public static void d(String enable_tag, String message,Object...args){ if(TAG) android.util.Log.d(enable_tag, message+args); } public static void e(String enable_tag, String message,Object...args){ if(TAG) android.util.Log.e(enable_tag, message+args); } public static void v(String enable_tag, String message,Object...args){ if(TAG) android.util.Log.v(enable_tag, message+args); } } if we do not need any print(s), at-all make TAG as false for all else remove the check for type of Log (say Log.d). as public static void i(String enable_tag, String message,Object...args){ // if(TAG) android.util.Log.i(enable_tag, message+args); } 

aquí el mensaje es para string y args es el valor que desea imprimir.

Para mí, a menudo es útil poder establecer diferentes niveles de registro para cada etiqueta.

Estoy usando esta clase de contenedor muy simple:

 public class Log2 { public enum LogLevels { VERBOSE(android.util.Log.VERBOSE), DEBUG(android.util.Log.DEBUG), INFO(android.util.Log.INFO), WARN( android.util.Log.WARN), ERROR(android.util.Log.ERROR); int level; private LogLevels(int logLevel) { level = logLevel; } public int getLevel() { return level; } }; static private HashMap logLevels = new HashMap(); public static void setLogLevel(String tag, LogLevels level) { logLevels.put(tag, level.getLevel()); } public static int v(String tag, String msg) { return Log2.v(tag, msg, null); } public static int v(String tag, String msg, Throwable tr) { if (logLevels.containsKey(tag)) { if (logLevels.get(tag) > android.util.Log.VERBOSE) { return -1; } } return Log.v(tag, msg, tr); } public static int d(String tag, String msg) { return Log2.d(tag, msg, null); } public static int d(String tag, String msg, Throwable tr) { if (logLevels.containsKey(tag)) { if (logLevels.get(tag) > android.util.Log.DEBUG) { return -1; } } return Log.d(tag, msg); } public static int i(String tag, String msg) { return Log2.i(tag, msg, null); } public static int i(String tag, String msg, Throwable tr) { if (logLevels.containsKey(tag)) { if (logLevels.get(tag) > android.util.Log.INFO) { return -1; } } return Log.i(tag, msg); } public static int w(String tag, String msg) { return Log2.w(tag, msg, null); } public static int w(String tag, String msg, Throwable tr) { if (logLevels.containsKey(tag)) { if (logLevels.get(tag) > android.util.Log.WARN) { return -1; } } return Log.w(tag, msg, tr); } public static int e(String tag, String msg) { return Log2.e(tag, msg, null); } public static int e(String tag, String msg, Throwable tr) { if (logLevels.containsKey(tag)) { if (logLevels.get(tag) > android.util.Log.ERROR) { return -1; } } return Log.e(tag, msg, tr); } } 

Ahora solo establece el nivel de registro por TAG al comienzo de cada clase:

 Log2.setLogLevel(TAG, LogLevels.INFO);