This guide is for operators who need to change where Wikantik writes logs, how they rotate, or what level each component logs at. It covers the Log4j2 setup, the wikantik.use.external.logconfig switch, and the log file locations for bare-metal Tomcat and the container.
wikantik.use.external.logconfigWikantik routes all logging through Log4j2:
Application Code → SLF4J API → log4j-slf4j2-impl → Log4j2 Core
The relevant JARs ship in WEB-INF/lib/ of the deployed WAR; exact
versions are pinned in the root pom.xml's <dependencyManagement>:
slf4j-api — Facade APIlog4j-slf4j2-impl — Routes SLF4J 2.x to Log4j2log4j-api, log4j-core — Log4j2 API + implementation| Environment | Config file path |
|---|---|
| Bare-metal local Tomcat | tomcat/tomcat-11/lib/log4j2.xml |
Container (${CATALINA_HOME}) | /usr/local/tomcat/lib/log4j2.xml |
| Container (baked config) | docker/config/log4j2-docker.xml (copied to /usr/local/tomcat/lib/log4j2.xml by the Dockerfile) |
For the bare-metal install, bin/deploy-local.sh copies
wikantik-war/src/main/config/tomcat/log4j2-local.xml.template to
tomcat/tomcat-11/lib/log4j2.xml on first deploy (the file is preserved on
subsequent deploys unless you delete it). The template uses a portable path,
${sys:catalina.base}/logs/wikantik, so it works regardless of the absolute
location of the Tomcat directory.
The container build bakes docker/config/log4j2-docker.xml into the image, and
docker/entrypoint.sh writes wikantik.use.external.logconfig = true into the
generated wikantik-custom.properties, so the baked file is the active
configuration. It writes wikantik.log and security.log to /var/wikantik/logs
and sends ECS-format JSON to the console (docker logs). Container operators can
volume-mount a custom log4j2.xml into /usr/local/tomcat/lib/ to override it.
wikantik.use.external.logconfig worksWhen false (default): the appender.*, logger.* and rootLogger.*
entries declared in ini/wikantik.properties (a console appender plus a rolling
file under ${sys:java.io.tmpdir}/wikantik.log) replace whatever log4j2.xml
configured. A log4j2.xml in lib/ is therefore ignored on bare-metal until
you set the property to true.
wikantik.properties / wikantik-custom.propertiesappender, logger, rootLogger, filter,
status, dest, name, properties, property, or log4j2PropertiesConfigurationFactoryWhen true:
log4j2-test.xml, log4j2.xml, log4j2.properties-Dlog4j2.configurationFile=/path/to/config.xmlBecause log4j2.xml is placed in tomcat/tomcat-11/lib/ (bare-metal) or
/usr/local/tomcat/lib/ (container), it is on the server classloader path and
Log4j2 picks it up automatically when wikantik.use.external.logconfig = true.
Use a separate log4j2.xml file with full Log4j2 capabilities.
wikantik.use.external.logconfig = true
Also remove any existing broken logging lines (log4j.* properties are Log4j 1.x
syntax and will be ignored).
bin/deploy-local.sh copies the template to tomcat/tomcat-11/lib/log4j2.xml on
first deploy only. In the container the active file is
/usr/local/tomcat/lib/log4j2.xml (see above).
To customize bare-metal logging, edit that file in place (it is not overwritten by
subsequent bin/redeploy.sh or bin/deploy-local.sh runs). If you need to start from the template again, copy it:
# Bare-metal: restore from template
cp wikantik-war/src/main/config/tomcat/log4j2-local.xml.template \
tomcat/tomcat-11/lib/log4j2.xml
The template uses ${sys:catalina.base}/logs/wikantik as the log directory,
which resolves correctly regardless of the absolute install path. It writes
wikantik.log, security.log and access.log there.
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN" name="Wikantik-Production" monitorInterval="30">
<Properties>
<!-- Portable path — resolves to tomcat/tomcat-11/logs/wikantik on bare-metal -->
<Property name="logDir">${sys:catalina.base}/logs/wikantik</Property>
<Property name="pattern">%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n</Property>
</Properties>
<Appenders>
<!-- Console for catalina.out -->
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%highlight{[%-5level]} %d{HH:mm:ss.SSS} [%t] %c{1.} - %msg%n"/>
</Console>
<!-- Main application log with daily rotation -->
<RollingFile name="AppLog"
fileName="${logDir}/wikantik.log"
filePattern="${logDir}/wikantik-%d{yyyy-MM-dd}-%i.log.gz">
<PatternLayout pattern="${pattern}"/>
<Policies>
<TimeBasedTriggeringPolicy interval="1"/>
<SizeBasedTriggeringPolicy size="50MB"/>
</Policies>
<DefaultRolloverStrategy max="30"/>
</RollingFile>
<!-- Security audit log -->
<RollingFile name="SecurityLog"
fileName="${logDir}/security.log"
filePattern="${logDir}/security-%d{yyyy-MM-dd}.log.gz">
<PatternLayout pattern="%d{ISO8601} %level %msg%n"/>
<Policies>
<TimeBasedTriggeringPolicy interval="1"/>
</Policies>
<DefaultRolloverStrategy max="90"/>
</RollingFile>
</Appenders>
<Loggers>
<!-- Wikantik core -->
<Logger name="com.wikantik" level="INFO" additivity="false">
<AppenderRef ref="Console"/>
<AppenderRef ref="AppLog"/>
</Logger>
<!-- Security events (login, logout, access denied) -->
<Logger name="SecurityLog" level="INFO" additivity="false">
<AppenderRef ref="SecurityLog"/>
</Logger>
<!-- Database connection issues -->
<Logger name="org.apache.tomcat.dbcp" level="WARN"/>
<!-- Lucene search indexing -->
<Logger name="com.wikantik.search" level="INFO"/>
<!-- Root: catch-all -->
<Root level="WARN">
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration>
# Bare-metal
tomcat/tomcat-11/bin/shutdown.sh && tomcat/tomcat-11/bin/startup.sh
# Or use the fast redeploy path (also rotates catalina.out)
bin/redeploy.sh
# Check catalina.out for startup messages (bare-metal)
tail -f tomcat/tomcat-11/logs/catalina.out
# Check new log files are created
ls -la tomcat/tomcat-11/logs/wikantik/
| Environment | Tomcat base | Log directory | Config file |
|---|---|---|---|
| Bare-metal local | tomcat/tomcat-11/ | tomcat/tomcat-11/logs/wikantik/ | tomcat/tomcat-11/lib/log4j2.xml |
| Container | /usr/local/tomcat/ | /var/wikantik/logs/ (set by logDir in log4j2-docker.xml) | /usr/local/tomcat/lib/log4j2.xml |
The bare-metal template uses ${sys:catalina.base}, which resolves to the Tomcat
base directory at runtime. The container file hard-codes /var/wikantik/logs.
The logDir property is referenced throughout the config as ${logDir}, so changing
it in one place updates all appenders:
<Properties>
<!-- Options: -->
<!-- Portable (recommended): -->
<Property name="logDir">${sys:catalina.base}/logs/wikantik</Property>
<!-- Standard Linux system log location: -->
<!-- <Property name="logDir">/var/log/wikantik</Property> -->
<!-- Environment variable with fallback: -->
<!-- <Property name="logDir">${env:WIKANTIK_LOG_DIR:-/var/log/wikantik}</Property> -->
</Properties>
| Lookup | Description | Example (bare-metal local) |
|---|---|---|
${sys:catalina.base} | Tomcat base directory | <repo>/tomcat/tomcat-11 |
${sys:catalina.home} | Tomcat home directory | <repo>/tomcat/tomcat-11 |
${sys:java.io.tmpdir} | Java temp directory | /tmp |
${sys:user.home} | User home directory | /home/jakefear |
Important: Ensure the Tomcat process has write permission to the chosen log
directory. bin/deploy-local.sh creates tomcat/tomcat-11/logs/wikantik/
automatically.
| Level | Description |
|---|---|
| TRACE | Finest-grained debug information |
| DEBUG | Detailed debug information |
| INFO | General operational messages |
| WARN | Warning conditions |
| ERROR | Error conditions |
| FATAL | Critical errors causing shutdown |
| Token | Description |
|---|---|
%d{pattern} | Date/time with specified format |
%t | Thread name |
%p or %level | Log level |
%c{n} | Logger name (n = precision) |
%C{n} | Class name |
%M | Method name |
%L | Line number |
%m or %msg | Log message |
%n | Newline |
%X{key} | Thread context map (MDC) value — used by RequestCorrelationFilter for X-Request-Id |
%highlight{pattern} | ANSI color highlighting |
| Logger | Purpose |
|---|---|
com.wikantik | All Wikantik classes |
com.wikantik.ui.WikiServletFilter | Page request handling |
com.wikantik.auth | Authentication/authorization |
com.wikantik.search | Lucene search indexing |
com.wikantik.providers | Page/attachment providers |
SecurityLog | Security audit events |
SpamLog | Spam filter events |
Add monitorInterval to automatically reload config changes (value in seconds):
<Configuration status="WARN" monitorInterval="30">
Wrap appenders for non-blocking logging:
<Appenders>
<RollingFile name="AppLogFile" .../>
<Async name="AppLog">
<AppenderRef ref="AppLogFile"/>
</Async>
</Appenders>
<Syslog name="Syslog"
host="logserver.example.com"
port="514"
protocol="UDP"
facility="LOCAL0"
appName="wikantik"/>
<RollingFile name="AppLog" ...>
<RegexFilter regex=".*password.*" onMatch="DENY" onMismatch="ACCEPT"/>
<PatternLayout .../>
</RollingFile>
log4j2.xml template deployed by bin/deploy-local.sh:
wikantik-war/src/main/config/tomcat/log4j2-local.xml.templatedocker/config/log4j2-docker.xmlwikantik.properties defaults:
wikantik-main/src/main/resources/ini/wikantik.properties
Cluster Hub: Wikantik Platform Development Hub: Architecture, MCP Protocols, and Agent Briefings (wikantik-development)