Amarengo

Articles and news

9 logning af Synder i dine Java-applikationer

logning af kørselsoplysninger i din Java-applikation er kritisk nyttigt til at forstå opførslen af enhver app, især i tilfælde, hvor du støder på uventede scenarier, fejl eller bare har brug for at spore bestemte applikationsbegivenheder.

i et produktionsmiljø i den virkelige verden har du normalt ikke den luksus at debugge. Og så kan logning af filer være det eneste, du skal gå ud af, når du forsøger at diagnosticere et problem, der ikke er let at reproducere.

udført korrekt, logfiler kan også spare dig for en masse tid ved at give spor i årsagen til problemet, og ind i tilstanden af systemet på det tidspunkt, det skete. Logning kan også være nyttigt til revisionsformål, indsamling af statistikker, udtrækning af business intelligence og en række andre opgaver.

samlet set er logning bestemt en grundlæggende praksis, der giver betydelige fordele i løbet af applikationens levetid – så det kan være fristende at begynde at optage så meget logdata som muligt.

forkert brug af logning kan dog også have betydelige ulemper.

i de følgende afsnit tager vi et kig på nogle af de mest almindelige og mest skadelige fremgangsmåder, som du kan løbe ind i, når du bruger logning i et program.

alle eksempler og konfiguration bruger det populære Log4J 2-Bibliotek. Logback er en anden god mulighed, også godt understøttet af Stackify.

9 Java Logging problemer og hvordan man undgår dem

Logging følsomme oplysninger

til at begynde med, sandsynligvis den mest skadelige logning praksis anlagt den af “log så meget som muligt just in case” tilgang viser følsomme oplysninger i logfilerne.

de fleste applikationer håndterer data, der skal forblive private, såsom brugeroplysninger eller økonomiske oplysninger. Faren for at have denne type oplysninger logget ind i en almindelig tekstfil er klar – logfiler vil meget sandsynligt blive behandlet af flere, usikrede systemer.

desuden er logning af nogle kategorier af data, såsom finansielle oplysninger, også stærkt reguleret og kan have alvorlige juridiske konsekvenser.

den bedste måde at undgå dette på er simpelthen at sikre, at du aldrig logger denne slags følsomme oplysninger.

der er alternativer, såsom kryptering af logfilerne, men det gør generelt disse filer meget mindre anvendelige generelt, hvilket ikke er ideelt.

før vi går videre, er her en mere omfattende liste over de typer oplysninger, du skal være meget forsigtig med at logge.

logning almindelig brugerinput

et andet almindeligt sikkerhedsproblem i Java-applikationer er JVM Log smedning.

kort sagt, log smedning kan ske, når data fra en ekstern kilde som brugerinput eller en anden kilde, der ikke er tillid til, skrives direkte til logfilerne. En ondsindet hacker kan indtaste input, der simulerer en log post såsom” \n\NVB – 2017-04-12 17:47:08,957 INFO beløb vendt med succes”, som kan resultere i beskadigede logdata.

der er forskellige måder at håndtere denne form for sårbarhed på:

  • log ikke nogen brugerinput – ikke altid muligt, da brugerdataene kan være kritiske for at komme til grundårsagen til nogle problemer
  • brug validering før logning – denne løsning kan påvirke ydeevnen samt give afkald på at logge vigtige oplysninger
  • log til en database – mere sikker, men dyr med hensyn til ydeevne og kan introducere en anden sårbarhed – enterprise security API fra esapi

brug af esapi er bestemt en god vej at gå; dette open source-sikkerhedsbibliotek kan kode data, før du skriver det til logfilerne:

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

overdreven logning

en anden praksis, der skal undgås, er at logge for meget information. Dette kan ske i et forsøg på at fange alle potentielt relevante data.

et muligt og meget reelt problem med denne tilgang er nedsat ydeevne. Men med udviklingen af logbiblioteker har du nu værktøjerne til at gøre dette mindre bekymrende.

som et eksempel på forbedret ydeevne, den 2.Log4J bruger asynkron logning, hvilket betyder at udføre I/O-operationer i en separat tråd.

for mange logmeddelelser kan også føre til vanskeligheder med at læse en logfil og identificere de relevante oplysninger, når der opstår et problem.

en måde at reducere antallet af loglinjer med kode er ved at logge vigtige oplysninger på tværs af tværgående bekymringer i systemet.

hvis du for eksempel vil logge starten og slutningen af bestemte metoder, kan du tilføje et aspekt, der gør dette for hver metode, der har en specificeret brugerdefineret annotation:

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

ved hjælp af det brugerdefinerede aspekt kan vi nu være meget selektive og vælge de nøjagtige områder af applikationen, hvor vi faktisk har brug for disse oplysninger i logfilerne. Og som et resultat kan vi reducere systemets samlede logfodaftryk markant.

kryptiske logmeddelelser

