¿Cómo eliminar todas las llamadas de registro de depuración antes de crear la versión de lanzamiento de una aplicación de Android?

397

Según Google, debo " desactivar las llamadas a los métodos de registro en el código fuente " antes de publicar mi aplicación de Android en Google Play. Extracto de la sección 3 de la lista de verificación de publicación :

Asegúrese de desactivar el registro y deshabilitar la opción de depuración antes de compilar su aplicación para su lanzamiento. Puede desactivar el registro eliminando llamadas a los métodos de registro en sus archivos de origen.

Mi proyecto de código abierto es grande y es difícil hacerlo manualmente cada vez que lo lanzo. Además, eliminar una línea de registro es potencialmente complicado, por ejemplo:

if(condition)
  Log.d(LOG_TAG, "Something");
data.load();
data.show();

Si comento la línea de registro, la condición se aplica a la siguiente línea y es probable que no se llame a load (). ¿Son tales situaciones tan raras que puedo decidir que no debería existir?

Entonces, ¿hay una mejor manera de hacerlo a nivel de código fuente? ¿O tal vez una sintaxis inteligente de ProGuard para eliminar de manera eficiente pero segura todas las líneas de registro?

Nicolas Raoul
fuente
2
+1 porque no recordaba que esto estaba en la lista de verificación de publicaciones.
rds
51
Para comentar una línea no bloqueada, uso "; //" en lugar de "//".
gritó el
Si necesita poder deshacer esto, probablemente quiera usarlo sed 's_^\(\s*Log\.\)_;//'`date|tr -s \ -`'\1_g'en su lugar.
gritó el
2
El enlace que Dimitar agregó ya no funciona. En cambio, encontré esto source.android.com/source/code-style.html#log-sparingly .
JosephL
1
@mboy: Probablemente para el rendimiento principalmente hoy en día, pero en las versiones antiguas de Android también tiene beneficios de seguridad.
Nicolas Raoul

Respuestas:

488

Creo que una solución mucho más fácil es olvidar todos los ifcontroles por todas partes y simplemente usar ProGuard para eliminar cualquier llamada Log.d()o Log.v()método cuando llamamos a nuestro releaseobjetivo Ant .

De esa manera, siempre tenemos la información de depuración que se genera para las compilaciones regulares y no tenemos que hacer ningún cambio de código para las compilaciones de lanzamiento. ProGuard también puede hacer múltiples pases sobre el código de bytes para eliminar otras declaraciones no deseadas, bloques vacíos y puede incorporar automáticamente métodos cortos cuando sea apropiado.

Por ejemplo, aquí hay una configuración ProGuard muy básica para Android:

-dontskipnonpubliclibraryclasses
-dontobfuscate
-forceprocessing
-optimizationpasses 5

-keep class * extends android.app.Activity
-assumenosideeffects class android.util.Log {
    public static *** d(...);
    public static *** v(...);
}

Así que guardaría eso en un archivo, luego llamaría a ProGuard desde Ant, pasando su JAR recién compilado y el JAR de la plataforma Android que está utilizando.

Vea también los ejemplos en el manual de ProGuard.


Actualización (4,5 años después): hoy en día utilizo Timber para el registro de Android.

No solo es un poco mejor que la Logimplementación predeterminada : la etiqueta de registro se configura automáticamente y es fácil registrar cadenas formadas y excepciones, sino que también puede especificar diferentes comportamientos de registro en tiempo de ejecución.

En este ejemplo, las declaraciones de registro solo se escribirán en logcat en las versiones de depuración de mi aplicación:

La madera está configurada en mi Application onCreate()método:

if (BuildConfig.DEBUG) {
  Timber.plant(new Timber.DebugTree());
}

Luego, en cualquier otro lugar de mi código, puedo iniciar sesión fácilmente:

Timber.d("Downloading URL: %s", url);
try {
  // ...
} catch (IOException ioe) {
  Timber.e(ioe, "Bad things happened!");
}

Consulte la aplicación de ejemplo Timber para ver un ejemplo más avanzado, donde todas las declaraciones de registro se envían a logcat durante el desarrollo y, en producción, no se registran declaraciones de depuración, pero los errores se informan silenciosamente a Crashlytics.

