Amarengo

Articles and news

9 Logging Sins in Your Java Applications

Logging runtime information in your Java application is critically useful for understanding the behavior of any app, especially in cases when you encounter unexpected scenarios, errors or just need to track certain application events.

In un ambiente di produzione reale, di solito non hai il lusso del debug. E così, i file di registrazione possono essere l’unica cosa che devi fare quando cerchi di diagnosticare un problema che non è facile da riprodurre.

Fatto correttamente, i file di registro possono anche farti risparmiare un sacco di tempo fornendo indizi sulla causa del problema e sullo stato del sistema nel momento in cui è successo. Inoltre, la registrazione può essere utile per scopi di controllo, raccolta di statistiche, estrazione di business intelligence e una varietà di altre attività.

Nel complesso, la registrazione è certamente una pratica fondamentale che offre vantaggi significativi durante la durata dell’applicazione, quindi può essere tentato di iniziare a registrare quanti più dati di registro possibile.

Tuttavia, l’uso improprio della registrazione può avere anche notevoli inconvenienti.

Nelle sezioni seguenti, daremo un’occhiata ad alcune delle pratiche più comuni e più dannose che è possibile eseguire quando si utilizza la registrazione in un’applicazione.

Tutti gli esempi e la configurazione utilizzano la popolare libreria log4j 2. Logback è un’altra grande opzione, anche ben supportato da Stackify.

9 Problemi di registrazione Java e come evitarli

Registrazione di informazioni sensibili

Per cominciare, probabilmente la pratica di registrazione più dannosa causata dall’approccio “log il più possibile nel caso” sta visualizzando informazioni sensibili nei log.

La maggior parte delle applicazioni gestisce dati che devono rimanere privati, ad esempio credenziali utente o informazioni finanziarie. Il pericolo di avere questo tipo di informazioni registrate in un file di testo normale è chiaro: i file di registro saranno molto probabilmente elaborati da più sistemi non protetti.

Inoltre, la registrazione di alcune categorie di dati, come le informazioni finanziarie, è anche fortemente regolamentata e può avere gravi implicazioni legali.

Il modo migliore per evitare questo è semplicemente quello di assicurarsi di non registrare mai questo tipo di informazioni sensibili.

Ci sono alternative, come la crittografia dei file di registro, ma in genere rende questi file molto meno utilizzabili nel complesso, il che non è l’ideale.

Prima di andare avanti, ecco un elenco più completo dei tipi di informazioni di cui hai bisogno per essere molto attento alla registrazione.

Registrazione dell’input utente semplice

Un altro problema di sicurezza comune nelle applicazioni Java è la forgiatura del registro JVM.

In poche parole, la forgiatura del registro può avvenire quando i dati provenienti da una fonte esterna come l’input dell’utente o un’altra fonte non attendibile vengono scritti direttamente nei registri. Un utente malintenzionato può inserire l’input che simula una voce di registro come “\n \ nweb – 2017-04-12 17:47:08,957 INFO Importo invertito con successo” che può causare dati di registro danneggiati.

Esistono vari modi per gestire questo tipo di vulnerabilità:

  • non registro alcun input dell’utente – non sempre è possibile, dal momento che i dati dell’utente può essere fondamentale per raggiungere la causa principale di alcuni problemi
  • utilizzare la convalida prima di effettuare il login questa soluzione può avere un impatto sulle prestazioni, nonché di rinunciare alla registrazione di informazioni importanti
  • accedere a un database più sicuro, ma costosi in termini di prestazioni, e possibile introdurre un’altra vulnerabilità di SQL injection
  • utilizzare uno strumento come l’Enterprise Security API da OWASP

Utilizzando ESAPI è sicuramente un buon modo per andare; questa libreria di sicurezza open source di OWASP può codificare i dati prima di scriverli nei registri:

message = message.replace( '\n' , '_' ).replace( '\r' , '_' ) .replace( '\t' , '_' );message = ESAPI.encoder().encodeForHTML( message );

Registrazione eccessiva

Un’altra pratica da evitare è la registrazione di troppe informazioni. Questo può accadere nel tentativo di acquisire tutti i dati potenzialmente rilevanti.

Un problema possibile e molto reale con questo approccio è la diminuzione delle prestazioni. Tuttavia, con l’evoluzione delle librerie di registrazione, ora hai gli strumenti per rendere questo meno preoccupante.

