android - registro - historial de llamadas personal
¿Cómo eliminar todas las llamadas de registro de depuración antes de publicar la aplicación en Google Play? (21)
Según Google, debo " desactivar todas las llamadas a los métodos de registro en el código fuente " antes de publicar mi aplicación de Android. Extracto de la sección 5 de la lista de verificación de la publicación :
Asegúrese de desactivar el registro y deshabilite la opción de depuración antes de compilar su aplicación para su lanzamiento. Puede desactivar el registro eliminando las llamadas a los métodos de registro en sus archivos de origen.
Mi proyecto de código abierto es grande y es un dolor hacerlo manualmente cada vez que lo publico. 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 lo suficientemente raras como para que yo decida que no debería existir?
Esto está en la lista de verificación oficial, así que supongo que muchas personas lo hacen regularmente.
Entonces, ¿cómo eliminar de forma eficiente pero segura todas las líneas de registro?
Agregue lo siguiente a su archivo proguard-rules.txt
-assumenosideeffects class android.util.Log {
public static *** d(...);
public static *** w(...);
public static *** v(...);
public static *** i(...);
}
Como sugirió el comentario de zserge ,
La madera es muy buena, 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 Timber tiene e incluso más.
github.com/zserge/log proporciona un simple interruptor de habilitar / deshabilitar la impresión de registros como se muestra abajo
Además, solo requiere cambiar las líneas de import
, y nada tiene que cambiar para 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
Consideraría utilizar el servicio de registro de roboguice en lugar del android.util.Log incorporado.
Su instalación deshabilita automáticamente los registros de depuración y detallados para las versiones de lanzamiento. Además, obtiene algunas funciones ingeniosas de forma gratuita (por ejemplo, comportamiento de registro personalizable, datos adicionales para cada registro y más)
El uso de proguard puede ser una molestia y no me tomaría la molestia de configurar y hacer que funcione con su aplicación a menos que tenga una buena razón para eso (deshabilitar los registros no es una buena idea)
Encuentro que una solución mucho más fácil es olvidar todas las comprobaciones en todo el lugar y simplemente usar ProGuard para eliminar cualquier llamada al método Log.d()
o Log.v()
cuando llamamos a nuestro objetivo de release
Ant.
De esa manera, siempre tenemos la información de depuración que se está generando 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 varias pasadas sobre el código de bytes para eliminar otras declaraciones no deseadas, bloques vacíos y puede insertar 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á usando.
Vea también los ejemplos en el manual de ProGuard.
Actualización (4.5 años después): Hoy en día usé Timber para el registro de Android.
No solo es un poco más agradable que la implementación de Log
predeterminada, la etiqueta de registro se establece automáticamente, y es fácil de registrar cadenas y excepciones con formato, sino que también puede especificar diferentes comportamientos de registro en el 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 se configura en mi onCreate()
Application
onCreate()
:
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 las declaraciones de depuración, pero los errores se informan silenciosamente a Crashlytics.
Estoy publicando esta solución que se aplica específicamente para los usuarios de Android Studio. También descubrí recientemente Timber y lo he importado correctamente 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 como haya definido sus mensajes de registro en el cuadro de "Text to find"
. Entonces, simplemente sustitúyalo por Timber.e(
Haga clic en Buscar y luego reemplazar todos.
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 después con un millón de mensajes de error 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 volverá a compilar.
También necesita poner este fragmento de código en su método onCreate
de su clase de Application
:
if (BuildConfig.DEBUG) {
Timber.plant(new Timber.DebugTree());
}
Esto hará que la aplicación se registre solo cuando esté en modo de desarrollo y no en producción. También puede tener BuildConfig.RELEASE
para iniciar sesión en modo de lanzamiento.
He mejorado la solución anterior brindando soporte para diferentes niveles de registro y cambiando los niveles de registro automáticamente dependiendo de si el código se está ejecutando en un dispositivo activo 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);
}
}
}
He utilizado una clase de LogUtils como en la aplicación de ejemplo de 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");
}
}
La solución de Christopher Proguard es la mejor, pero si por alguna razón no te 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''
Uncomment logs:
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 de UNIX o use comandos de Windows equivalentes)
Los registros se pueden eliminar utilizando 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.
Me gusta usar Log.d (TAG, alguna cadena, a menudo String.format ()).
TAG es siempre el nombre de la clase
Transformar 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!
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.
Con el fin de hacer que los assumenosideeffects
de assumenosideeffects
en los trabajos de Proguard, hay un requisito previo.
En su archivo de gradle, debe especificar el uso de proguard-android-optimize.txt
como archivo 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.txt
predeterminado proguard-android.txt
, la optimización está deshabilitada con los dos indicadores:
-dontoptimize
-dontpreverify
El proguard-android-optimize.txt
no agrega esas líneas, por lo que ahora puede funcionar con assumenosideeffects
.
Luego, personalmente, uso SLF4J , tanto más cuando desarrollo algunas bibliotecas que se distribuyen a otros. La ventaja es que por defecto no hay salida. Y si el integrador desea 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 LogCat.
Si realmente necesito eliminar los registros de la biblioteca final, luego los agrego a mi archivo Proguard (luego de haber habilitado el proguard-android-optimize.txt
supuesto):
-assumenosideeffects class * implements org.slf4j.Logger {
public *** trace(...);
public *** debug(...);
public *** info(...);
public *** warn(...);
public *** error(...);
}
Por android.util.Log proporciona una manera de habilitar / deshabilitar el registro:
public static native boolean isLoggable(String tag, int level);
El método predeterminado isLoggable (...) devuelve falso, solo después de que a setprop en el dispositivo le gusta esto:
adb shell setprop log.tag.MyAppTag DEBUG
Significa que cualquier registro por encima del nivel DEBUG se puede imprimir. Referencia de android doc:
Comprueba si un registro para la etiqueta especificada se puede registrar en el nivel especificado. El nivel predeterminado de cualquier etiqueta se establece en INFO. Esto significa que cualquier nivel superior e incluyendo INFO será registrado. Antes de realizar cualquier llamada a un método de registro, debe verificar si se debe registrar su etiqueta. Puede cambiar el nivel predeterminado configurando una propiedad del sistema: ''setprop log.tag. ''Donde el nivel es VERBOSE, DEBUG, INFO, WARN, ERROR, ASSERT o SUPPRESS. SUPPRESS desactivará todos los registros de su etiqueta. También puede crear un archivo local.prop que incluya lo siguiente: ''log.tag. ='' Y colocarlo en /data/local.prop.
Así que podríamos usar el uso de registro personalizado:
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);
}
}
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 confunde su código al eliminar el código no utilizado y cambiar el nombre de las 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 aplicar 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 utiliza características que son sensibles a la seguridad, como cuando está licenciando sus aplicaciones.
ProGuard está integrado en el sistema de compilación de Android, por lo que no tiene que invocarlo manualmente. ProGuard se ejecuta solo cuando crea su aplicación en modo de lanzamiento, por lo que no tiene que lidiar con el código confuso cuando construye su aplicación en modo de depuración. Tener ProGuard es completamente opcional, pero muy recomendable.
Este documento describe cómo habilitar y configurar ProGuard, así como usar la herramienta de rastreo para descodificar trazas de pila ofuscadas
Recomiendo usar Timber de Jake Wharton
resuelve su problema habilitando / deshabilitando además agrega la 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 utilizarán en su versión de depuración y luego usar
Timber.d("lol");
o
Timber.i("lol says %s","lol");
imprimir
"Tu clase / msg" sin especificar la etiqueta
Sé que esta es una pregunta antigua, pero ¿por qué no reemplazó todas sus llamadas de registro con algo como Boolean logCallWasHere = true; // --- el resto de su registro aquí
Esta es la razón por la que sabrá cuándo desea volver a ponerlas, y no afectarán su llamada if if :)
Si puede ejecutar una sustitución global (una vez), y después de eso conservar alguna convención de codificación, puede seguir el patrón que se usa a menudo en el framework 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 el StringBuilder y todas las cadenas y métodos que usa en el camino, desde la versión optimizada DEX. Use proguard-android-optimize.txt
y no tiene que preocuparse por android.util.Inicie proguard-rules.pro
en su proguard-rules.pro
:
android {
…
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile(''proguard-android-optimize.txt''), ''proguard-rules.pro''
}
}
}
Con Android Studio Gradle Plugin, BuildConfig. DEBUG
BuildConfig. DEBUG
es bastante confiable, por lo que no necesita constantes adicionales para controlar la extracción.
Sugiero tener un booleano estático en algún lugar que indique si se debe registrar o no:
class MyDebug { static final boolean LOG = true; }
Entonces, donde quiera que desee iniciar sesión en su código, simplemente haga esto:
if (MyDebug.LOG) { if (condition) Log.i(...); }
Ahora, cuando establece MyDebug.LOG en falso, el compilador eliminará todo el código dentro de dichas comprobaciones (ya que es un final estático, sabe que en el momento de la compilación no se usa ese código).
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 diversas 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 + ")");
Tengo una solución muy simple. Utilizo IntelliJ para el desarrollo, por lo que los detalles varían, pero la idea debería aplicarse a todos los IDE.
Escojo la raíz de mi árbol de origen, hago clic derecho y selecciono "reemplazar". Entonces elijo reemplazar todos los "Log". con "// Log.". Esto elimina todas las declaraciones de registro. Para volver a colocarlos más tarde, repito el mismo reemplazo, pero esta vez reemplaza todo "// Log". con "Log".
Funciona muy bien para mí. Solo recuerde configurar el reemplazo como sensible a 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.
Todas buenas respuestas, pero cuando terminé mi desarrollo, no quise usar las declaraciones if en todas las llamadas de Log ni tampoco usar herramientas externas.
Así que la solución que estoy usando es reemplazar la clase android.util.Log con mi propia clase de registro:
public class Log {
static final boolean LOG = false;
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 de origen era reemplazar la importación de android.util.Log con mi propia clase.
la forma más sencilla;
usar DebugLog
DebugLog deshabilita todos los registros cuando se lanza la aplicación.