É verdade que não se deve usar NSLog () no código de produção?

155

Foi-me dito isso algumas vezes neste mesmo site, mas eu queria ter certeza de que esse é realmente o caso.

Eu esperava poder espalhar chamadas de função do NSLog por todo o meu código e que o Xcode / gcc removesse essas chamadas automaticamente ao criar minhas compilações de Liberação / Distribuição.

Devo evitar usar isso? Em caso afirmativo, quais alternativas são mais comuns entre programadores experientes em Objective-C?

jpm
fonte
7
Sei que essa pergunta agora é muito antiga, mas, se você ainda puder, marcaria a resposta de Marc Charbonneau como aceita. Modifiquei minha resposta para apontar para a dele, mas a resposta é a correta.
e.James
5
O NSLog () dentro de um loop frequente absolutamente matará seu desempenho, ele disse, tendo descoberto da maneira mais difícil.
willc2

Respostas:

197

Macros de pré-processador são realmente ótimas para depuração. Não há nada errado com o NSLog (), mas é simples definir sua própria função de log com melhor funcionalidade. Aqui está o que eu uso, ele inclui o nome do arquivo e o número da linha para facilitar o rastreamento de instruções de log.

#define DEBUG_MODE

#ifdef DEBUG_MODE
    #define DebugLog( s, ... ) NSLog( @"<%p %@:(%d)> %@", self, [[NSString stringWithUTF8String:__FILE__] lastPathComponent], __LINE__, [NSString stringWithFormat:(s), ##__VA_ARGS__] )
#else
    #define DebugLog( s, ... ) 
#endif

Achei mais fácil colocar essa declaração inteira no cabeçalho do prefixo do que em seu próprio arquivo. Você poderia, se quisesse, criar um sistema de log mais complicado, fazendo o DebugLog interagir com objetos Objective-C normais. Por exemplo, você pode ter uma classe de log que grava em seu próprio arquivo de log (ou banco de dados) e inclui um argumento de 'prioridade' que você pode definir em tempo de execução, para que as mensagens de depuração não sejam mostradas na versão de lançamento, mas as mensagens de erro ( se você fez isso, poderá criar DebugLog (), WarningLog () e assim por diante).

Ah, lembre-se de que #define DEBUG_MODEpode ser reutilizado em diferentes locais do seu aplicativo. Por exemplo, no meu aplicativo, eu o uso para desativar as verificações de chave de licença e permitir que o aplicativo seja executado somente antes de uma determinada data. Isso me permite distribuir uma cópia beta totalmente funcional e com tempo limitado, com o mínimo esforço da minha parte.

Marc Charbonneau
fonte
8
+1 para uma excelente resposta. Alterei o meu para indicar que suas macros #define são o caminho a percorrer, e espero que o OP mude a resposta aceita (deixei um comentário para ele). Eu estava usando uma função fictícia porque não sabia que você poderia usar ... argumentos em uma macro. Viva e aprenda!
e.James
15
Uma excelente resposta, apesar de eu recomendar o uso de um prefixo pessoal em sua definição "DEBUG_MODE", como chamá-lo de "JPM_DEBUG" ou algo semelhante. Muitas vezes, encontrei código de terceiros que também usa DEBUG ou DEBUG_MODE ou algo semelhante e, às vezes, esse código não funciona corretamente no modo DEBUG. Se você deseja ativar a depuração de bibliotecas de terceiros, faça isso intencionalmente. (Obviamente, são os escritores de bibliotecas que devem prefixar seus símbolos, mas muitas estruturas C e C ++ não, especialmente para isso definir).
911 Rob Napier
1
existe uma macro predefinida do Xcode que pode ser usada para ativar isso somente quando a configuração estiver definida para depuração? Prefiro não definir manualmente essa macro de pré-processador em todos os projetos. podemos fazer algo como seguir o pseudocódigo #if XCODE_CONFIGURATION == DEBUG?
23410 frankodwyer
1
#include <TargetConditionals.h>
slf
2
Essa abordagem leva a avisos espúrios de "variáveis ​​não utilizadas" do compilador no modo de liberação quando as instruções de log usam variáveis ​​intermediárias com o único objetivo de calcular os valores a serem registrados. Qual seria a maneira mais inteligente de evitar isso se você odeia os avisos do compilador tanto quanto eu?
Jean-Denis Muys
78

Coloque estas 3 linhas no final do arquivo -prefix.pch:

#ifndef DEBUG
  #define NSLog(...) /* suppress NSLog when in release mode */
#endif

Você não precisa definir nada no seu projeto, porque DEBUGé definido na configuração de compilação por padrão quando você cria o seu projeto.