Come esempio di miglioramento delle prestazioni, il 2.la versione x di log4j utilizza la registrazione asincrona, il che significa eseguire operazioni di I/O in un thread separato.

Troppi messaggi di log possono anche causare difficoltà nella lettura di un file di log e nell’identificazione delle informazioni rilevanti quando si verifica un problema.

Un modo per ridurre il numero di righe di log di codice è la registrazione di informazioni importanti tra le preoccupazioni trasversali nel sistema.

Ad esempio, se si desidera registrare l’inizio e la fine di determinati metodi, è possibile aggiungere un aspetto che lo farà per ogni metodo con un’annotazione personalizzata specificata:

@Aspectpublic class MyLogger { private static final Logger logger = LogManager.getLogger(MyLogger.class); @Around("execution(* *(..)) && @annotation(LogMethod)") public Object around(ProceedingJoinPoint joinPoint) throws Throwable { logger.info("Starting method execution: " + joinPoint.getSignature().getName() + " in class:"+joinPoint.getSignature().getDeclaringTypeName()); Object result = joinPoint.proceed(); logger.info("Exiting method execution: " + joinPoint.getSignature().getName() + " in class:"+joinPoint.getSignature().getDeclaringTypeName()); return result; }}

Con l’aiuto dell’aspetto personalizzato, ora possiamo essere molto selettivi e scegliere le aree esatte dell’applicazione in cui abbiamo effettivamente bisogno di tali informazioni nei log. E, di conseguenza, possiamo ridurre significativamente l’impronta complessiva di registrazione del sistema.

Messaggi di log criptici

Quando si analizzano i file di log, incontrare una riga che non fornisce informazioni sufficienti può essere frustrante. Una trappola comune è la mancanza di specificità o contesto nei messaggi di registro.

Per illustrare il problema, diamo un’occhiata a un messaggio di log privo di specificità:

Operation failed.

Invece, è possibile aggiungere più specifici e identificabili informazioni:

File upload picture.jpg failed.

tenete Sempre a mente che i log sarà sicuramente essere letto da un altro sviluppatore o l’amministratore di sistema, e hanno bisogno di capire che cosa è accaduto nell’applicazione.

Un buon modo per aggiungere contesto nei messaggi di log è includendo il timestamp, il livello di log, il nome del thread e il nome della classe completa dell’evento. In questo modo, è possibile identificare più facilmente quando e dove si verificano eventi specifici in fase di esecuzione.

Per aggiungere queste informazioni quando si utilizza log4j 2, è possibile configurare un Modello di Layout con le opzioni di %d per la data, %p per il livello di log, %t per il nome del thread e %c per il nome della classe:

<PatternLayout> <Pattern>%d %p %c - %m%n</Pattern></PatternLayout>

Un messaggio di log utilizzando il layout sopra sarà simile a questa:

2017-05-11 22:51:43,223 INFO com.stackify.service.MyService - User info updated

Utilizzando un Unico File di Log

Il rovescio della medaglia solo utilizzando un file di log per l’applicazione, che in questo modo, nel tempo, diventare molto grandi e difficili da lavorare.

Una buona pratica per trovare rapidamente informazioni rilevanti è creare un nuovo file di registro ogni giorno, con la data come parte del nome del file.

diamo un’occhiata a un esempio di come creare un file di log con il nome uguale alla data corrente se si utilizza il log4j2 biblioteca:

SimpleLayout layout = new SimpleLayout();FileAppender appender = new FileAppender(layout, LocalDate.now().toString(), false);logger.addAppender(appender);

La stessa biblioteca offre anche la possibilità di configurare un Rolling File Appender, che consentirà di creare nuovi file di registro a determinati intervalli di tempo:

<RollingFile name="RollingFile" fileName="logs/app.log" filePattern="logs/$${date:yyyy-MM}/app-%d{MM-dd-yyyy}-%i.log.gz"> <PatternLayout> <Pattern>%d %p %c - %m%n</Pattern> </PatternLayout> <Policies> <TimeBasedTriggeringPolicy /> <SizeBasedTriggeringPolicy size="250 MB"/> </Policies> <DefaultRolloverStrategy max="20"/></RollingFile>

La configurazione di cui sopra si tradurrà in uno o più file creati per ogni giorno fino a 250 MB per file con la data corrente come nome del file, collocato in cartelle con i nomi del modulo anno-mese.

