web-dev-qa-db-pt.com

Como remover todas as chamadas de registro de depuração antes de publicar o aplicativo no Google Play?

De acordo com o Google, devo " desativar todas as chamadas para os métodos de Log no código-fonte " antes de publicar meu Android app. Extrair da seção 5 do lista de verificação da publicação :

Certifique-se de desativar o registro e desabilite a opção de depuração antes de criar seu aplicativo para lançamento. Você pode desativar o registro removendo chamadas para métodos de registro em seus arquivos de origem.

Meu projeto de código aberto é grande e é difícil fazê-lo manualmente toda vez que eu o libero. Além disso, remover uma linha de Log é potencialmente complicado, por exemplo:

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

Se eu comentar a linha Log, a condição se aplica à próxima linha e as chances são de que load () não seja chamado. Essas situações são tão raras que eu posso decidir que não deveria existir?

Isso está na lista de verificação oficial, então acho que muitas pessoas fazem isso regularmente.
Então, como remover de maneira eficiente, mas segura, todas as linhas do Log?

363
Nicolas Raoul

Acho que uma solução muito mais fácil é esquecer todas as verificações de if em todo o lugar e apenas usar ProGuard para remover qualquer chamada de método Log.d() ou Log.v() quando chamarmos nosso destino Ant release.

Dessa forma, sempre temos as informações de depuração sendo enviadas para compilações regulares e não precisamos fazer nenhuma alteração de código para compilações de lançamentos. O ProGuard também pode fazer várias passagens pelo bytecode para remover outras instruções indesejadas, blocos vazios e pode automaticamente inline métodos curtos quando apropriado.

Por exemplo, aqui está uma configuração do ProGuard muito 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(...);
}

Então, você salvaria isso em um arquivo, em seguida, chamaria ProGuard de Ant, passando seu JAR recém-compilado e o JAR da plataforma Android que você está usando.

Veja também os exemplos no manual do ProGuard.


Atualização (4,5 anos depois): Hoje em dia eu usei Timber para Android logging.

Não só é um pouco melhor do que a implementação padrão de Log - a tag de log é definida automaticamente, e é fácil registrar strings e exceções formatadas - mas você também pode especificar diferentes comportamentos de log em tempo de execução.

Neste exemplo, as instruções de registro só serão gravadas no logcat em compilações de depuração do meu aplicativo:

A madeira está configurada no meu método ApplicationonCreate():

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

Então, em qualquer outro lugar no meu código, eu posso logar facilmente:

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

Veja o Timber sample app para um exemplo mais avançado, onde todas as declarações de log são enviadas ao logcat durante o desenvolvimento e, na produção, nenhuma instrução de depuração é registrada, mas os erros são relatados silenciosamente ao Crashlytics.

458
Christopher Orr

Todas as boas respostas, mas quando terminei meu desenvolvimento, eu não queria usar instruções em todas as chamadas do Log, nem eu queria usar ferramentas externas.

Então a solução que estou usando é substituir a classe Android.util.Log pela minha própria classe Log:

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);
    }
}

A única coisa que eu tive que fazer em todos os arquivos de origem foi substituir a importação do Android.util.Log pela minha própria classe.

110
Reiner

Eu sugiro ter um booleano estático em algum lugar indicando se deve ou não registrar:

 class MyDebug {
 final estático booleano LOG = verdadeiro; 
} 

Então, onde quer que você queira entrar no seu código, faça o seguinte:

 if (MyDebug.LOG) {
 if (condição) Log.i (...); 
} 

Agora, quando você define MyDebug.LOG como false, o compilador removerá todo o código dentro de tais verificações (já que é uma final estática, ele sabe, em tempo de compilação, que o código não é usado).

Para projetos maiores, você pode querer começar a ter booleanos em arquivos individuais para poder habilitar ou desabilitar facilmente o log, conforme necessário. Por exemplo, estas são as várias constantes de registro que temos no gerenciador de janelas:

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;

Com código correspondente como:

    if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT) Log.v(
        TAG, "Adding window " + window + " at "
        + (i+1) + " of " + mWindows.size() + " (after " + pos + ")");
59
hackbod

A solução de Christopher's Proguard é a melhor, mas se por algum motivo você não gostar do Proguard, aqui está uma solução de baixa tecnologia:

Registros de comentários:

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'

Uma restrição é que suas instruções de registro não devem se estender por várias linhas.

(Execute estas linhas em um shell UNIX na raiz do seu projeto. Se estiver usando o Windows, obtenha uma camada do UNIX ou use comandos equivalentes do Windows)

30
Nicolas Raoul

Eu gostaria de adicionar algumas precisões sobre o uso do Proguard com Android Studio e gradle, já que eu tive muitos problemas para remover linhas de registro do binário final.

Para fazer o assumenosideeffects no Proguard funcionar, existe um pré-requisito.