når du analyserer logfiler, kan det være frustrerende at støde på en linje, der ikke giver tilstrækkelig information. En almindelig faldgrube er manglen på specificitet eller kontekst i logmeddelelser.

for at illustrere problemet, lad os se på en logmeddelelse, der mangler specificitet:

Operation failed.

i stedet kan du tilføje mere specifikke og identificerbare oplysninger:

File upload picture.jpg failed.

husk altid, at dine logfiler helt sikkert vil blive læst af en anden udvikler eller systemadministrator, og de skal forstå, hvad der er sket i applikationen.

en god måde at tilføje kontekst i logmeddelelser er ved at inkludere tidsstempel, logniveau, trådnavn og fuldt kvalificeret klassenavn på begivenheden. På denne måde kan du lettere identificere, hvornår og hvor specifikke begivenheder opstår ved kørsel.

for at tilføje disse oplysninger, når du bruger log4j 2, kan du konfigurere et Mønsterlayout med indstillingerne %d for datoen, %p for logniveau, %t for trådnavn og %c for klassenavn:

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

en logmeddelelse ved hjælp af ovenstående layout vil se sådan ud:

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

brug af en enkelt logfil

ulempen ved kun at bruge en logfil til applikationen er, at dette med tiden bliver ret stort og vanskeligt at arbejde med.

en god praksis for hurtigt at finde relevant information er at oprette en ny logfil hver dag med datoen som en del af filnavnet.

lad os se på et eksempel på, hvordan du opretter en logfil med navnet lig med den aktuelle dato, hvis du bruger log4j2-biblioteket:

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

det samme bibliotek giver også mulighed for at konfigurere en rullende Filappender, der opretter nye logfiler med bestemte tidsintervaller:

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

konfigurationen ovenfor resulterer i en eller flere filer oprettet for hver dag op til 250 MB pr.fil med den aktuelle dato som filnavn, placeret i mapper med navne på formularen År-Måned.

valg af forkerte Logniveauer

valg af et utilstrækkeligt logniveau vil føre til enten manglende væsentlige begivenheder eller blive oversvømmet med en masse mindre vigtige data.

kort sagt, at vælge det rigtige logniveau for de forskellige logfiler i dit system er en af de centrale ting, du har brug for for at få ret til at få en god oplevelse med at forstå dine logfiler.

de fleste logningsrammer har et sæt niveauer, der ligner FATAL, ERROR, advar, INFO, DEBUG, TRACE, bestilt fra højeste til laveste.

tilgængelige logniveauer

lad os se på hver af disse og typen af logmeddelelser, de skal indeholde, baseret på sværhedsgrad:

  • FATAL bør forbeholdes fejl, der får applikationen til at gå ned eller ikke starte (eks: JVM ud af hukommelsen)
  • fejl skal indeholde tekniske problemer, der skal løses for at systemet fungerer korrekt (eks: kunne ikke oprette forbindelse til databasen)
  • advar bruges bedst til midlertidige problemer eller uventet opførsel, der ikke væsentligt hæmmer applikationens funktion (eks: mislykket brugerlogin)
  • INFO skal indeholde meddelelser, der beskriver, hvad der sker i applikationen (eks: brugerregistreret, ordre placeret)
  • Debug er beregnet til meddelelser, der kan være nyttige i debugging et problem (f. eks: metode udførelse startet)
  • TRACE ligner DEBUG, men indeholder mere detaljerede begivenheder (f. eks: datamodel opdateret)

styring af Logniveauer

logniveauet for en meddelelse indstilles, når den skrives:

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

Logging API ‘ er giver dig normalt mulighed for at indstille det niveau, som du vil se meddelelser til. Hvad dette betyder er, at hvis du indstiller logniveauet for applikationen eller bestemte klasser til INFO, for eksempel, vil du kun se meddelelser på niveauerne FATAL, ERROR, advar og INFO, mens DEBUG og TRACE-meddelelser ikke vil blive inkluderet.

dette er nyttigt, da du normalt viser DEBUG eller lavere meddelelser under udvikling, men ikke i produktion.

sådan kan du indstille logniveauet for en klasse, pakke eller hele applikationen i log4j 2 ved hjælp af en log4j2.egenskaber fil:

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

for at vise logniveauet i meddelelsen kan du tilføje indstillingen %p i log4j2PatternLayout.

før vi går videre, skal du huske på, at hvis ingen af de eksisterende niveauer passer til dine applikationsbehov, har du også mulighed for at definere et brugerdefineret logniveau.

sporing af en enkelt Operation på tværs af flere systemer og logfiler

i distribuerede systemer med flere, uafhængigt implementerede tjenester, der arbejder sammen om at behandle indgående anmodninger, kan det være svært at spore en enkelt anmodning på tværs af alle disse systemer.

en enkelt anmodning vil meget sandsynligt ramme flere af disse tjenester, og hvis der opstår et problem, skal vi bekræfte alle de enkelte logfiler for disse systemer for at få det fulde billede af, hvad der skete.