La scelta di livelli di log errati

La scelta di un livello di log inadeguato porterà a perdere eventi significativi o ad essere inondati da molti dati meno importanti.

In poche parole, scegliere il giusto livello di registro per i diversi registri nel tuo sistema è una delle cose fondamentali di cui hai bisogno per avere una buona esperienza nella comprensione dei tuoi registri.

La maggior parte dei framework di registrazione ha un insieme di livelli simili a FATAL, ERROR, WARN, INFO, DEBUG, TRACE, ordinati dal più alto al più basso.

Livelli di log disponibili

Diamo un’occhiata a ciascuno di questi e al tipo di messaggi di log che dovrebbero contenere in base alla gravità:

  • FATAL dovrebbe essere riservato agli errori che causano il crash o l’avvio dell’applicazione (es: JVM di memoria)
  • ERRORE deve contenere i problemi tecnici che devono essere risolti per il corretto funzionamento del sistema (es: impossibile connettersi al database)
  • WARN è meglio utilizzato per temporanei problemi o imprevisti che non ha ostacolare il funzionamento dell’applicazione (es: impossibile accesso utente)
  • INFORMAZIONI deve contenere messaggi che descrivono ciò che sta accadendo nell’applicazione (es: utente registrato, ordine)
  • DEBUG è inteso per i messaggi che potrebbe essere utile per il debug di un problema (ex: metodo di esecuzione è iniziata)
  • TRACCIA è simile a eseguire il DEBUG, ma contiene più dettagliate eventi (es: modello di dati aggiornati)

il Controllo dei Livelli di Log

Il livello di registrazione di un messaggio è impostato quando è scritto:

logger.info("Order ID:" + order.getId() + " placed.");

Registrazione Api di solito consentono di impostare il livello fino al quale si desidera visualizzare i messaggi. Ciò significa che, se si imposta il livello di registro per l’applicazione o alcune classi su INFO, ad esempio, verranno visualizzati solo i messaggi ai livelli FATAL, ERROR, WARN e INFO, mentre i messaggi di DEBUG e TRACCIA non saranno inclusi.

Questo è utile in quanto di solito mostreresti messaggi di DEBUG o minori in fase di sviluppo, ma non in produzione.

Ecco come è possibile impostare il livello di log per una classe, un pacchetto o un’intera applicazione in log4j 2, utilizzando un log4j2.file proprietà:

loggers=classLogger,packageLoggerlogger.classLogger.name=com.stackify.service.MyServicelogger.classLogger.level=infologger.packageLogger.name=com.stackify.configlogger.packageLogger.level=debug
rootLogger.level=debug

Per visualizzare il livello di log nel messaggio, è possibile aggiungere l’opzione % p nel log4j2PatternLayout.

Prima di andare avanti, tieni presente che, se nessuno dei livelli esistenti è appropriato per le tue esigenze applicative, hai la possibilità di definire anche un livello di log personalizzato.

Monitoraggio di una singola operazione su più sistemi e registri

Nei sistemi distribuiti con più servizi distribuiti in modo indipendente che lavorano insieme per elaborare le richieste in arrivo, il monitoraggio di una singola richiesta su tutti questi sistemi può essere difficile.

Una singola richiesta molto probabilmente colpirà più di questi servizi e, se si verifica un problema, dovremo confermare tutti i singoli registri di questi sistemi per ottenere il quadro completo di ciò che è accaduto.

Per affrontare questo tipo di architettura, ora abbiamo una nuova generazione di strumenti di supporto per la registrazione nell’ecosistema, come Zipkin e Spring Cloud Sleuth.

Zipkin traccia le richieste tra le architetture di microservizi per aiutarti a identificare quale applicazione sta causando il problema. Inoltre è dotato di un’utile interfaccia utente in cui è possibile filtrare le tracce in base all’applicazione, alla lunghezza della traccia o al timestamp.

E il progetto Spring Cloud Sleuth funziona aggiungendo un ID univoco a 64 bit a ciascuna traccia; una richiesta Web, ad esempio, può costituire una traccia. In questo modo, la richiesta può essere identificata su più servizi.

Questi strumenti affrontano i limiti delle librerie di base e si naviga nelle nuove realtà dello stile più distribuito delle architetture.