roel
fonte
2
Longe a melhor solução. Você precisa adicionar prefix.pch manualmente a partir XCode 6.
Teddy
Ainda precisamos configuração mudança de construção antes do lançamento ou seja depuração para liberar
UserDev
25

As chamadas do NSLog podem ser deixadas no código de produção, mas só devem existir para casos realmente excepcionais ou para as informações desejadas que serão registradas no log do sistema.

Os aplicativos que desarrumam o log do sistema são irritantes e parecem não profissionais.

Matthew Schinckel
fonte
14
Desculpe - pareça não profissional para quem? Quem provavelmente está verificando seus logs em um aplicativo lançado e julgando seu profissionalismo com base nisso? (Para ser claro, eu concordo totalmente que você não deve manter uma tonelada de NSLogs na versão da sua aplicação, mas estou confuso com o argumento de 'profissionalismo'.)
WendiKidd
4
Outros desenvolvedores ficarão irritados com o que você está fazendo. Android tem um problema semelhante com alguns desenvolvedores sendo muito ruim plus.google.com/110166527124367568225/posts/h4jK38n4XYR
Roger Binns
24

Não posso comentar a resposta de Marc Charbonneau , por isso vou postar isso como resposta.

Além de adicionar a macro ao cabeçalho pré-compilado, você pode usar as configurações de criação do Target para controlar a definição (ou falta de definição) do DEBUG_MODE .

Se você selecionar a configuração ativa " Debug ", DEBUG_MODEela será definida e a macro será expandida para a totalidadeNSLog definição .

Selecionar a configuração ativa " Liberar " não definirá DEBUG_MODEe seuNSLog ging será omitido na compilação do release.

Passos:

  • Alvo> Obter informações
  • Guia Build
  • Procure por "Macros de pré-processador" (ou GCC_PREPROCESSOR_DEFINITIONS )
  • Selecione Configuração: Depuração
  • Editar definição neste nível
  • Adicionar DEBUG_MODE=1
  • Selecione Configuração: Liberar
  • confirmar DEBUG_MODEnão está definido emGCC_PREPROCESSOR_DEFINITIONS

se você omitir o caractere '=' na definição, você receberá um erro do pré-processador

Além disso, cole este comentário (mostrado abaixo) acima da definição da macro para lembrá-lo de onde DEBUG_MACROvem a definição;)

// Target > Get Info > Build > GCC_PREPROCESSOR_DEFINITIONS
// Configuration = Release: <empty>
//               = Debug:   DEBUG_MODE=1
ohhorob
fonte
1
É uma resposta adicional valiosa para a pergunta. Merece ser mais que um comentário.
morningstar
DEBUG_MODEe DEBUG_MACROnão são convencionais. Eu encontrei apenas uma referência DEBUG_MACROno site da apple ( opensource.apple.com/source/gm4/gm4-15/src/m4.h?txt ). Talvez o mais padrão DEBUGe NDEBUGseria melhores escolhas? NDEBUGé especificado por Posix; while DEBUGé usado por convenção.
achou
+1 Sim, este é um post antigo, mas esse é o ponto ... Na minha versão do Xcode (4 anos depois), uma pesquisa por GCC_PREPROCESSOR_DEFINITIONS retorna um idioma diferente. Por favor, considere atualizar esta excelente resposta para maior clareza.
David
11

EDIT: O método publicado por Marc Charbonneau , e trouxe à minha atenção por sho , é muito melhor que este.

Excluí a parte da minha resposta que sugeria o uso de uma função vazia para desativar o log quando o modo de depuração está desativado. A parte que lida com a configuração de uma macro de pré-processador automático ainda é relevante, portanto permanece. Também editei o nome da macro do pré-processador para que ela se encaixe melhor na resposta de Marc Charbonneau.


Para alcançar o comportamento automático (e esperado) no Xcode:

Nas configurações do projeto, vá para a guia "Build" e selecione a configuração "Debug". Localize a seção "Macros de pré-processador" e adicione uma macro denominada DEBUG_MODE.

...

EDIT: consulte a resposta de Marc Charbonneau para a maneira correta de ativar e desativar o log com a DEBUG_MODEmacro.

e.James
fonte
7

Eu concordo com Matthew. Não há nada errado com o NSLog no código de produção. De fato, pode ser útil para o usuário. Dito isto, se o único motivo pelo qual você está usando o NSLog é ajudar na depuração, sim, isso deve ser removido antes do lançamento.

Além disso, como você marcou isso como uma pergunta do iPhone, o NSLog usa recursos, algo que o iPhone tem muito pouco. Se você está registrando algo no iPhone, isso reduz o tempo do processador do seu aplicativo. Use com sabedoria.

