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:
- Core-plug-ins
- Omvormers
- Sleutelaanbieders
- Opzoeken
- 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:
- 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
- Vergelijkbaar Log4j2Plugins.dat bestand uit de OSGi-bundels
- Een door komma's gescheiden pakketlijst in het log4j.plugin.packages systeemeigenschap
- In programmatische Log4j 2-configuratie kunnen we bellen PluginManager.addPackages () methode om een lijst van pakketnamen
- 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.