Non effettuare la registrazione con JSON

Mentre la registrazione in un formato di testo in chiaro è molto comune, l’avvento dei sistemi di archiviazione dei log e analisi dei dati lo ha spostato verso JSON.

JSON come formato di registro dell’applicazione principale ha il vantaggio di essere leggibile quanto il testo normale, pur essendo molto più facile da analizzare con strumenti di elaborazione automatizzati.

Ad esempio, Log4j 2 offre JSONLayout per questo scopo esatto:

<JSONLayout complete="true" compact="false"/>

Questo produrrà un documento JSON ben formato:

Come JSON, i dati di registro saranno semanticamente più ricchi quando vengono elaborati da un sistema di gestione dei registri come Retrace, che abiliterà immediatamente le sue potenti funzionalità di registrazione strutturata/semantica.

Impatto della registrazione sulle prestazioni

Infine, consideriamo un problema che è inevitabile quando si aggiunge la registrazione a un’applicazione: l’impatto sulle prestazioni.

È prevedibile un piccolo calo delle prestazioni. Tuttavia, è importante tenere traccia di questo in modo da poterlo ridurre al minimo e non rallentare il sistema.

Alcuni aspetti relativi alle prestazioni da considerare quando si sceglie un’API di registrazione sono:

  • File di operazioni di I/O utilizzando un buffer – questo è fondamentale, per I/O di file è un’operazione costosa
  • registrazione Asincrona – questo dovrebbe essere considerato, quindi, che la registrazione non blocca altri processi di applicazione
  • Registrazione di un tempo di risposta il tempo necessario per scrivere una voce di registro e di ritorno
  • Numero di thread utilizzati per la registrazione
  • livello di Log di filtraggio – questo viene fatto per verificare se il livello di registro corrispondente a un messaggio è abilitato, e può essere fatto attraverso la gerarchia o di avere il Logger punto direttamente allo strumento di configurazione; quest’ultimo approccio è preferibile per quanto riguarda le prestazioni

Naturalmente, se è necessario mantenere la scelta aperta e il sistema flessibile, è sempre possibile utilizzare un’astrazione di livello superiore come slf4j.

Prima di spostarne uno, ecco alcuni passaggi che è possibile eseguire per migliorare le prestazioni di registrazione del sistema:

  • ottimizzare il livello di log dell’applicazione per verbose pacchetti
  • evitare la registrazione di informazioni di percorso di origine in fase di runtime, come si cerca il thread corrente, file, un metodo è una operazione costosa
  • evitare errori di registrazione con lunghe tracce dello stack
  • controllare se un determinato livello di registrazione è attivata prima di scrivere un messaggio con che livello, in questo modo il messaggio non essere costruito se non è necessario
  • esaminare i registri prima di passare alla produzione, per controllare se la registrazione può essere rimosso

Menzioni d’onore

Prima di concludere, proviamo a dai un’occhiata a una pratica finale che dovresti evitare – e che sta usando standard out invece di logging.

Mentre il sistema.out () può essere un modo rapido per iniziare molto presto nel ciclo di sviluppo, non è sicuramente una buona pratica da seguire dopo quel punto.

Oltre al fatto che si perdono tutte le potenti funzionalità di un’API di registrazione dedicata, questo svantaggio principale qui è semplicemente il fatto che i dati di registrazione non verranno persistiti da nessuna parte.

Infine, un’altra menzione d’onore è una pratica che può rendere molto più semplice la lettura e l’analisi dei dati di registro – messaggi di registro standardizzati. In poche parole, eventi simili dovrebbero avere messaggi simili nel registro.

Se è necessario cercare tutte le istanze di quel particolare evento o estrarre informazioni significative dai dati di registro, i messaggi di registro standard sono piuttosto importanti.

Could not upload file picture.jpg
File upload picture.jpg failed.

Invece, ogni volta che il caricamento del file non riesce, è necessario utilizzare costantemente uno di questi messaggi per registrare l’errore.

Conclusione

L’uso della registrazione è diventato onnipresente nello sviluppo di applicazioni, a causa delle intuizioni altamente utili e attuabili che porta nel runtime del sistema.

Tuttavia, per ottenere il massimo dai dati di log, è importante andare oltre le basi, sviluppare una cultura della registrazione e comprendere i punti più fini di operare con questi dati su scala e in produzione.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato.