Introdução

Log, bem como estruturas remotas de relatórios de erros e travamentos, já existem há algum tempo. O uso de ambas as estruturas é bastante diferente dependendo do caso.

Neste artigo, cobriremos os usos de ambos os tipos de estruturas, incluindo problemas nas compilações de nossos aplicativos móveis e algumas soluções sugeridas. Também incluo uma estrutura centralizada que nos ajudará a evitar esses problemas e obter o máximo do registro e do relatório remoto de erros.

Estruturas de registro

Primeiro, vamos definir o que exatamente as estruturas de registro e relatório de erros fazem.
Você já usou as instruções de log no Android ou as instruções de impressão no iOS? Eles são frameworks de registro. Eles permitem que nós, os desenvolvedores, imprimamos praticamente qualquer coisa na janela do console de nossos IDEs.

Precisa verificar o valor de uma variável em um método? Faça login.
Precisa verificar a resposta da API? Faça login.
Precisa verificar o erro de análise JSON da API? Faça login.
Precisa verificar as exceções de erro nos blocos Catch? Faça login.
E a lista continua.

O uso mais comum de logs é durante a depuração. Atualmente, todos os principais IDEs vêm equipados com depuradores integrados. Ele permite que os desenvolvedores adicionem pontos de interrupção e naveguem pelo código. Também nos permite acessar os valores das variáveis ​​enquanto percorremos o código.

Ainda assim, um grande número de desenvolvedores depende da abordagem de registro tradicional! Não acredita em mim? Veja estes memes por si mesmo:

Meme sobre depuração Drake meme sobre depuração

Além dos loggers disponíveis por padrão em Java e Swift, existem várias estruturas de log construídas sobre eles. Essas estruturas estendem os recursos dos loggers e seus usos. Exemplos comuns são Timber (Android) , Willow (iOS) e CocoaLumberjack (iOS) .

Agora que temos uma boa ideia sobre o que são as estruturas de registro, vamos prosseguir para as estruturas de relatórios de erros e falhas.

Estruturas de relatórios de falhas e erros

Usamos registros enquanto um aplicativo está em desenvolvimento. Os desenvolvedores os usam para acessar os valores das variáveis ​​em cada estágio, identificar travamentos e depurar o problema. As saídas de registro são visíveis no console do IDE.

Então, que tal receber relatórios de erros e falhas enquanto o aplicativo já está em produção?

Vamos considerar um cenário: você testou seu aplicativo exaustivamente em seu dispositivo e publicou o aplicativo em sua respectiva loja. Alguns usuários reclamam de falhas de aplicativos ou funcionalidades que não funcionam em seus dispositivos.

O que você faria aqui?

Como há um grande número de fabricantes de dispositivos, sistemas operacionais, ROMs personalizados e tamanhos de dispositivos, é quase impossível testar um aplicativo em todas essas permutações e combinações. Isso deixa espaço para possíveis erros no ambiente de produção. Mas como você pode depurar esses erros quando você não tem acesso ao dispositivo físico?

Felizmente, algumas ferramentas permitem que façamos isso. Firebase Crashlytics é uma ferramenta popular. Depois de integrado a um aplicativo, ele captura automaticamente os relatórios de falha do aplicativo e os salva no console. Os desenvolvedores podem acessar facilmente esses relatórios de registro e depurar o erro.

Ele também nos permite capturar erros não fatais e registros de nosso aplicativo. Podem ser respostas de erro de API, exceções de captura ou o que quisermos registrar.

Qual é a diferença?

Se você perceber, há algo comum aqui nessas duas estruturas. Veja, o objetivo principal das estruturas de registro e das estruturas de relatório de falhas e erros é a depuração de erros. A principal diferença é que um é usado durante o desenvolvimento e o outro na produção.

Agora que entendemos esses tipos de framework e seus usos, vamos aprender sobre os problemas que podemos enfrentar quando começarmos a usá-los na abordagem tradicional. Assim que entendermos o problema, estaremos em uma posição melhor para encontrar uma solução.

Problemas e soluções com relatórios de erros remotos