No seu arquivo gradle, você tem que especificar o uso do proguard-Android-optimize.txt como arquivo padrão.

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'
    }
}

Na verdade, no arquivo proguard-Android.txt padrão, a otimização é desativada com os dois sinalizadores:

-dontoptimize
-dontpreverify

O arquivo proguard-Android-optimize.txt não adiciona essas linhas, então agora assumenosideeffects pode funcionar.

Então, pessoalmente, eu uso SLF4J , ainda mais quando eu desenvolvo algumas bibliotecas que são distribuídas para outras pessoas. A vantagem é que, por padrão, não há saída. E se o integrador quiser algumas saídas de log, ele poderá usar o Logback para Android e ativar os logs, para que os logs possam ser redirecionados para um arquivo ou para o LogCat.

Se eu realmente precisar remover os logs da biblioteca final, eu adiciono ao meu arquivo Proguard (depois de ter habilitado o arquivo proguard-Android-optimize.txt, é claro):

-assumenosideeffects class * implements org.slf4j.Logger {
    public *** trace(...);
    public *** debug(...);
    public *** info(...);
    public *** warn(...);
    public *** error(...);
}
16
Vincent Hiribarren

Eu usei uma classe LogUtils como no aplicativo de exemplo Google IO. Eu modifiquei isso para usar uma constante DEBUG específica do aplicativo em vez de BuildConfig.DEBUG porque BuildConfig.DEBUG não é confiável . Então nas minhas aulas eu tenho o seguinte.

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");
  }
}
8
JosephL

Eu sugiro usar Timber de Jake Wharton

https://github.com/JakeWharton/timber

ele resolve seu problema com ativação/desativação e adição de marca de classe automagicamente

somente

public class MyApp extends Application {

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

logs só serão usados ​​em seu ver de depuração e, em seguida, usar

Timber.d("lol");

ou

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

imprimir

"Sua classe/msg" sem especificar a tag

8
AndroidGecko

Eu consideraria usar o logging facility do roboguice em vez do Android.util.Log integrado

Sua facilidade desabilita automaticamente logs debug e verbose para compilações de releases. Além disso, você obtém gratuitamente alguns recursos interessantes (por exemplo, comportamento de registro personalizável, dados adicionais para cada registro e muito mais)

Usar o proguard pode ser um incômodo e eu não teria o trabalho de configurá-lo e fazê-lo funcionar com seu aplicativo, a menos que você tenha uma boa razão para isso. que (desabilitar logs não é bom)

7
Zvi

Por Android.util.Log fornece uma maneira de ativar/desativar o log:

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

Por padrão o método isLoggable (...) retorna false, somente após setprop no dispositivo gostar disso:

adb Shell setprop log.tag.MyAppTag DEBUG

Isso significa que qualquer log acima do nível DEBUG pode ser impresso. Referência Android doc:

Verifica se um log da tag especificada pode ou não ser logado no nível especificado. O nível padrão de qualquer tag é definido como INFO. Isso significa que qualquer nível acima e incluindo INFO será registrado. Antes de fazer qualquer chamada para um método de registro, você deve verificar se sua tag deve ser registrada. Você pode alterar o nível padrão definindo uma propriedade do sistema: 'setprop log.tag. 'Onde o nível é VERBOSE, DEBUG, INFO, WARN, ERROR, ASSERT ou SUPPRESS. O SUPPRESS desligará todos os registros da sua tag. Você também pode criar um arquivo local.prop com o seguinte: 'log.tag. =' E colocar isso em /data/local.prop.

Então, poderíamos usar o utilitário de log 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);
    }
}
6
Richard

Estou postando essa solução que se aplica especificamente a Android usuários do Studio. Eu também descobri recentemente o Timber e o importei com sucesso para o meu aplicativo, fazendo o seguinte:

Coloque a versão mais recente da biblioteca em seu build.gradle:

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

Então em Android Studios, vá em Edit -> Find -> Replace in Path ...

