Skip to content

Commit f9c5a39

Browse files
juanluisrpgithub-actions[bot]
authored andcommitted
Add Log4j2 JsonTemplateLayout support for JSON log output
Add the log4j-layout-template-json dependency and a new log4j2-json.xml configuration file that uses JsonTemplateLayout to produce structured JSON logs. Each log event includes @timestamp, level, loggerName, message, and exception fields suitable for ingestion by ELK stack or similar log aggregation tools. The file naming convention (log4j2-json.xml) makes the "JSON" option automatically available in the Admin Settings log level dropdown via the existing LoggingApi regex discovery mechanism. Harvester logs retain their existing PatternLayout as they are consumed separately.
1 parent 7868f2b commit f9c5a39

File tree

4 files changed

+229
-2
lines changed

4 files changed

+229
-2
lines changed

docs/manual/docs/install-guide/logging.md

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,13 @@ using Tomcat).
2626

2727
## Setting the Loglevel
2828

29-
GeoNetwork by default has 4 log levels: PROD, INDEX, SEARCH, DEV.
29+
GeoNetwork by default has 5 log levels: PROD, INDEX, SEARCH, DEV, JSON.
3030

3131
- PROD is the default option, it will only log critical errors.
3232
- INDEX is similar to PROD, but with extended logging around the indexation process.
3333
- Search is similar to PROD, but with extended logging around the search process.
3434
- DEV is the most extended level, all debug messages will be logged.
35+
- JSON outputs structured JSON logs suitable for log aggregation tools such as Elasticsearch/Kibana (ELK stack).
3536

3637
You can set the log level from the Admin --> Settings page.
3738

@@ -41,7 +42,23 @@ You can set the log level from the Admin --> Settings page.
4142