Problema 1: exposição de mensagens de registro confidenciais em compilações de lançamento

Se seus aplicativos móveis passaram por avaliação de vulnerabilidade e teste de penetração (VAPT), você pode ter se deparado com esta vulnerabilidade: “Mensagens de log revelam informações confidenciais. Desative os loggers em compilações de produção. ”

Isso é muito comum durante o desenvolvimento. Registramos as respostas da API e detectamos erros e outras variáveis. O que esquecemos é como remover esses comandos de log antes de criar o build de produção.

Se alguém conectar seu dispositivo ao computador e observar os registros impressos no console, poderá ver tudo o que registramos. Isso pode incluir parâmetros confidenciais, respostas inteiras da API ou outras informações privadas.

Mesmo se nos lembrarmos de remover esses comandos de log, teremos que remover ou comentar esses loggers manualmente em todo o código-fonte. Um processo agitado e repetitivo!

Solução 1: depurar e liberar registro baseado em ambiente

Com o tipo de construção do aplicativo, seja uma versão de lançamento ou uma depuração, podemos controlar quais instruções de log precisam ser impressas no console e quais podem ser ignoradas. Usando isso, podemos esquecer a preocupação em registrar informações confidenciais nos aplicativos de produção.

Problema 2: problemas de API e erros não fatais na produção

A maioria de nossos aplicativos móveis é alimentada por dados de APIs remotas. Se a estrutura de dados esperada não corresponder à da resposta da API codificada no aplicativo, a funcionalidade dependente dela pode falhar.

Mas, quando um aplicativo está em produção e uma alteração na estrutura da API como esta acontece, a funcionalidade do nosso aplicativo não funciona. Como saberíamos sobre esses cenários antes, para que possamos lançar uma correção antes que ela afete muitos usuários? Continuamos monitorando toda a funcionalidade do aplicativo diariamente? Esperamos que alguém denuncie?

Não, não podemos fazer isso! O que precisamos é de um processo no qual possamos relatar e ser notificados sobre esses problemas o mais rápido possível.

Solução 2: relatório de erro remoto baseado em nível de registro

O Firebase Crashlytics, com seu relatório de erros personalizado, fornece uma solução: Precisamos identificar o nível de nossos registros. Alguns podem ser apenas informativos, alguns podem ser um erro, alguns podem ser para depuração.

Os erros de API, por exemplo, se enquadrariam na categoria “erro”. Podemos conceber uma lógica na qual compartilhamos as instruções de registro com o nível correto como”erro”para nosso relatório de erro remoto do Firebase. Dessa forma, podemos rastrear os problemas não fatais, mas que quebram a funcionalidade, e resolvê-los o mais rápido possível.

Mas isso significa que teríamos que escrever esse código em todo o aplicativo? Isso nos leva ao nosso próximo problema…

Problema 3: código disperso e facilidade de manutenção

Os problemas um e dois têm algumas soluções viáveis: adicionar sinalizadores de compilação e usar o Firebase Crashlytics para registro remoto de erros. Mas implementá-los em torno de cada instrução de registro não seria uma boa solução.

Nossas declarações de registro estão espalhadas por todo o aplicativo. Durante a depuração, acabamos liberando uma enxurrada de instruções de log em nosso código. Eu sei disso porque sou culpado de fazer isso. Não podemos continuar adicionando nossa lógica personalizada em torno de cada uma dessas instruções de log.

Vejamos também a partir de uma perspectiva de sustentabilidade do código. O que acontece quando queremos mudar a lógica de nossos madeireiros? Continuamos alterando cada instrução de log em toda a base de código? De jeito nenhum! Nós codificamos para tornar a vida de nossos usuários mais fácil. Por que não fazer o nosso também?

Solução 3: estrutura de registro centralizado com base no tipo de compilação e nível de registro

Agora, a peça que faltava: precisamos que todas as nossas soluções acima funcionem em conjunto. Uma única classe que controlará os logs baseados no tipo de construção e no nível de log, e nenhuma lógica if-else repetida em cada instrução de log na base de código. Isso evitará a dispersão de código e ajudará na manutenção e escalabilidade do código.