agosto
fonte
4

A verdade simples é que o NSLog é simplesmente lento.

Mas por que? Para responder a essa pergunta, vamos descobrir o que o NSLog faz e, em seguida, como ele faz.

O que o NSLog faz exatamente?

O NSLog faz 2 coisas:

Ele grava mensagens de log no recurso Apple System Logging (asl). Isso permite que as mensagens de log sejam exibidas no Console.app. Ele também verifica se o fluxo stderr do aplicativo está indo para um terminal (como quando o aplicativo está sendo executado via Xcode). Nesse caso, ele grava a mensagem de log no stderr (para que apareça no console do Xcode).

Escrever para STDERR não parece difícil. Isso pode ser feito com fprintf e a referência do descritor de arquivo stderr. Mas e quanto a asl?

A melhor documentação que eu encontrei sobre o ASL é uma postagem de blog de 10 partes de Peter Hosey: link

Sem entrar em muitos detalhes, o destaque (no que diz respeito ao desempenho) é o seguinte:

Para enviar uma mensagem de log para o recurso ASL, você basicamente abre uma conexão do cliente com o daemon ASL e envia a mensagem. MAS - cada thread deve usar uma conexão de cliente separada. Portanto, para ser seguro para threads, toda vez que o NSLog é chamado, ele abre uma nova conexão de cliente ASL, envia a mensagem e fecha a conexão.

Recursos podem ser encontrados aqui e aqui .

Andrew
fonte
Editou o texto. Os recursos precisam estar apenas no rodapé.
31813 Johan
2

Conforme observado em outras respostas, você pode usar um #define para alterar se o NSLog é usado ou não no momento da compilação.

No entanto, uma maneira mais flexível é usar uma biblioteca de registro como o Cocoa Lumberjack, que permite alterar se algo também é registrado em tempo de execução.

No seu código, substitua o NSLog por DDLogVerbose ou DDLogError etc, adicione um #import para as definições de macro etc e configure os registradores, geralmente no método applicationDidFinishLaunching.

Para ter o mesmo efeito que o NSLog, o código de configuração é

[DDLog addLogger:[DDASLLogger sharedInstance]];
[DDLog addLogger:[DDTTYLogger sharedInstance]];
mmmmmm
fonte
2

Do ponto de vista da segurança, depende do que está sendo registrado. Se NSLog(ou outros registradores) estiverem gravando informações confidenciais, você deverá remover o registrador no código de produção.

Do ponto de vista da auditoria, o auditor não deseja examinar cada uso de NSLog para garantir que não registre informações confidenciais. Ele simplesmente diz para você remover o logger.

Eu trabalho com os dois grupos. Auditamos código, escrevemos os guias de codificação, etc. Nosso guia exige que o registro seja desativado no código de produção. Portanto, as equipes internas sabem que não devem tentar;)

Também rejeitaremos um aplicativo externo que faça logon na produção, porque não queremos aceitar o risco associado ao vazamento acidental de informações confidenciais. Não nos importamos com o que o desenvolvedor nos diz. Simplesmente não vale o nosso tempo para investigar.

E lembre-se, definimos 'sensível', e não o desenvolvedor;)

Também percebo um aplicativo que executa muitos logs como um aplicativo pronto para implodir. Há uma razão pela qual tanto o log é realizado / necessário, e geralmente não é estável. É lá em cima com threads 'watchdog' que reiniciam os serviços travados.

Se você nunca passou por uma revisão da Security Architecture (SecArch), esses são os tipos de coisas que analisamos.

jww
fonte
1

Você não deve ser desnecessariamente detalhado com printf ou NSLog no código de liberação. Tente fazer apenas um printf ou NSLog se o aplicativo tiver algo ruim, ou seja, um erro irrecuperável.

MaddTheSane
fonte
1

Lembre-se de que o NSLogs pode diminuir a velocidade da interface do usuário / thread principal. É melhor removê-los das versões de lançamento, a menos que seja absolutamente necessário.

psy
fonte
0

Eu recomendo usar o TestFlight para log (gratuito). O método deles substituirá o NSLog (usando uma macro) e permitirá que você ative / desative o log no servidor deles, o log do Apple System e o STDERR, para todas as chamadas existentes para o NSLog. O bom disso é que você ainda pode revisar suas mensagens de log para aplicativos implantados em testadores e aplicativos implantados na App Store, sem que os logs apareçam no log do sistema do usuário. O melhor de dois mundos.

Joel
fonte
Deve-se considerar a sobrecarga que o TestFlight adiciona ao aplicativo. É possível adicionar apenas a parte de log do TestFlight?
31813 Johan