Christopher Orr
fuente
59
¿Y por qué no está eso en el archivo de protección predeterminado?
rds
10
+ rds ya que hará que la producción apile los números de línea diferentes a los de su código, ya que las líneas se eliminan.
Chico
55
Puedo confirmar que eliminar las llamadas de registro desplazará los números de línea en los seguimientos de pila. No siempre estará fuera de sincronización (hice varias pruebas rápidas, pero no puedo determinar exactamente cuál es la causa, posiblemente si concatena una cadena en la llamada de registro), pero a veces estarán algunas líneas apagadas. Vale la pena el problema de la OMI por la capacidad de eliminar fácilmente las llamadas de registro.
Tony Chan
55
@Fraggle De proguard-android.txt en las herramientas ADT: "Tenga en cuenta que si desea habilitar la optimización, no puede simplemente incluir indicadores de optimización en su propio archivo de configuración del proyecto; en su lugar, deberá apuntar a" proguard-android-optimizar. txt "en lugar de este archivo" # project.properties.
Raanan
3
Como dijo espinchi en la respuesta a continuación. "El único problema con este enfoque es que, si hace Log.d (" etiqueta "," Procesado: "+ new ItemCounter (blabla) +" items "), incluso si este mensaje de registro no aparece en su versión publicada, Se utiliza un StringBuilder para crear el mensaje, que podría ser costoso de crear. "¿Es esto cierto también en el caso de Timber?
Chitrang
117

Todas son buenas respuestas, pero cuando terminé con mi desarrollo, no quería usar declaraciones if en todas las llamadas de Log, ni quería usar herramientas externas.

Entonces, la solución que estoy usando es reemplazar la clase android.util.Log con mi propia clase Log:

public class Log {
    static final boolean LOG = BuildConfig.DEBUG;

    public static void i(String tag, String string) {
        if (LOG) android.util.Log.i(tag, string);
    }
    public static void e(String tag, String string) {
        if (LOG) android.util.Log.e(tag, string);
    }
    public static void d(String tag, String string) {
        if (LOG) android.util.Log.d(tag, string);
    }
    public static void v(String tag, String string) {
        if (LOG) android.util.Log.v(tag, string);
    }
    public static void w(String tag, String string) {
        if (LOG) android.util.Log.w(tag, string);
    }
}

Lo único que tenía que hacer en todos los archivos fuente era reemplazar la importación de android.util.Log con mi propia clase.

Reiner
fuente
143
El único problema con este enfoque es que, si hace Log.d ("etiqueta", "Procesado:" + nuevo ItemCounter (blabla) + "items"), incluso si este mensaje de registro no aparece en su versión publicada, un StringBuilder se usa para crear el mensaje, lo que podría ser costoso de crear.
espinchi
99
Esta solución tiene un gran problema. Espinchi mencionó solo la punta del iceberg. El problema es que cuando se llama Log.d("tag", someValue.toString());, es muy fácil olvidarse de comprobar algún valor por no ser nulo, lo que significa que podría arrojar un NullPointerExceptionproducto en producción. Sugiere una solución segura pero te engañará. Nos private static boolean DEBUGif(DEBUG)Log.d(TAG, msg);
usamos
2
@espinchi Su preocupación parece aplicarse a todas las bibliotecas de registro como se describe en esta respuesta stackoverflow.com/a/15452492/433718 (Slf4j, backlog, ...). ¿No se sugiere usarlos?
OneWorld
1
La única forma de minimizar los gastos generales mencionados en el primer comentario de @espinchi es cambiar los métodos de registro para aceptar varargs en lugar de String. La solución completa se describe aquí . Aparentemente, esto tiene otro inconveniente: cada llamada debe editarse (no solo una línea de importación).
Stan
21
Solo para su información, si está usando Android Studio y el sistema de construcción Gradle, puede usar static final boolean LOG = BuildConfig.DEBUGy no tener que modificar este archivo nunca.
ashishduh
61

Sugiero tener un booleano estático en algún lugar que indique si iniciar sesión o no:

clase MyDebug {
  LOG booleano final estático = verdadero;
}