Vamos construir uma estrutura em torno dos níveis de registro e tipos de construção, incluindo quais instruções devem ser executadas onde e quando.

Nível de registro Nível de registro-Uso Tipo de construção Console Registro remoto
Erro Ocorreu um erro não fatal que interrompeu a funcionalidade do aplicativo, por exemplo, um formato JSON errado. O aplicativo não pode analisar este formato e, portanto, a funcionalidade do aplicativo parou de funcionar. Depurar ✔
Lançamento ✔
Aviso Ocorreu um erro inesperado no aplicativo que não deveria ter ocorrido em primeiro lugar, por exemplo, uma exceção específica do dispositivo em uma função ou código movendo-se para um bloco catch que não era esperado. Depurar ✔
Lançamento ✔
Informações Mensagens de registro adicionadas para observar o comportamento do aplicativo, por exemplo, tela aberta ou fechada, a chamada de API retornada com sucesso ou consultas de banco de dados retornando com sucesso. Depurar ✔
Lançamento
Depurar Mensagens de registro que são adicionadas para depurar um erro específico, por exemplo, valores de variáveis ​​ou valores de respostas de API. Depurar ✔
Lançamento

Agora que projetamos a solução, vamos avançar rapidamente e verificar a implementação da mesma no Android e no iOS.

Usaremos estruturas de registro de terceiros existentes que nos ajudarão a criar registradores com base no tipo de construção durante o tempo de execução. Para relatórios de erros remotos, usaremos o Firebase Crashlytics. Você pode saber mais sobre como personalizar seus relatórios de erros com o Crashlytics aqui.

O esquema para ambas as implementações é assim:

  1. Crie registradores específicos do tipo de construção usando uma estrutura de registro de terceiros
  2. Adicione nossa lógica de nível de registro nos registradores de liberação
  3. Substitua as declarações de registro tradicionais por nossas personalizadas

Android

Para criar registradores específicos do tipo de construção, usaremos uma das melhores bibliotecas de registro no Android: Timber . Se você já está usando, ótimo! Se não, eu recomendo fortemente usar isso em seus projetos. Estaremos criando nossa estrutura de relatório de erros com base em nível de log usando os recursos que o Timber oferece.

Observe que estou pulando os detalhes de integração do Timber e do Firebase Crashlytics . É melhor descrito em suas páginas oficiais, que eu incluí nesta seção.

Vamos mergulhar na criação de nossa estrutura.

Primeiro, vamos implementar a lógica do tipo de construção na inicialização do framework. Estaremos usando dois registradores diferentes: um para modo de depuração e outro para liberação. O logger do modo de liberação será nosso personalizado:

 public class App estende Application { @Sobrepor public void onCreate () { super.onCreate (); if (BuildConfig.DEBUG) { Timber.plant (novo Timber.DebugTree ()); } senão { Timber.plant (novo LoggingController ()); } }
}

Agora, vamos implementar nosso logger remoto personalizado para o modo de liberação, que mencionamos acima. Isso conterá a lógica de nível de log:

 public class LoggingController extends Timber.Tree
{ @Override protected void log (int logLevel, String tag, @NonNull String message, Throwable t) { if (logLevel==Log.ERROR || logLevel==Log.WARN) { FirebaseCrashlytics.getInstance (). RecordException (t); }senão{ Retorna; } }
}

Vamos verificar o exemplo de uso:

 Timber.d ("Mensagem de depuração de teste");
Timber.i ("Mensagem de informação de teste");
Timber.w (new RuntimeException (),"Mensagem de aviso de teste");
Timber.e (new RuntimeException (),"Mensagem de erro de teste");

Em vez de usar Log.d () ou Log.e () , agora teremos que usar o Timber.d () ou Timber.e () . O resto será tratado pelo nosso framework!

iOS

No iOS, para implementar registradores específicos do tipo de construção, usaremos Willow . Criado pela Nike, é uma das melhores implementações Swift de um logger personalizado.