4243
GeoNetwork uses [Apache log4j](https://logging.apache.org/log4j) for logging.
4344
The log4j configuration files are located in the **`/WEB-INF/classes`** directory of the GeoNetwork web application:
44-
**`/WEB-INF/classes/log4j2.xml`**, **`/WEB-INF/classes/log4j2-dev.xml`** and **`/WEB-INF/classes/log4j2-index.xml`**.
45+
**`/WEB-INF/classes/log4j2.xml`**, **`/WEB-INF/classes/log4j2-dev.xml`**, **`/WEB-INF/classes/log4j2-index.xml`** and **`/WEB-INF/classes/log4j2-json.xml`**.
4546
The configuration file configures for each debug level at what severity messages will be logged.
4647

4748
The file used is determined by the log level set in the *Admin**Settings* page.
49+
50+
## JSON logging
51+
52+
The **JSON** log level uses `log4j2-json.xml` which replaces the default `PatternLayout` with Log4j2's `JsonTemplateLayout`. This produces structured JSON output on both the console and the rolling log file, making it suitable for ingestion by log aggregation tools such as the ELK stack (Elasticsearch, Logstash, Kibana).
53+
54+
Each log event is output as a JSON object with the following fields:
55+
56+
- `@timestamp` — event timestamp in ISO 8601 format
57+
- `level` — log level (e.g. ERROR, INFO, DEBUG)
58+
- `loggerName` — the logger that produced the event
59+
- `message` — the log message
60+
- `exception` — stack trace, if present
61+
62+
To activate JSON logging, go to *Admin**Settings* and select **JSON** from the log level dropdown.
63+
64+
The `JsonTemplateLayout` is provided by the `log4j-layout-template-json` library which is included in GeoNetwork's dependencies. Harvester logs continue to use plain-text `PatternLayout` as they are consumed separately.

pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -574,6 +574,11 @@
574574
<artifactId>log4j-jul</artifactId>
575575
<version>${log4j2.version}</version>
576576
</dependency>
577+
<dependency>
578+
<groupId>org.apache.logging.log4j</groupId>
579+
<artifactId>log4j-layout-template-json</artifactId>
580+
<version>${log4j2.version}</version>
581+
</dependency>
577582
<dependency>
578583
<groupId>org.apache.httpcomponents</groupId>
579584
<artifactId>httpclient</artifactId>

web/pom.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,10 @@
6969
<groupId>org.apache.logging.log4j</groupId>
7070
<artifactId>log4j-jul</artifactId>
7171
</dependency>
72+
<dependency>
73+
<groupId>org.apache.logging.log4j</groupId>
74+
<artifactId>log4j-layout-template-json</artifactId>
75+
</dependency>
7276
<dependency>
7377
<groupId>org.tuckey</groupId>
7478
<artifactId>urlrewritefilter</artifactId>
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<Configuration status="warn" dest="out">
3+
<Properties>
4+
<Property name="logs_dir">logs</Property>
5+
</Properties>
6+
<Appenders>
7+
<Console name="Console" target="SYSTEM_OUT">
8+
<JsonTemplateLayout>
9+
<EventTemplate><![CDATA[{
10+
"@timestamp": {
11+
"$resolver": "timestamp",
12+
"pattern": {
13+
"format": "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
14+
}
15+
},
16+
"level": {
17+
"$resolver": "level",
18+
"field": "name"
19+
},
20+
"loggerName": {
21+
"$resolver": "logger",
22+
"field": "name"
23+
},
24+
"message": {
25+
"$resolver": "message",
26+
"stringified": true
27+
},
28+
"exception": {
29+
"$resolver": "exception",
30+
"field": "stackTrace",
31+
"stackTrace": {
32+
"stringified": true
33+
}
34+
}
35+
}]]></EventTemplate>
36+
</JsonTemplateLayout>
37+
</Console>
38+
<RollingFile name="File">
39+
<filename>${sys:log_dir:-${logs_dir}}/geonetwork.log</filename>
40+
<filePattern>${sys:log_dir:-${logs_dir}}/geonetwork.log-%i.log</filePattern>
41+
<JsonTemplateLayout>
42+
<EventTemplate><![CDATA[{
43+
"@timestamp": {
44+
"$resolver": "timestamp",
45+
"pattern": {
46+
"format": "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
47+
}
48+
},
49+
"level": {
50+
"$resolver": "level",
51+
"field": "name"
52+
},
53+
"loggerName": {
54+
"$resolver": "logger",
55+
"field": "name"
56+
},
57+
"message": {
58+
"$resolver": "message",
59+
"stringified": true
60+
},
61+
"exception": {
62+
"$resolver": "exception",
63+
"field": "stackTrace",
64+
"stackTrace": {
65+
"stringified": true
66+
}
67+
}
68+
}]]></EventTemplate>
69+
</JsonTemplateLayout>
70+
<Policies>
71+
<SizeBasedTriggeringPolicy size="10 MB" />
72+
</Policies>
73+
<DefaultRolloverStrategy max="3" fileIndex="min"/>
74+
</RollingFile>
75+
<Routing name="Harvester">
76+
<Routes pattern="$${ctx:logfile}">
77+
<!-- value dynamically determines the name of the log file. -->
78+
<Route>
79+
<File name="harvester-${ctx:harvester}" fileName="${sys:log_dir:-${logs_dir}}/${ctx:logfile:-harvester_default.log}">
80+
<PatternLayout>
81+
<pattern>%date{ISO8601}{${ctx:timeZone}} %-5level [%logger] - %message%n</pattern>
82+
</PatternLayout>
83+
</File>
84+
</Route>
85+
</Routes>
86+
</Routing>
87+
</Appenders>
88+
<Loggers>
89+
<!-- Geonetwork module (and submodule) logging -->
90+
<Logger name="geonetwork" level="error" additivity="false">
91+
<AppenderRef ref="Console"/>
92+
<AppenderRef ref="File"/>
93+
</Logger>
94+
<Logger name="geonetwork.accessmanager" level="error"/>
95+
<Logger name="geonetwork.atom" level="info"/>
96+
<Logger name="geonetwork.backup" level="error"/>
97+
<Logger name="geonetwork.camel-harvester" level="error"/>
98+
<Logger name="geonetwork.cors" level="error"/>
99+
<Logger name="geonetwork.csw" level="error"/>
100+
<Logger name="geonetwork.csw.search" level="error"/>
101+
<Logger name="geonetwork.data.directory" level="error"/>
102+
<Logger name="geonetwork.database" level="error"/>
103+
<Logger name="geonetwork.databasemigration" level="info"/>
104+
<Logger name="geonetwork.datamanager" level="error"/>
105+
<Logger name="geonetwork.domain" level="error"/>
106+
<Logger name="geonetwork.editor" level="error"/>
107+
<Logger name="geonetwork.editorexpandelement" level="error"/>
108+
<Logger name="geonetwork.editorfillelement" level="error"/>
109+
<Logger name="geonetwork.encryptor" level="info"/>
110+
<Logger name="geonetwork.formatter" level="error"/>
111+
<Logger name="geonetwork.geoserver.publisher" level="error"/>
112+
<Logger name="geonetwork.geoserver.rest" level="error"/>
113+
<Logger name="geonetwork.harvest.wfs.features" level="debug"/>
114+
<Logger name="geonetwork.harvester" level="info">
115+
<AppenderRef ref="Harvester"/>
116+
</Logger>
117+
<Logger name="geonetwork.harvest-man" level="error"/>
118+
<Logger name="geonetwork.index" level="error"/>
119+
<Logger name="geonetwork.ldap" level="error"/>
120+
<Logger name="geonetwork.lucene" level="error"/>
121+
<Logger name="geonetwork.mef" level="error"/>
122+
<Logger name="geonetwork.resources" level="error"/>
123+
<Logger name="geonetwork.schemamanager" level="error"/>
124+
<Logger name="geonetwork.search" level="error"/>
125+
<Logger name="geonetwork.security" level="error"/>
126+
<Logger name="geonetwork.spatineo" level="error"/>
127+
<Logger name="geonetwork.sru" level="error"/>
128+
<Logger name="geonetwork.sru.search" level="error"/>
129+
<Logger name="geonetwork.thesaurus" level="error"/>
130+
<Logger name="geonetwork.thesaurus-man" level="error"/>
131+
<Logger name="geonetwork.userwatchlist" level="error"/>
132+
<Logger name="geonetwork.wro4j" level="error"/>
133+
<Logger name="geonetwork.doi" level="error"/>
134+
135+
<!-- Jeeves module and submodule logger configuration -->
136+
<Logger name="geonetwork.engine" level="error"/>
137+
<Logger name="geonetwork.monitor" level="error"/>
138+
<Logger name="geonetwork.resources" level="error"/>
139+
<Logger name="geonetwork.security" level="error"/>
140+
<Logger name="geonetwork.transformerFactory" level="error"/>
141+
<Logger name="geonetwork.xlinkprocessor" level="error"/>
142+
<Logger name="geonetwork.xmlresolver" level="error"/>
143+
144+
145+
<!-- Spring logging configuration -->
146+
<Logger name="org.springframework" level="error" additivity="false">
147+
<AppenderRef ref="Console"/>
148+
<AppenderRef ref="File"/>
149+
</Logger>
150+
<Logger name="org.springframework.beans" level="error"/>
151+
<Logger name="org.springframework.security" level="error"/>
152+
<Logger name="org.springframework.security.ldap" level="error"/>
153+
<Logger name="org.springframework.aop.framework.CglibAopProxy" level="error"/>
154+
155+
<Logger name="com.k_int" level="error" additivity="false">
156+
<AppenderRef ref="Console"/>
157+
<AppenderRef ref="File"/>
158+
</Logger>
159+
160+
<!-- JZKIT logging configuration -->
161+
<Logger name="org.jzkit" level="error" additivity="false">
162+
<AppenderRef ref="Console"/>
163+
<AppenderRef ref="File"/>
164+
</Logger>
165+
<Logger name="org.jzkit.a2j" level="error"/>
166+
<Logger name="org.jzkit.search.impl.LRUCache" level="error"/>
167+
168+
<Logger name="ro.isdc.wro.http" level="error" additivity="false">
169+
<AppenderRef ref="Console"/>
170+
<AppenderRef ref="File"/>
171+
</Logger>
172+
173+
<!-- Check domain/src/main/resources/config-spring-geonetwork.xml show_sql properties. -->
174+
<Logger name="org.hibernate.SQL" level="error" additivity="false">
175+
<AppenderRef ref="Console"/>
176+
<AppenderRef ref="File"/>
177+
</Logger>
178+
<Logger name="org.hibernate.type" level="error" additivity="false">
179+
<AppenderRef ref="Console"/>
180+
<AppenderRef ref="File"/>
181+
</Logger>
182+
<Logger name="org.hibernate.tool.hbm2ddl" level="error" additivity="false">
183+
<AppenderRef ref="Console"/>
184+
<AppenderRef ref="File"/>
185+
</Logger>
186+
<Logger name="org.xhtmlrenderer" level="error" additivity="false">
187+
<AppenderRef ref="Console"/>
188+
<AppenderRef ref="File"/>
189+
</Logger>
190+
191+
<Logger name="org.apache.camel" level="error" additivity="false">
192+
<AppenderRef ref="Console"/>
193+
<AppenderRef ref="File"/>
194+
</Logger>
195+
196+
<!-- Turn off logging except when explicitly declared above -->
197+
<Root level="off">
198+
<AppenderRef ref="File"/>
199+
</Root>
200+
</Loggers>
201+
</Configuration>

0 commit comments

Comments
 (0)