Luego, donde quiera iniciar sesión en su código, simplemente haga esto:

if (MyDebug.LOG) {
  if (condición) Log.i (...);
}

Ahora, cuando configura MyDebug.LOG en falso, el compilador eliminará todo el código dentro de tales comprobaciones (dado que es un final estático, sabe en tiempo de compilación que el código no se usa).

Para proyectos más grandes, es posible que desee comenzar a tener booleanos en archivos individuales para poder habilitar o deshabilitar fácilmente el registro allí según sea necesario. Por ejemplo, estas son las diferentes constantes de registro que tenemos en el administrador de ventanas:

static final String TAG = "WindowManager";
static final boolean DEBUG = false;
static final boolean DEBUG_FOCUS = false;
static final boolean DEBUG_ANIM = false;
static final boolean DEBUG_LAYOUT = false;
static final boolean DEBUG_RESIZE = false;
static final boolean DEBUG_LAYERS = false;
static final boolean DEBUG_INPUT = false;
static final boolean DEBUG_INPUT_METHOD = false;
static final boolean DEBUG_VISIBILITY = false;
static final boolean DEBUG_WINDOW_MOVEMENT = false;
static final boolean DEBUG_ORIENTATION = false;
static final boolean DEBUG_APP_TRANSITIONS = false;
static final boolean DEBUG_STARTING_WINDOW = false;
static final boolean DEBUG_REORDER = false;
static final boolean DEBUG_WALLPAPER = false;
static final boolean SHOW_TRANSACTIONS = false;
static final boolean HIDE_STACK_CRAWLS = true;
static final boolean MEASURE_LATENCY = false;

Con el código correspondiente como:

    if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT) Log.v(
        TAG, "Adding window " + window + " at "
        + (i+1) + " of " + mWindows.size() + " (after " + pos + ")");
hackbod
fuente
1
Yo votaría por ese enfoque también. También se utilizó en la muestra de facturación oficial de Google en la aplicación.
LA_
44
¿No sería menos detallado pasar la condición como primer parámetro?
Snicolas
1
Esta parece ser la mejor solución, aunque requiere un código adicional en cada declaración de registro: se conservan los números de línea (debilidad del enfoque ProGuard), no se ejecuta ningún código para crear un mensaje de registro ( debilidad del enfoque de clase envolvente y aparentemente del enfoque de la biblioteca de registro también) . El uso de este enfoque en Google en la muestra de facturación de aplicaciones según @LA_ también apoya mis pensamientos.
OneWorld
2
@Snicolas ¿Cómo puede pasar la condición como primer parámetro sin implementar un contenedor? Además, si lo agrega como parámetro, antes de ingresar el método, todos los parámetros deben evaluarse, es decir, también la cadena del mensaje. La condición necesita ser probada antes de construir los parámetros. La solución propuesta es posiblemente la mejor sin una herramienta externa.
type-a1pha
2
Código binario sabio, esto es lo mejor. Pero una codificación como esta solo supone un gran esfuerzo para una salida de registro de depuración simple. La legibilidad del código cae significativamente. Gana un poco, pierde un poco, supongo ...
Richard Le Mesurier
30

La solución Proguard de Christopher es la mejor, pero si por alguna razón no le gusta Proguard, aquí hay una solución de muy baja tecnología:

Registros de comentarios:

find . -name "*\.java" | xargs grep -l 'Log\.' | xargs sed -i 's/Log\./;\/\/ Log\./g'

Registros sin comentar:

find . -name "*\.java" | xargs grep -l 'Log\.' | xargs sed -i 's/;\/\/ Log\./Log\./g'

Una restricción es que sus instrucciones de registro no deben abarcar varias líneas.

(Ejecute estas líneas en un shell de UNIX en la raíz de su proyecto. Si usa Windows, obtenga una capa UNIX o use comandos equivalentes de Windows)