Estaremos criando nossa estrutura de relatório de erros com base em nível de log usando os recursos que Willow fornece.

Observe que, como em nossa implementação anterior do Android, estou pulando os detalhes de integração do Willow e do Firebase Crashlytics. É melhor descrito em suas páginas oficiais, às quais incluí um link anteriormente neste artigo.

Vamos começar a criar nossa estrutura.

Primeiro, vamos implementar a lógica do tipo de construção na configuração do framework. Estaremos usando dois registradores diferentes: um para modo de depuração e outro para liberação. O logger do modo de liberação será nosso personalizado:

 var logger: Logger!
public struct LoggingConfiguration { func configure () { #if DEBUG logger=buildDebugLogger () #senão logger=buildReleaseLogger () #fim se } função privada buildReleaseLogger ()-> Logger { let consoleWriter=LoggingController.sharedInstance let queue=DispatchQueue (rótulo:"serial.queue", qos:.utility) return Logger (logLevels: [.error,.warn], escritores: [consoleWriter], método de execução:.assíncrono (fila: fila)) } função privada buildDebugLogger ()-> Logger { let consoleWriter=ConsoleWriter () return Logger (logLevels: [.all], gravadores: [consoleWriter], executionMethod:.synchronous (lock: NSRecursiveLock ())) }
}

Agora, vamos implementar nosso logger remoto personalizado para o modo de liberação, que mencionamos acima. Isso terá a lógica de nível de log:

 abrir a classe LoggingController: LogWriter { static public var sharedInstance=LoggingController () static public var attributeKey="erro" init privado () {} public func writeMessage (_ message: String, logLevel: LogLevel) { //Uma vez que este é um registrador de liberação, não usaremos isso... } public func writeMessage (_ message: LogMessage, logLevel: LogLevel) { if logLevel==.error || logLevel==.warn { if let error=message.attributes [LoggingController.attributeKey] como? Erro{ Crashlytics.crashlytics (). Record (erro: erro) } } }
}
extension Error { func getLogMessage ()-> LogMessage { return ErrorLogMessage (nome:"Error", erro: self) }
}
struct ErrorLogMessage: LogMessage { var name: String atributos var: [String: Qualquer] init (nome: String, erro: Erro) { self.name=name self.attributes=[LoggingController.attributeKey: erro] }
}

Teremos que inicializar esta estrutura em AppDelegate :

 classe AppDelegate: UIResponder, UIApplicationDelegate { func application (_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?)-> Bool { FirebaseApp.configure () LoggingConfiguration (). Configure () retorno verdadeiro }
}

Você pode ver o exemplo de uso aqui:

//Registros de depuração
logger.debugMessage ("Mensagem de depuração de registro") //Logs de informações
logger.infoMessage ("Mensagem de informações de registro") //Logs de erros e avisos
let logMessage=getSampleErrorObj (). getLogMessage ()
logger.error (logMessage) func getSampleErrorObj ()-> Error { let userInfo=[]//Você pode adicionar qualquer informação de erro relevante aqui para ajudar a depurá-lo return NSError.init (domínio: NSCocoaErrorDomain, código:-1001, userInfo: userInfo)
}

Portanto, em vez de usar o comando tradicional print () , agora teríamos que usar o logger.debugMessage () ou logger.error () , por exemplo. Todo o resto é tratado pelo nosso framework!

Conclusão

Conseguimos! Construímos nosso relatório remoto de erros e estrutura de registro. Bem, não exatamente uma estrutura, mas mais como uma estrutura de “invólucro” que se estende sobre os recursos das bibliotecas existentes.

Como esta é nossa implementação personalizada e toda a lógica reside em um único controlador, podemos estender sua capacidade a qualquer momento para adicionar mais filtros e aprimorar nossos registradores. Isso também deve manter nosso código limpo e ajudar na manutenção.

Espero que você tenha aprendido algo novo e útil hoje. Continue aprendendo e construindo, e feliz registro!

A postagem Registro e relatório remoto de erros no celular apps apareceu primeiro no LogRocket Blog .

Source link