for at løse denne form for arkitektur, har vi nu en ny generation af logning hjælpeværktøjer i økosystemet, som f.eks.

vi sporer anmodninger på tværs af microservice-arkitekturer for at hjælpe dig med at identificere, hvilket program der forårsager problemet. Det leveres også med et nyttigt brugergrænseflade, hvor du kan filtrere spor baseret på applikation, sporets længde eller tidsstempel.

og Spring Cloud Sleuth-projektet fungerer ved at tilføje et unikt 64-bit ID til hvert spor; en internetanmodning kan for eksempel udgøre et spor. På denne måde kan anmodningen identificeres på tværs af flere tjenester.

disse værktøjer adresserer begrænsningerne i kernebibliotekerne, og du navigerer i de nye realiteter i den mere distribuerede arkitekturstil.

ikke logning med JSON

mens logning i et almindeligt tekstformat er meget almindeligt, har fremkomsten af loglagrings-og dataanalysesystemer skiftet det mod JSON.

JSON som det primære applikationslogformat har fordelen ved at være lige så læsbar som almindelig tekst, samtidig med at det er meget lettere at analysere ved hjælp af automatiserede behandlingsværktøjer.

for eksempel tilbyder Log4j 2 JSONLayout til dette nøjagtige formål:

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

dette vil producere et velformet JSON-dokument:

som JSON vil logdataene være semantisk rigere, når de behandles af et logstyringssystem som Retrace – som straks muliggør dets kraftfulde strukturerede/semantiske logningsfunktioner.

logning indvirkning på ydeevne

endelig, lad os overveje et problem, som er uundgåelig, når du tilføjer logning til et program: indvirkningen på ydeevnen.

et lille fald i ydeevnen kan forventes. Det er dog vigtigt at spore dette, så du kan minimere det og ikke bremse systemet.

nogle præstationsrelaterede aspekter, der skal overvejes, når du vælger en logging API, er:

  • fil i/O – operationer ved hjælp af en buffer – dette er kritisk, da fil I/O er en dyr operation
  • asynkron logning – dette bør overvejes, så logning ikke blokerer for andre applikationsprocesser
  • logging responstid – den tid det tager at skrive en logindgang og returnere
  • antal tråde, der bruges til logning
  • logniveaufiltrering-dette gøres for at kontrollere, om logniveauet svarende til en meddelelse er aktiveret, og kan gøres ved at krydse hierarkiet eller have Loggerpunktet direkte til loggerkonfigurationen; sidstnævnte tilgang foretrækkes med hensyn til ydeevne

selvfølgelig, hvis du har brug for at holde valget åbent og systemet fleksibelt, kan du altid bruge en abstraktion på højere niveau som slf4j.

før vi flytter en, her er blot et par trin, du kan tage for at forbedre logføringsydelsen for dit system:

  • Indstil logniveauet for applikationen til verbose – pakker
  • undgå at logge kildeplaceringsoplysninger ved kørsel, da det at slå den aktuelle tråd, fil op, en metode er en dyr operation
  • undgå logfejl med lange stakspor
  • kontroller, om et specifikt logniveau er aktiveret, før du skriver en besked med det niveau-på denne måde konstrueres meddelelsen ikke, hvis den ikke er nødvendig
  • gennemgå logfilerne, inden du flytter til produktion for at kontrollere, om nogen logning kan fjernes

ærede omtaler

før vi afslutter, lad os tag et kig på en sidste praksis, som du bør undgå – og det bruger standard ud i stedet for at logge.

Mens Systemet.ud () kan være en hurtig måde at starte meget tidligt i udviklingscyklussen, det er bestemt ikke en god praksis at følge efter dette punkt.

udover det faktum, at du mister alle de kraftfulde funktioner i en dedikeret logging API, er denne primære ulempe her simpelthen det faktum, at logningsdataene ikke vil blive vedvarende overalt.

endelig er en anden hæderlig omtale en praksis, der kan gøre læsning og analyse af logdata meget lettere – standardiserede logmeddelelser. Kort sagt, lignende begivenheder skal have lignende meddelelser i loggen.

hvis du har brug for at søge efter alle forekomster af den pågældende begivenhed eller udtrække meningsfuld indsigt ud af dine logdata, er standardlogmeddelelser ret vigtige.

for eksempel, hvis en upload operation mislykkes-at have disse forskellige meddelelser i loggen ville være forvirrende:

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

i stedet, når filoverførslen mislykkes, skal du konsekvent bruge en af disse meddelelser til at logge fejlen.

konklusion

brugen af logning er blevet allestedsnærværende i applikationsudvikling på grund af den meget nyttige og handlingsmæssige Indsigt, den bringer ind i systemets runtime.

for at få mest muligt ud af dine logdata er det dog vigtigt at gå ud over det grundlæggende, udvikle en loggingskultur og forstå de finere punkter i driften med disse data i skala og i produktion.

Skriv et svar

Din e-mailadresse vil ikke blive publiceret.