Nicolas Raoul
fuente
1
necesita un "" después de -i en Sed si se ejecuta en Mac (según esto ) Gracias.
Vishal
Siento que esto podría ser lo que termino usando para algo en lo que estoy trabajando porque no tuve mucha suerte haciéndolo con Proguard en absoluto
Joe Plante
¿Y qué pasa si tiene un registro después de una rama mientras no está entre corchetes, como sugirió en su primera publicación?
type-a1pha
@ type-a1pha: si adopta esta solución, debe considerar los bloques de paréntesis como obligatorios.
Nicolas Raoul
2
@NicolasRaoul El punto y coma soluciona este problema ( //vs. ;//)
Alex Gittemeier
18

Me gustaría agregar algunas precisiones sobre el uso de Proguard con Android Studio y gradle, ya que tuve muchos problemas para eliminar las líneas de registro del binario final.

Para poder hacer assumenosideeffects trabajos en Proguard, hay un requisito previo.

En su archivo gradle, debe especificar el uso del proguard-android-optimize.txtarchivo predeterminado.

buildTypes {
    release {
        minifyEnabled true
        proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'

        // With the file below, it does not work!
        //proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    }
}

En realidad, en el proguard-android.txtarchivo predeterminado , la optimización está deshabilitada con las dos banderas:

-dontoptimize
-dontpreverify

El proguard-android-optimize.txtarchivo no agrega esas líneas, por lo que ahora assumenosideeffectspuede funcionar.

Luego, personalmente, uso SLF4J , aún más cuando desarrollo algunas bibliotecas que se distribuyen a otras. La ventaja es que por defecto no hay salida. Y si el integrador quiere algunas salidas de registro, puede usar Logback para Android y activar los registros, por lo que los registros se pueden redirigir a un archivo o a LogCat.

Si realmente necesito quitar los registros de la biblioteca final, entonces agrego a mi archivo Proguard (después de haber habilitado el proguard-android-optimize.txtarchivo, por supuesto):

-assumenosideeffects class * implements org.slf4j.Logger {
    public *** trace(...);
    public *** debug(...);
    public *** info(...);
    public *** warn(...);
    public *** error(...);
}
Vincent Hiribarren
fuente
Esto no funciona con el nuevo compilador de
Jack--
Esto me ayudo; ¡ se necesitaban tanto el proguard-android-optimize.txtarchivo Proguard predeterminado como el archivo -assumenosideeffectsProguard personalizado! Estoy usando R8 shinker (el valor predeterminado hoy en día) y el registro predeterminado de Android.
Jonik
10

Recomiendo usar Timber de Jake Wharton

https://github.com/JakeWharton/timber

resuelve su problema con habilitar / deshabilitar además agrega clase de etiqueta automáticamente

sólo

public class MyApp extends Application {

  public void onCreate() {
    super.onCreate();
    //Timber
    if (BuildConfig.DEBUG) {
      Timber.plant(new DebugTree());
    }
    ...

los registros solo se usarán en la versión de depuración y luego se usarán

Timber.d("lol");

o

Timber.i("lol says %s","lol");

imprimir

"Tu clase / mensaje" sin especificar la etiqueta

AndroidGecko
fuente
2
La madera es muy agradable, pero si ya tiene un proyecto existente, puede probar github.com/zserge/log . Es un reemplazo directo para android.util.Log y tiene la mayoría de las características que tiene Timber e incluso más.
zserge
zserge, su solución de registro se ve bien. Muchas características. ¿Has considerado agregar reglas de pelusa como las de Timber?
jk7
8

He usado una clase LogUtils como en la aplicación de ejemplo Google IO. Modifiqué esto para usar una constante DEBUG específica de la aplicación en lugar de BuildConfig.DEBUG porque BuildConfig.DEBUG no es confiable . Luego en mis clases tengo lo siguiente.

import static my.app.util.LogUtils.makeLogTag;
import static my.app.util.LogUtils.LOGV;

public class MyActivity extends FragmentActivity {
  private static final String TAG = makeLogTag(MyActivity.class);

  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    LOGV(TAG, "my message");
  }
}
JosephL
fuente
+1 para el informe de errores Build.DEBUGque solía usar. También me di por vencido con las diversas soluciones "correctas" y utilicé una solución de estilo similar para usted.
Richard Le Mesurier
7

Consideraría usar la función de registro de roboguice en lugar del android.util.Log incorporado

Su instalación deshabilita automáticamente la depuración y los registros detallados para las versiones de lanzamiento. Además, obtienes algunas funciones ingeniosas de forma gratuita (por ejemplo, comportamiento de registro personalizable, datos adicionales para cada registro y más)

Usar proguard podría ser bastante complicado y no tendría problemas para configurarlo y hacerlo funcionar con su aplicación a menos que tenga una buena razón para eso (deshabilitar los registros no es bueno)

Zvi
fuente
Un enfoque muy agradable cuando no se puede usar la Ofuscación ... particularmente debido a la ruptura de un robot debido a la protección LOL
Snicolas
1
Enlace actualizado para la instalación de registro de robojuice
RenniePet
7

Estoy publicando esta solución que se aplica específicamente para usuarios de Android Studio. También descubrí recientemente Timber y lo importé con éxito en mi aplicación haciendo lo siguiente:

Ponga la última versión de la biblioteca en su build.gradle:

compile 'com.jakewharton.timber:timber:4.1.1'

Luego, en Android Studios, vaya a Editar -> Buscar -> Reemplazar en ruta ...

Escriba Log.e(TAG,o, sin embargo, haya definido sus mensajes de registro en el "Text to find"cuadro de texto. Entonces solo lo reemplazas conTimber.e(

ingrese la descripción de la imagen aquí

Haga clic en Buscar y luego reemplace todo.

Android Studios ahora revisará todos sus archivos en su proyecto y reemplazará todos los registros con Timbers.

El único problema que tuve con este método es que gradle aparece con un millón de mensajes de error después porque no puede encontrar "Madera" en las importaciones para cada uno de sus archivos java. Simplemente haga clic en los errores y Android Studios importará automáticamente "Timber" a su Java. Una vez que lo haya hecho para todos sus archivos de errores, Gradle compilará nuevamente.

También debe poner este código en el onCreatemétodo de su Applicationclase:

    if (BuildConfig.DEBUG) {
        Timber.plant(new Timber.DebugTree());
    }

Esto dará como resultado el registro de la aplicación solo cuando esté en modo de desarrollo y no en producción. También puede tener BuildConfig.RELEASEpara iniciar sesión en modo de lanzamiento.

Simón
fuente
3
Intente hacer lo mismo para sus importaciones y asegúrese de que la casilla Expresión regular esté marcada Texto para buscar: import android\.util\.Log\;Reemplace con:import android\.util\.Log\;\nimport timber\.log\.Timber\;
Clark Wilson
o puede usar la búsqueda estructural y reemplazar como muestra Chike Mgbemena en su publicación
Maksim Turaev
@MaksimTuraev Su enlace ya no es relevante. Ahora es un blog sobre peinados.
Vadim Kotov
Parece que la publicación se eliminó = (no se puede encontrar en ninguna parte.
Maksim Turaev
@MaksimTuraev aquí hay una copia de la máquina Wayback, pero las imágenes están rotas - web.archive.org/web/20161004161318/http://chikemgbemena.com/…
Vadim Kotov
6

Por android.util.Log proporciona una forma de habilitar / deshabilitar el registro:

public static native boolean isLoggable(String tag, int level);

Por defecto, el método isLoggable (...) devuelve falso, solo después de configurar la aplicación en el dispositivo le gusta esto:

adb shell setprop log.tag.MyAppTag DEBUG

Significa que se puede imprimir cualquier registro por encima del nivel DEBUG. Documento de Android de referencia:

Comprueba si un registro para la etiqueta especificada es registrable en el nivel especificado. El nivel predeterminado de cualquier etiqueta se establece en INFO. Esto significa que se registrará cualquier nivel superior e incluido INFO. Antes de realizar llamadas a un método de registro, debe verificar si su etiqueta debe estar registrada. Puede cambiar el nivel predeterminado estableciendo una propiedad del sistema: 'setprop log.tag. 'Donde el nivel es VERBOSO, DEPURAR, INFORMACIÓN, AVISO, ERROR, ASISTENCIA o SUPRESIÓN. SUPPRESS desactivará todos los registros de su etiqueta. También puede crear un archivo local.prop que contenga lo siguiente: 'log.tag. =' Y colocarlo en /data/local.prop.

Entonces podríamos usar la utilidad de registro personalizada:

public final class Dlog 
{
    public static void v(String tag, String msg)
    {
        if (Log.isLoggable(tag, Log.VERBOSE))
            Log.v(tag, msg);
    }

    public static void d(String tag, String msg)
    {
        if (Log.isLoggable(tag, Log.DEBUG))
            Log.d(tag, msg);
    }

    public static void i(String tag, String msg)
    {
        if (Log.isLoggable(tag, Log.INFO))
            Log.i(tag, msg);
    }

    public static void w(String tag, String msg)
    {
        if (Log.isLoggable(tag, Log.WARN))
            Log.w(tag, msg);
    }

    public static void e(String tag, String msg)
    {
        if (Log.isLoggable(tag, Log.ERROR))
            Log.e(tag, msg);
    }
}
Ricardo
fuente
6

Si puede ejecutar un reemplazo global (una vez), y luego preservar una convención de codificación, puede seguir el patrón que se usa a menudo en el marco de Android .

En lugar de escribir

Log.d(TAG, string1 + string2 + arg3.toString());

tenerlo como

if (BuildConfig.DEBUG) Log.d(TAG, string1 + String.format("%.2f", arg2) + arg3.toString());

Ahora proguard puede eliminar StringBuilder y todas las cadenas y métodos que utiliza en el camino, desde la versión optimizada DEX. Use proguard-android-optimize.txty no necesita preocuparse por android.util. Inicie sesión en proguard-rules.pro:

android {
  
  buildTypes {
    release {
      minifyEnabled true
      proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
    }
  }
}

Con el complemento Gradle de Android Studio, es bastante confiable, por lo que no necesita constantes adicionales para controlar la eliminación.BuildConfig.DEBUG

Alex Cohn
fuente
4

Agregue seguimiento a su archivo proguard-rules.txt

-assumenosideeffects class android.util.Log {
  public static *** d(...);
  public static *** w(...);
  public static *** v(...);
  public static *** i(...);
}
eranga
fuente
4

ingrese la descripción de la imagen aquí

Esto es lo que solía hacer en mis proyectos de Android ...

En Android Studio, podemos hacer una operación similar, Ctrl + Shift + F para buscar desde todo el proyecto (Comando + Shift + F en MacOs) y Ctrl + Shift + R para Reemplazar ((Comando + Shift + R en MacOs))

Lins louis
fuente
Esto parece abrir el trabajo con proyectos de eclipse. La opción de búsqueda ni siquiera está disponible en los estudios de Android.
Simon
2
en Android Studio puedes hacer una búsqueda similar con Ctrl + Shift + F atajo
Lins Louis
El código de ejemplo en la pregunta explica por qué esto no es confiable.
Nicolas Raoul
Podría causar problemas al eliminar cualquier comando que contenga en el Registro. Por ejemplo chocolateLog.recipie ();
Andrew S
No se puede encontrar esta opción para Android Studio 2.1. Además, puedo usar este truco en 1 archivo a la vez mediante la búsqueda / reemplazo normal.
VVB
3

Tengo una solución muy simple. Utilizo IntelliJ para el desarrollo, por lo que los detalles varían, pero la idea debería aplicarse en todos los IDE.

Elijo la raíz de mi árbol de origen, hago clic derecho y selecciono "reemplazar". Luego elijo reemplazar todo "Registro". con "// Registro". Esto elimina todas las declaraciones de registro. Para volver a colocarlos más tarde, repito el mismo reemplazo, pero esta vez como reemplazar todos los "// Registro". con "Registro".

Funciona muy bien para mí. Solo recuerde configurar el reemplazo como mayúsculas y minúsculas para evitar accidentes como "Diálogo". Para mayor seguridad, también puede hacer el primer paso con "Registro". como la cadena para buscar.

Brillante.

kg_sYy
fuente
2
Lea el párrafo "Si comento la línea de registro" en mi pregunta.
Nicolas Raoul el
OK, sí, debería volver a leer más seguido después de buscar las respuestas :). Si tiene estos casos, es posible que desee una solución diferente, como se sugirió anteriormente, como poner todos sus registros detrás de otra interfaz. Mi sugerencia funcione mejor para los equipos y proyectos más pequeños, donde las personas desean evitar la sobrecarga de librerías de registro adicionales, conoces a la gente y código bien, etc
kg_sYy
1
Reemplazar Log.d con; // Log.d también se ocupa de ese escenario "If".
Jasper
3

Como sugirió el comentario de zserge ,

La madera es muy agradable, pero si ya tiene un proyecto existente, puede probar github.com/zserge/log. Es un reemplazo directo para android.util.Log y tiene la mayoría de las características que tiene Timber e incluso más.

su biblioteca de registro proporciona un interruptor de activación / desactivación de impresión de registros simple como se muestra a continuación.

Además, solo requiere cambiar las importlíneas, y nada necesita cambiar para la Log.d(...);declaración.

if (!BuildConfig.DEBUG)
    Log.usePrinter(Log.ANDROID, false); // from now on Log.d etc do nothing and is likely to be optimized with JIT
Youngjae
fuente
¿Tiene que poner esa línea de código en cada Actividad / Fragmento, o solo en un lugar?
Noah Ternullo
@NoahTernullo // en el archivo de aplicación derivado. DefaultApplication.java
Youngjae
1

He mejorado la solución anterior al proporcionar soporte para diferentes niveles de registro y al cambiar los niveles de registro automáticamente dependiendo de si el código se ejecuta en un dispositivo en vivo o en el emulador.

public class Log {

final static int WARN = 1;
final static int INFO = 2;
final static int DEBUG = 3;
final static int VERB = 4;

static int LOG_LEVEL;

static
{
    if ("google_sdk".equals(Build.PRODUCT) || "sdk".equals(Build.PRODUCT)) {
        LOG_LEVEL = VERB;
    } else {
        LOG_LEVEL = INFO;
    }

}


/**
 *Error
 */
public static void e(String tag, String string)
{
        android.util.Log.e(tag, string);
}

/**
 * Warn
 */
public static void w(String tag, String string)
{
        android.util.Log.w(tag, string);
}

/**
 * Info
 */
public static void i(String tag, String string)
{
    if(LOG_LEVEL >= INFO)
    {
        android.util.Log.i(tag, string);
    }
}

/**
 * Debug
 */
public static void d(String tag, String string)
{
    if(LOG_LEVEL >= DEBUG)
    {
        android.util.Log.d(tag, string);
    }
}

/**
 * Verbose
 */
public static void v(String tag, String string)
{
    if(LOG_LEVEL >= VERB)
    {
        android.util.Log.v(tag, string);
    }
}


}
danwms
fuente
1
Mismo problema que la solución anterior. Si el parámetro de cadena se crea utilizando llamadas costosas, aún desperdicia recursos. La comprobación de llamadas debe realizarse antes de construir los parámetros pasados.
type-a1pha
1

ProGuard lo hará por usted en su versión de lanzamiento y ahora las buenas noticias de android.com:

http://developer.android.com/tools/help/proguard.html

La herramienta ProGuard reduce, optimiza y ofusca su código al eliminar el código no utilizado y renombrar clases, campos y métodos con nombres semánticamente oscuros. El resultado es un archivo .apk de menor tamaño que es más difícil de realizar ingeniería inversa. Debido a que ProGuard hace que su aplicación sea más difícil de aplicar ingeniería inversa, es importante que la use cuando su aplicación utilice características sensibles a la seguridad, como cuando está otorgando licencias a sus aplicaciones.

ProGuard está integrado en el sistema de compilación de Android, por lo que no tiene que invocarlo manualmente. ProGuard solo se ejecuta cuando compila su aplicación en modo de lanzamiento, por lo que no tiene que lidiar con el código ofuscado cuando construye su aplicación en modo de depuración. Hacer que ProGuard se ejecute es completamente opcional, pero muy recomendable.

Este documento describe cómo habilitar y configurar ProGuard, así como utilizar la herramienta de retroceso para decodificar trazas de pila ofuscadas

Max Gold
fuente
2
Sin embargo, no parece eliminar el registro de depuración de forma predeterminada. Entonces la respuesta de Christopher suena mejor.
Nicolas Raoul
0

Me gusta usar Log.d (TAG, alguna cadena, a menudo un String.format ()).

TAG es siempre el nombre de la clase

Transform Log.d (TAG, -> Logd (en el texto de su clase

private void Logd(String str){
    if (MainClass.debug) Log.d(className, str);
}

De esta manera, cuando esté listo para hacer una versión de lanzamiento, configure MainClass.debug en falso.

usuario462990
fuente
1
El problema con esta y otras soluciones aparte de proguard o comentarlas es que está dejando en el código, causando posiblemente una gran cantidad de compilaciones de cadenas. en una aplicación promedio no es un problema, pero si estás tratando de optimizarlo se convierte en un problema.
Lassi Kinnunen
0

Los registros se pueden eliminar usando bash en linux y sed:

find . -name "*\.java" | xargs sed -ri ':a; s%Log\.[ivdwe].*\);%;%; ta; /Log\.[ivdwe]/ !b; N; ba'

Funciona para registros multilínea. En esta solución puede estar seguro de que los registros no están presentes en el código de producción.

sylwano
fuente
0

Sé que esta es una vieja pregunta, pero ¿por qué no reemplazaste todas tus llamadas de registro con algo como Boolean logCallWasHere = true; // --- resto de tu registro aquí

Es por eso que sabrá cuándo desea volver a colocarlos, y no afectarán su llamada de declaración if :)

masood elsad
fuente
Interesante, es de esperar que el compilador / optimizador ignore esas líneas. Sin embargo, el nombre de la variable debería ser único, porque algunos métodos tienen varias llamadas de registro y no puede declarar la misma variable dos veces.
Nicolas Raoul
Puede declarar la variable en la parte superior de la actividad y eliminar la declaración booleana de esta línea;)
masood elsad
0

¿Por qué no solo hacer

if(BuildConfig.DEBUG)
  Log.d("tag","msg");

? No se necesitan bibliotecas adicionales, no hay reglas de protección que tienden a arruinar el proyecto y el compilador de Java simplemente omitirá el código de bytes para esta llamada cuando realice la compilación de lanzamiento.

Primož Kralj
fuente
Un inconveniente es que es más detallado que solo escribir Log.d("tag","msg");, y también es fácil olvidar escribir la if(BuildConfig.DEBUG)parte.
Nicolas Raoul
1
Otro problema con esto es que las cadenas permanecen en la versión empaquetada.
straya
0

Aquí está mi solución si no desea meterse con bibliotecas adicionales o editar su código manualmente. He creado este portátil Jupyter para repasar todos los archivos java y comentar todos los mensajes de registro. No es perfecto, pero hizo el trabajo por mí.

Naci
fuente
0

mi manera:

1) habilitar el modo de selección de columna (alt + shift + insert)

2) seleccione en un Log.d (TAG, "texto"); la parte 'Registro'.

3) luego haz shift + ctrl + alt + j

4) haz clic en la flecha izquierda

5) hacer shift + end

6) presiona eliminar.

esto elimina todas las llamadas de LOG a la vez en un archivo java.

Ronny Bigler
fuente
0

Puede intentar usar este sencillo método convencional:

Ctrl+ Shift+R

reemplazar

Log.e(

Con

// Log.e(
Fathur Rohim
fuente
Eso no funcionaría bien con el código de ejemplo dado en la pregunta.
Nicolas Raoul
0

Fácil con kotlin, solo declara algunas funciones de nivel superior

val isDebug: Boolean
    get() = BuildConfig.DEBUG

fun logE(tag: String, message: String) {
    if (isDebug) Log.e(tag, message)
}

fun logD(tag: String, message: String) {
    if (isDebug) Log.d(tag, message)
}
Zakhar Rodionov
fuente
-1

la forma más simple

utilizar DebugLog

DebugLog desactiva todos los registros cuando se lanza la aplicación.

https://github.com/MustafaFerhan/DebugLog

Mustafa Ferhan
fuente
Esto está absolutamente mal. Esto solo hace que los registros no se registren, no los elimina del código, por lo que todavía están allí para ayudar a las personas a realizar ingeniería inversa de su código, y aún tiene el costo de formatear las cadenas de todos esos registros.
Glenn Maynard