LOG4J2 in Datenbank

DaBe1812

Mitglied
Hi,

ich weiß, viele von euch mögen Frameworks nicht so gerne, aber ich habe noch nichts anderes gefunden, was das Out-of-the-Box können soll.
Also vorhaben ist einfach erklärt: ich möchte das bestehende Logging in eine Tabelle umleiten, weil wir vom Unternehmen jede Nacht die Logfiles wegkopiert werden und ich somit über die Anwendung immer nur das Logging bis zur jeweiligen Nacht einsehen kann. Läuft also ein Job um 22:30 Uhr, dann muss ich erst die alten Logs vom Vortag anfordern und diese dann durchsuchen, in der Anwendung habe ich mir mittlerweile eine Textparser gebaut, damit ich ein wenig komfortabler suchen kann.

Ausgangslage:
WLP Server mit Serververwalteten Datenbanken, d.h. ich habe auf die Config im WLP nur bedingt Einfluss und darf nicht frei alle Parameter ändern. In der Anwendung eine persistence.xml. Tabelle auf der Datenbank habe ich mir selbst angelegt.
SQL:
CREATE TABLE LOGGING
(
    id        NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    log_date  TIMESTAMP,
    log_level VARCHAR2(10),
    marker    VARCHAR2(255),
    logger    VARCHAR2(255),
    message   CLOB,
    throw     CLOB
);
Das hatte ich mir aus einem Bespiel kopiert. Marker nutzen wir (noch) nicht, aber ich finde das Prinzip spannend und damit ließe sich das Log in der Anwendung nochmal ganz anders Clustern und die Spalte throw habe ich keine Ahnung, was ich damit anfangen soll.

Die meisten Informationen habe ich versucht mir HIER zu holen, aber das klappt irgendwie alles nicht so einfach.

in der log4j2.xml hatte ich diese Konfiguration eingestellt
XML:
<JDBC name="DATABASE" tableName="LOGGING" >
    <DataSource jndiName="jdbc/DataSource" />
    <Column name="log_level" pattern="%level" />
    <Column name="marker" pattern="%marker" />
    <Column name="logger" pattern="%logger" />
    <Column name="message" pattern="%message" isClob="true" />
    <Column name="throw" pattern="%ex{full}" isClob="true" />
</JDBC>
Damit habe ich auch Logs in die Datenbank bekommen, allerdings hat jedes Log eine Exception geworfen mit der Quintessenz
Code:
Caused by: java.sql.SQLException: DSRA9350E: Die Operation Connection.commit ist in einer globalen Transaktion nicht zulässig.
Im Hintergrund ist auch eine Oracle mit AutoCommit, also sollte log4j eigentlich nicht nochmal zusätzlich versuchen zu committen.

Das zweite Problem ist, dass er für die Message die Spalte nicht richtig verwendet. Die ist CLOB und in der Konfiguration steht auch CLOB, aber der Logger schreibt jede Zeile in einen eigenen Record, das heißt ein Stacktrace hat dann viele Datensätze in der Datenbank.

Laut der hinterlegten Seite sollte die Config aber eigentlich eher so aussehen:
XML:
<JDBC name="DATABASE" tableName="LOGGING" >
    <DataSource jndiName="jdbc/DataSource" />
    <ColumnMapping name="log_date" />
    <ColumnMapping name="log_level" pattern="%level"/>
    <ColumnMapping name="marker" pattern="%marker"/>
    <ColumnMapping name="logger" pattern="%logger"/>
    <ColumnMapping name="message" pattern="%message"/>
</JDBC>
Dann erhalte ich aber folgenden Fehler:
Code:
Caused by: java.sql.SQLException: Fehlender IN- oder OUT-Parameter auf Index:: 5
Den verstehe ich garnicht.

Ich hoffe ihr könnt mir helfen.
 
Das klingt unglaublich kompliziert, binde ich den dann ganz einfach über das ConnectionFactory ein?
Die Datenbank-Verbindung so einstellen, weiß ich nicht, ob wir das "dürfen", sprich ab mir das als Config überhaupt offen steht.

Ich hatte auch schon über das Einbinden über JPA nachgedacht, da kann ich in meiner persistenxe.xml die Datenbankverbindung als non-tx einstellen und wäre raus. In dem Kapitel steht aber, dass
Code:
Due to breaking changes in the underlying API, the JPA Appender cannot be used with Jakarta Persistence API 3.0 or later.
Und ich verwende in meiner Server.xml
Code:
<feature>persistence-3.0</feature>
Also denke ich, dass die Lösung auch schon raus ist.
 
Also es gibt die server.xml, da ist der ganze Connect-String, nenne ich es mal, drin. Also Benutzername und Passwort, die SID, der Timeout und sowas und das wird der Anwendung per JNDI-Name reingereicht. Insgesamt haben wir dort drei Datenbanken hinterlegt.
In der Anwendung greife ich den JNDI Namen in der persistence.xml wieder auf, verbinde die Entity-Klassen mit dem jeweiligen JNDI Namen und hier verwende ich sogar einen JNDI doppelt, nämlich einmal als JTA und einmal als RESOURCE_LOCAL, weil ich in einer Klasse explizit die Transactions selber steuern muss, um Timer-Jobs zu planen.

