Log4j 2 plug-ins

1. Overzicht

Log4j 2 gebruikt plug-ins zoals Appenders en Layouts om logboeken op te maken en uit te voeren. Deze staan ​​bekend als kernplug-ins en Log4j 2 biedt veel opties waaruit we kunnen kiezen.

In sommige gevallen kunnen we echter ook de bestaande plug-in moeten uitbreiden of zelfs aangepaste plug-ins moeten schrijven.

In deze tutorial gebruiken we het Log4j 2-uitbreidingsmechanisme om aangepaste plug-ins te implementeren.

2. Uitbreiding van Log4j 2 plug-ins

Plug-ins in Log4j 2 zijn grofweg onderverdeeld in vijf categorieën:

  1. Core-plug-ins
  2. Omvormers
  3. Sleutelaanbieders
  4. Opzoeken
  5. Type converters

Met Log4j 2 kunnen we aangepaste plug-ins implementeren in alle bovenstaande categorieën met behulp van een gemeenschappelijk mechanisme. Bovendien stelt het ons ook in staat om bestaande plug-ins uit te breiden met dezelfde aanpak.

In Log4j 1.x is de enige manier om een ​​bestaande plug-in uit te breiden, het overschrijven van de implementatieklasse ervan. Aan de andere kant maakt Log4j 2 het gemakkelijker om bestaande plug-ins uit te breiden door een klasse te annoteren met @Inpluggen.

In de volgende secties zullen we een aangepaste plug-in implementeren in een paar van deze categorieën.

3. Core-plug-in

3.1. Een aangepaste kernplug-in implementeren

Sleutelelementen zoals Appenders, Layouts en Filters staan ​​bekend als core plug-ins in Log4j 2. Hoewel er een gevarieerde lijst van dergelijke plug-ins is, kan het in sommige gevallen nodig zijn om een ​​aangepaste kernplug-in te implementeren. Beschouw bijvoorbeeld een ListAppender die alleen logboekrecords naar een in-geheugen schrijft Lijst:

@Plugin (name = "ListAppender", categorie = Core.CATEGORY_NAME, elementType = Appender.ELEMENT_TYPE) openbare klasse ListAppender breidt AbstractAppender {privélijst logList uit; beschermde ListAppender (Stringnaam, Filterfilter) {super (naam, filter, null); logList = Collections.synchronizedList (nieuwe ArrayList ()); } @PluginFactory openbare statische ListAppender createAppender (@PluginAttribute ("naam") Stringnaam, @PluginElement ("Filter") laatste filterfilter) {retourneer nieuwe ListAppender (naam, filter); } @Override public void append (LogEvent-gebeurtenis) {if (event.getLevel (). IsLessSpecificThan (Level.WARN)) {error ("Kan niet loggen op minder dan WARN-niveau."); terugkeren; } logList.add (evenement); }}

We hebben de klas geannoteerd met @Inpluggen waarmee we onze plug-in een naam kunnen geven. De parameters zijn ook geannoteerd met @PluginAttribute. De geneste elementen zoals filter of lay-out worden doorgegeven als @PluginElement. Nu kunnen we deze plug-in in de configuratie doorverwijzen met dezelfde naam:

3.2. Plugin Builders

Het voorbeeld in de laatste sectie is vrij eenvoudig en accepteert slechts één parameter naam. Over het algemeen zijn kernplug-ins zoals appenders veel complexer en accepteren ze meestal verschillende configureerbare parameters.

Overweeg bijvoorbeeld een appender die logs schrijft naar Kafka:

Om dergelijke appenders te implementeren, biedt Log4j 2 een plug-in-builder-implementatie op basis van de Bouwer patroon:

@Plugin (name = "Kafka2", categorie = Core.CATEGORY_NAME) openbare klasse KafkaAppender breidt AbstractAppender uit {openbare statische klasse Builder implementeert org.apache.logging.log4j.core.util.Builder {@PluginBuilderAttribute ("naam") @ Vereist privé String naam; @PluginBuilderAttribute ("ip") privé String ipAddress; // ... aanvullende eigenschappen // ... getters en setters @Override openbare KafkaAppender build () {retourneer nieuwe KafkaAppender (getName (), getFilter (), getLayout (), true, nieuwe KafkaBroker (ipAddress, port, topic, partitie)); }} particuliere KafkaBroker-makelaar; private KafkaAppender (String naam, Filter filter, Layout layout, boolean ignoreExceptions, KafkaBroker broker) {super (naam, filter, layout, ignoreExceptions); this.broker = makelaar; } @Override public void append (LogEvent event) {connectAndSendToKafka (broker, event); }}

Kortom, we hebben een Bouwer class en annoteerde de parameters met @PluginBuilderAttribute. Door dit, KafkaAppender accepteert de Kafka-verbindingsparameters van de configuratie die hierboven wordt weergegeven.

3.3. Een bestaande plug-in uitbreiden

We kunnen ook een bestaande kernplug-in in Log4j 2 uitbreiden. We kunnen dit bereiken door onze plug-in dezelfde naam te geven als een bestaande plug-in. Als we bijvoorbeeld het RollingFileAppender:

@Plugin (name = "RollingFile", categorie = Core.CATEGORY_NAME, elementType = Appender.ELEMENT_TYPE) public class RollingFileAppender breidt AbstractAppender uit {public RollingFileAppender (String naam, filterfilter, layout layout) {super (naam, filter, layout); } @Override public void append (LogEvent-gebeurtenis) {}}

We hebben nu met name twee appenders met dezelfde naam. In een dergelijk scenario gebruikt Log4j 2 de appender die als eerste wordt ontdekt. We zullen in een later gedeelte meer zien over het ontdekken van plug-ins.

Houd er rekening mee dat Log4j 2 meerdere plug-ins met dezelfde naam ontmoedigt. Het is beter om in plaats daarvan een aangepaste plug-in te implementeren en die te gebruiken in de logboekconfiguratie.

4. Converter-plug-in

De lay-out is een krachtige plug-in in Log4j 2. Het stelt ons in staat om de outputstructuur voor onze logboeken te definiëren. We kunnen bijvoorbeeld gebruiken JsonLayout voor het schrijven van de logboeken in JSON-indeling.

Een andere dergelijke plug-in is de PatternLayout. In sommige gevallen wil een toepassing informatie zoals thread-ID, threadnaam of tijdstempel publiceren bij elke log-instructie. PatternLayout plug-in stelt ons in staat om dergelijke details via een conversiepatroonstring in de configuratie in te sluiten:

Hier, % d is het conversiepatroon. Log4j 2 converteert dit% d patroon via een DatePatternConverter die het conversiepatroon begrijpt en vervangt door de opgemaakte datum of tijdstempel.

Stel nu dat een toepassing die in een Docker-container wordt uitgevoerd, de containernaam bij elke logboekinstructie wil afdrukken. Om dit te doen, implementeren we een DockerPatterConverter en verander de bovenstaande configuratie om de conversietekenreeks op te nemen:

@Plugin (name = "DockerPatternConverter", categorie = PatternConverter.CATEGORY) @ConverterKeys ({"docker", "container"}) openbare klasse DockerPatternConverter breidt LogEventPatternConverter uit {private DockerPatternConverter (String [] -opties) {super ("Docker", " docker "); } openbare statische DockerPatternConverter newInstance (String [] opties) {retourneer nieuwe DockerPatternConverter (opties); } @Override public void format (LogEvent-gebeurtenis, StringBuilder toAppendTo) {toAppendTo.append (dockerContainer ()); } private String dockerContainer () {retourneer "container-1"; }}

Dus hebben we een maatwerk geïmplementeerd DockerPatternConverter vergelijkbaar met het datumpatroon. Het zal het conversiepatroon vervangen door de naam van de Docker-container.

Deze plug-in is vergelijkbaar met de kernplug-in die we eerder hebben geïmplementeerd. Met name is er slechts één annotatie die verschilt van de laatste plug-in. @ConverterKeys annotatie accepteert het conversiepatroon voor deze plug-in.

Als gevolg hiervan zal deze plug-in % docker of % container patroontekenreeks in de containernaam waarin de toepassing wordt uitgevoerd:

5. Opzoekplug-in

Opzoekplug-ins worden gebruikt om dynamische waarden toe te voegen aan het Log4j 2-configuratiebestand. Hiermee kunnen applicaties runtime-waarden insluiten in sommige eigenschappen in het configuratiebestand. De waarde wordt toegevoegd via een sleutelgebaseerde zoekactie in verschillende bronnen, zoals een bestandssysteem, database, enz.

Een van die plug-ins is de DateLookupPlugin waarmee een datumpatroon kan worden vervangen door de huidige systeemdatum van de applicatie:

  % d% p% c {1.} [% t]% m% n 

In dit voorbeeldconfiguratiebestand, RollingFileAppender gebruikt een datum zoek op waar de uitvoer de indeling MM-dd-jjjj zal hebben. Als gevolg hiervan schrijft Log4j 2 logboeken naar een uitvoerbestand met een datumachtervoegsel.

Net als bij andere plug-ins, biedt Log4j 2 veel bronnen voor zoekopdrachten. Bovendien maakt het het gemakkelijk om aangepaste zoekacties te implementeren als een nieuwe bron vereist is:

@Plugin (name = "kafka", categorie = StrLookup.CATEGORY) openbare klasse KafkaLookup implementeert StrLookup {@Override openbare String-lookup (String-sleutel) {return getFromKafka (key); } @Override public String lookup (LogEvent event, String key) {return getFromKafka (key); } private String getFromKafka (String topicName) {return "topic1-p1"; }}

Zo KafkaLookup lost de waarde op door een Kafka-onderwerp op te vragen. We zullen nu de onderwerpnaam uit de configuratie doorgeven:

  % d% p% c {1.} [% t]% m% n 

We hebben de datum lookup in ons eerdere voorbeeld met Kafka lookup die een query zal uitvoeren onderwerp-1.

Aangezien Log4j 2 alleen de standaardconstructor van een lookup-plug-in aanroept, hebben we de @BuienRadarNL zoals we deden in eerdere plug-ins.

6. Plugin Ontdekking

Laten we tot slot eens kijken hoe Log4j 2 de plug-ins in een applicatie ontdekt. Zoals we in de bovenstaande voorbeelden hebben gezien, hebben we elke plug-in een unieke naam gegeven. Deze naam fungeert als een sleutel, die Log4j 2 omzet in een plugin-klasse.

Er is een specifieke volgorde waarin Log4j 2 een zoekopdracht uitvoert om een ​​plugin-klasse op te lossen:

  1. Geserialiseerd plug-in lijstbestand in het log4j2-core bibliotheek. In het bijzonder een Log4j2Plugins.dat is verpakt in deze jar om de standaard Log4j 2-plug-ins weer te geven
  2. Vergelijkbaar Log4j2Plugins.dat bestand uit de OSGi-bundels
  3. Een door komma's gescheiden pakketlijst in het log4j.plugin.packages systeemeigenschap
  4. In programmatische Log4j 2-configuratie kunnen we bellen PluginManager.addPackages () methode om een lijst van pakketnamen
  5. Een door komma's gescheiden lijst met pakketten kan worden toegevoegd aan het Log4j 2-configuratiebestand

Als een vereiste moet annotatieverwerking zijn ingeschakeld om Log4j 2 in staat te stellen de plug-in op te lossen met de naam gegeven in de @Inpluggen annotatie.

Aangezien Log4j 2 namen gebruikt om de plug-in op te zoeken, wordt de bovenstaande volgorde belangrijk. Als we bijvoorbeeld twee plug-ins met dezelfde naam hebben, zal Log4j 2 de plug-in ontdekken die als eerste is opgelost. Daarom, als we een bestaande plug-in in Log4j 2 moeten uitbreiden, we moeten de plug-in in een aparte pot verpakken en vóór de log4j2-core.jar.

7. Conclusie

In dit artikel hebben we gekeken naar de brede categorieën plug-ins in Log4j 2. We hebben besproken dat hoewel er een uitputtende lijst van bestaande plug-ins is, we in sommige gevallen aangepaste plug-ins moeten implementeren.

Later hebben we gekeken naar de aangepaste implementatie van enkele nuttige plug-ins. Verder zagen we hoe Log4j 2 ons in staat stelt deze plug-ins een naam te geven en deze plug-in-naam vervolgens in het configuratiebestand te gebruiken. Ten slotte hebben we besproken hoe Log4j 2 plug-ins oplost op basis van deze naam.

Zoals altijd zijn alle voorbeelden beschikbaar op GitHub.