Digite Log.e(TAG, ou, no entanto, você definiu suas mensagens de log na caixa de texto "Text to find". Então você acabou de substituí-lo com Timber.e(

enter image description here

Clique em Localizar e, em seguida, substitua todos.

O Android Studios passará por todos os seus arquivos em seu projeto e substituirá todos os registros por madeiras.

O único problema que tive com esse método é que o gradle aparece com um milhão de mensagens de erro depois, porque não consegue encontrar "Timber" nas importações de cada um de seus arquivos Java. Basta clicar nos erros e Android Os estúdios irão importar automaticamente "Timber" para o seu Java. Depois de ter feito isso para todos os seus arquivos de erros, gradle irá compilar novamente.

Você também precisa colocar este trecho de código em seu método onCreate da sua classe Application:

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

Isso resultará no registro do aplicativo apenas quando você estiver no modo de desenvolvimento e não na produção. Você também pode ter BuildConfig.RELEASE para fazer o login no modo de liberação.

6
Simon

Se você pode executar uma substituição global (uma vez) e depois preservar alguma convenção de codificação, você pode seguir o padrão geralmente usado em Android framework .

Em vez de escrever

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

tê-lo como

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

Agora o proguard pode remover o StringBuilder e todas as strings e métodos que ele usa no caminho, a partir do lançamento otimizado DEX. Use proguard-Android-optimize.txt e você não precisa se preocupar com Android.util.Log no seu proguard-rules.pro:

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

Com Android O plugin Studio gradle, BuildConfig.DEBUG é bastante confiável, então você não precisa de constantes extras para controlar o stripping.

5
Alex Cohn

Eu tenho uma solução muito simples. Eu uso o IntelliJ para desenvolvimento, então os detalhes variam, mas a ideia deve se aplicar a todos os IDEs.

Eu escolho para raiz da minha árvore de origem, clique com o botão direito e selecione fazer "substituir". Eu escolho então substituir todos os "Logs". com "// Log." Isso remove todas as instruções de log. Para colocá-los de volta mais tarde, repito a mesma substituição, mas desta vez como substituir todos os "// Log". com "Log".

Funciona muito bem para mim. Apenas lembre-se de definir a substituição como diferencia maiúsculas de minúsculas para evitar acidentes como "Diálogo". Para garantia adicional, você também pode fazer o primeiro passo com "Log". como a string a ser pesquisada.

Brilhante.

3
kg_sYy

Adicione o seguinte ao seu arquivo proguard-rules.txt

-assumenosideeffects class Android.util.Log {
  public static *** d(...);
  public static *** w(...);
  public static *** v(...);
  public static *** i(...);
}
3
eranga

enter image description here

Isso é o que eu costumava fazer em meus projetos Android.

No Android Studio, podemos fazer uma operação semelhante, Ctrl + Shift + F para encontrar todo o projeto (Command + Shift + F no MacOs) e Ctrl + Shift + R para substituir ((Command + Shift + R no MacOs) ))

3
Lins Louis

As comentário do zserge sugerido,

A madeira é muito boa, mas se você já tem um projeto existente - você pode tentar github.com/zserge/log. É um substituto para o Android.util.Log e tem a maioria dos recursos que o Timber tem e ainda mais.

sua biblioteca de log fornece simples habilitar/desabilitar o switch de impressão de log como abaixo.

Além disso, somente requer a mudança de import linhas, e nada precisa para alterar para a instrução Log.d(...);.

if (!BuildConfig.DEBUG)
    Log.usePrinter(Log.Android, false); // from now on Log.d etc do nothing and is likely to be optimized with JIT
2
Youngjae

O ProGuard fará isso para você no seu lançamento e agora as boas notícias do Android.com:

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

A ferramenta ProGuard reduz, otimiza e ofusca seu código removendo códigos não utilizados e renomeando classes, campos e métodos com nomes semanticamente obscuros. O resultado é um arquivo .apk de tamanho menor que é mais difícil de fazer engenharia reversa. Como o ProGuard dificulta a engenharia reversa do aplicativo, é importante usá-lo quando o aplicativo utiliza recursos sensíveis à segurança, como quando você está licenciando seus aplicativos.

O ProGuard está integrado ao sistema de compilação Android, portanto, você não precisa invocá-lo manualmente. O ProGuard só é executado quando você cria seu aplicativo no modo de liberação, para que você não precise lidar com o código ofuscado ao criar seu aplicativo no modo de depuração. A execução do ProGuard é completamente opcional, mas altamente recomendada.

Este documento descreve como habilitar e configurar o ProGuard, bem como usar a ferramenta de retorno para decodificar traços de pilha ofuscados

1
Max Gold

Eu melhorei a solução acima fornecendo suporte para diferentes níveis de log e alterando os níveis de log automaticamente dependendo se o código está sendo executado em um dispositivo ativo ou no 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);
    }
}


}
1
danwms

Eu gosto de usar o Log.d (TAG, alguma string, geralmente String.format ()).

TAG é sempre o nome da turma

Transform Log.d (TAG, -> Logd (no texto da sua classe

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

Desta forma, quando estiver pronto para fazer uma versão de lançamento, defina MainClass.debug como false!

0
user462990

Eu sei que esta é uma pergunta antiga, mas por que você não substituiu todas as suas chamadas de log com algo como Boolean logCallWasHere = true; // --- resto do seu log aqui

É por isso que você saberá quando quiser colocá-los de volta, e eles não afetarão sua chamada de instrução if :)

0
masood elsad

Logs podem ser removidos usando o bash no linux e no sed:

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

Funciona para logs de múltiplas linhas. Nesta solução, você pode ter certeza de que os logs não estão presentes no código de produção.

0
sylwano