Und dann ganz normal den Namen der PersistenceUnit in den jeweiligen Resourcen-Klassen zu den Entities, um sie da als @PersistenceContext zu verwenden
 
Gut, dass in der Mail, die ich gestern bekommen hatte noch der Original-Text drin stand. Das war nämlich fast die Lösung. Statt dem vorgeschlagenen now() musste einfach noch CURRENT_TIMESTAMP als literal rein und jetzt geht das mit dem Mapping, gefällt mir erstmal besser, weil das gemäß der offiziellen Doku ist.

In der server.xml habe ich die Datasource geändert:
XML:
<dataSource id="atcDB" jndiName="jdbc/DataSource" type="javax.sql.XADataSource" transactional="false">
    <connectionManager agedTimeout="30m" connectionTimeout="10s" maxPoolSize="300" minPoolSize="30" defaultAutoCommit="true" />
    <jdbcDriver libraryRef="OracleJDBC" />
    <properties.oracle URL="jdbc:oracle:thin:@SERVER:1111/SESSION" password="*****" user="*****" />
</dataSource>
transactional="false" macht scheinbar den Trick und soweit ich sehen kann funktioniert der Rest, der über die Verbindung geht noch ganz normal. Ich denke mal, dass da JPA einfach weiter seinen Dienst tut und die Transaktionen eben selbst verwaltet.

Und mein letztes Problem war scheinbar nur meiner eigenen Unfähigkeit geschuldet. Ich habe im Standard-Vaadin-Blabla, was beim Starten des Servers so im Log landet eine Ausgabe fälschlich als Stack interpretiert. Diese ist aber auch im normalen Log in mehreren Zeilen, also immer mit Timestamp und Level und Co. Also habe ich mal eine Exception provoziert. Musste noch eine Spalte ergänzen, in der die Exception landet, und jetzt sieht in der Datenbank und im File-Log alles genau so aus, wie ich das gerne hatte.
Jetzt muss ich nur noch mit der Config-Abteilung klären, ob wir das transactional="false" auch in die Config auf dem Server machen können.
 
Sorry, mir kam etwas dazwischen. Eigentlich wollte ich das noch testen, weil ich kein Beispiel zu Hand hatte. Das war nur so aus dem Gedächtnis.
Interessant ist aber: Du bekommst Mails? - Bei mir funktioniert das gefühlt schon Jahre nicht mehr. Ich denke andere Nutzer haben das Problem auch.
Wie du mit Transaktionen umgehst, da habe ich so meine Bedenken. Bei JEE/JakartaEE verwaltet eigentlich grundsätzlich der Server die Transaktionen. Das ist wichtig, da es neben JPA noch andere Dinge gibt wie EJBs, Timer, Events, .... Der Standard bringt da ganz viel mit. Server laufen auch oft in Clustern. Dann spannen sich Transaktionen nicht unbedingt nur über einen Server, noch nicht mal nur über eine DB. Wenn alles richtig funktioniert, dann sind Transaktionen in JPA nur eine Fassade für die Transaktionen des Servers.
 
Grundsätzlich würde ich eher vorschlagen für die Log Ausgaben eine eigene Datasource zu konfigurieren. Die über die gleiche zu schicken, wie die regulären Statements macht mir Bauchschmerzen - bzgl. Transaktionen, Pooling etc.
 
Du bekommst Mails?
Ja, bei meinem "alten" Account konnte ich auch machen, was ich will, aber es kamen keine Mails mehr, aber seit der gelöscht wurde (ich weiß übrigens immer noch nicht, warum) und ich einen neuen gemacht habe, geht es wieder. Das macht das Leben so viel schöner...

Wie du mit Transaktionen umgehst, da habe ich so meine Bedenken.
ICH gehe garnicht mit Transaktionen um. Das überlasse ich alles der Anwendung. Hier und da mal ein @Transactional und dann soll der EntityManager seine Arbeit tun. Lediglich für meinen Timer habe ich eine Verbindung, wo ich mich um die Transaktionen selbst kümmern muss, aber damit bin ich scheinbar keine Ausnahme, der Quarz Timer erfordert auch eine Unmanaged-Verbindung.
Grundsätzlich würde ich eher vorschlagen für die Log Ausgaben eine eigene Datasource zu konfigurieren.
Könnte ich auch noch machen. Also neue Datasource aber selbe Datenbank? Das war in meinem Kopf eher problematisch, als alles in einer Hand zu lassen, aber da fehlt mir definitiv der Vergleich. Werde ich Montag mal anleiern, die zusätzliche DS tut ja keinem weh.
 

Zurück
Oben