diff --git a/jena-fuseki/Dockerfile b/jena-fuseki/Dockerfile index f6b81f3..d949afe 100644 --- a/jena-fuseki/Dockerfile +++ b/jena-fuseki/Dockerfile @@ -73,6 +73,10 @@ COPY shiro.ini $FUSEKI_HOME/shiro.ini COPY docker-entrypoint.sh / RUN chmod 755 /docker-entrypoint.sh +COPY logging.sh $FUSEKI_HOME/logging.sh +RUN chmod 444 $FUSEKI_HOME/logging.sh + +COPY log4j2.properties $FUSEKI_HOME/log4j2.properties.templ COPY load.sh $FUSEKI_HOME/ COPY tdbloader $FUSEKI_HOME/ diff --git a/jena-fuseki/README.md b/jena-fuseki/README.md index 579d5b3..6e2e7a8 100644 --- a/jena-fuseki/README.md +++ b/jena-fuseki/README.md @@ -63,6 +63,21 @@ heap (default: 1200 MiB), set the `JVM_ARGS` environment with `-e`: docker run -p 3030:3030 -e JVM_ARGS=-Xmx2g stain/jena-fuseki +## Customize logging + +The Docker container outputs logs to stdout which can be read by using docker logs CONTAINER_ID. +The log entries follows the pattern of `09:44:30 INFO Entrypoint :: Initializing Apache Jena Fuseki`. +The first component is the timestamp of the event and it can changed with the LOG_FORMAT variable. + +For example if you were to start the container like this: + +```docker run -p 3030:3030 -e LOG_FORMAT="%y-%m-%d %H:%M:%S %% %Z" stain/jena-fuseki``` + +Then you would see log entries like theese: + +```24-12-14 09:44:30 % UTC INFO Entrypoint :: Initializing Apache Jena Fuseki``` + +Please read the man page for date(1) for information about strfttime format. ## Data persistence diff --git a/jena-fuseki/docker-entrypoint.sh b/jena-fuseki/docker-entrypoint.sh index 4134561..37d09bf 100644 --- a/jena-fuseki/docker-entrypoint.sh +++ b/jena-fuseki/docker-entrypoint.sh @@ -14,28 +14,54 @@ # See the License for the specific language governing permissions and # limitations under the License. +source $FUSEKI_HOME/logging.sh set -e +# Check if $LOG_FORMAT contains any invalid specifiers +# The regex ^((%[aAbBcdHIjmMpSUwWxXyYZFT%])|[^%])+$ matches strings that consist +# of valid strftime format specifiers (% followed by a valid character), any non-% character. +# It ensures the string contains valid format elements, including literals and percent signs. +# Valid examples, please date(1) manpage for information on the valid format: +# LOG_FORMAT="%y%m%d%H%M%S" ## 241214174530 +# LOG_FORMAT="%Y-%m-%d %H:%M:%S" ## 2024-12-14 17:45:30 +# LOG_FORMAT="%y-%m-%d %H:%M:%S %% %Z" ## 24-12-14 17:45:30 % UTC + +if [ ! -z "$LOG_FORMAT" ] && ! echo -n "$LOG_FORMAT" | grep -qE '^((%[aAbBcdHIjmMpSUwWxXyYZFT%])|[^%])+$'; then + invalid_strfttime_format=$LOG_FORMAT + unset LOG_FORMAT + log "WARN" "Invalid strfttime date format specifier in '$invalid_strfttime_format'." +fi + if [ ! -f "$FUSEKI_BASE/shiro.ini" ] ; then # First time - echo "###################################" - echo "Initializing Apache Jena Fuseki" - echo "" - cp "$FUSEKI_HOME/shiro.ini" "$FUSEKI_BASE/shiro.ini" + log "INFO" "###################################" + log "INFO" "Initializing Apache Jena Fuseki" + + cp ${FUSEKI_HOME}/shiro.ini ${FUSEKI_BASE}/shiro.ini + if [ -z "$ADMIN_PASSWORD" ] ; then ADMIN_PASSWORD=$(pwgen -s 15) - echo "Randomly generated admin password:" - echo "" - echo "admin=$ADMIN_PASSWORD" + log "INFO" "Randomly generated admin password:" + log "INFO" "admin=$ADMIN_PASSWORD" fi - echo "" - echo "###################################" + log "INFO" "###################################" fi if [ -d "/fuseki-extra" ] && [ ! -d "$FUSEKI_BASE/extra" ] ; then ln -s "/fuseki-extra" "$FUSEKI_BASE/extra" fi +if [ ! -z "$LOG_FORMAT" ]; then + cp ${FUSEKI_HOME}/log4j2.properties.templ ${FUSEKI_BASE}/log4j2.properties + # translate the format to so it works with Log4j2 + LOG4J2_FORMAT="%d{$(translate_to_log4j2_format "$LOG_FORMAT")}" + export LOG4J2_FORMAT + envsubst '${LOG4J2_FORMAT}' < "$FUSEKI_BASE/log4j2.properties" > "$FUSEKI_BASE/log4j2.properties.$$" && \ + mv "$FUSEKI_BASE/log4j2.properties.$$" "$FUSEKI_BASE/log4j2.properties" + unset LOG4J2_FORMAT + export LOGGING="-Dlog4j2.configurationFile=$FUSEKI_BASE/log4j2.properties" +fi + # $ADMIN_PASSWORD only modifies if ${ADMIN_PASSWORD} # is in shiro.ini if [ -n "$ADMIN_PASSWORD" ] ; then @@ -56,7 +82,7 @@ else fi # Wait until server is up -echo "Waiting for Fuseki to finish starting up..." +log "INFO" "Waiting for Fuseki to finish starting up..." until $(curl --output /dev/null --silent --head --fail http://localhost:3030); do sleep 1s done @@ -65,13 +91,13 @@ done printenv | egrep "^FUSEKI_DATASET_" | while read env_var do dataset=$(echo $env_var | egrep -o "=.*$" | sed 's/^=//g') - echo "Creating dataset $dataset" + log "INFO" "Creating dataset $dataset" curl -s 'http://localhost:3030/$/datasets'\ -u admin:${ADMIN_PASSWORD}\ -H 'Content-Type: application/x-www-form-urlencoded; charset=UTF-8'\ --data "dbName=${dataset}&dbType=${TDB_VERSION}" done -echo "Fuseki is available :-)" +log "INFO" "Fuseki is available :-)" unset ADMIN_PASSWORD # Don't keep it in memory # rejoin our exec diff --git a/jena-fuseki/log4j2.properties b/jena-fuseki/log4j2.properties new file mode 100644 index 0000000..5039b00 --- /dev/null +++ b/jena-fuseki/log4j2.properties @@ -0,0 +1,64 @@ +## Licensed under the terms of http://www.apache.org/licenses/LICENSE-2.0 +status = error +name = PropertiesConfig +filters = threshold + +filter.threshold.type = ThresholdFilter +filter.threshold.level = ALL + +appender.console.type = Console +appender.console.name = OUT +appender.console.target = SYSTEM_ERR +appender.console.layout.type = PatternLayout +appender.console.layout.pattern = $LOG4J2_FORMAT %-5p %-15c{1} :: %m%n + +rootLogger.level = INFO +rootLogger.appenderRef.stdout.ref = OUT + +logger.jena.name = org.apache.jena +logger.jena.level = INFO + +logger.arq-exec.name = org.apache.jena.arq.exec +logger.arq-exec.level = INFO + +logger.arq-info.name = org.apache.jena.arq.info +logger.arq-info.level = INFO + +logger.riot.name = org.apache.jena.riot +logger.riot.level = INFO + +logger.fuseki.name = org.apache.jena.fuseki +logger.fuseki.level = INFO + +logger.fuseki-fuseki.name = org.apache.jena.fuseki.Fuseki +logger.fuseki-fuseki.level = INFO + +logger.fuseki-server.name = org.apache.jena.fuseki.Server +logger.fuseki-server.level = INFO + +logger.fuseki-config.name = org.apache.jena.fuseki.Config +logger.fuseki-config.level = INFO + +logger.fuseki-admin.name = org.apache.jena.fuseki.Admin +logger.fuseki-admin.level = INFO + +logger.jetty.name = org.eclipse.jetty +logger.jetty.level = WARN + +logger.shiro.name = org.apache.shiro +logger.shiro.level = WARN + +# Hide bug with Shiro 1.5.0+, 2.0.0 +logger.shiro-realm.name = org.apache.shiro.realm.text.IniRealm +logger.shiro-realm.level = ERROR + +# This goes out in NCSA format +appender.plain.type = Console +appender.plain.name = PLAIN +appender.plain.layout.type = PatternLayout +appender.plain.layout.pattern = %m%n + +logger.fuseki-request.name = org.apache.jena.fuseki.Request +logger.fuseki-request.additivity = false +logger.fuseki-request.level = OFF +logger.fuseki-request.appenderRef.plain.ref = PLAIN \ No newline at end of file diff --git a/jena-fuseki/logging.sh b/jena-fuseki/logging.sh new file mode 100644 index 0000000..a351a23 --- /dev/null +++ b/jena-fuseki/logging.sh @@ -0,0 +1,39 @@ +#!/bin/sh + +log() { + local level="$1" + shift + local message="$*" + local timestamp + if [ -z "$LOG_FORMAT" ]; then + LOG_FORMAT="%H:%M:%S" + fi + timestamp=$(date +"$LOG_FORMAT") # Get current time in HH:MM:SS format, which is used by Fuseki logging + printf "%s %-5s %-15s :: %s\n" "$timestamp" "$level" Entrypoint "$message" +} + +translate_to_log4j2_format() { + local strfttime_format="$1" + # Define translation patterns using Bash parameter substitutions + local log4j_format="$strfttime_format" + log4j_format="${log4j_format//%a/EEE}" # Abbreviated weekday name + log4j_format="${log4j_format//%A/EEEE}" # Full weekday name + log4j_format="${log4j_format//%b/MMM}" # Abbreviated month name + log4j_format="${log4j_format//%B/MMMM}" # Full month name + log4j_format="${log4j_format//%d/dd}" # Day of the month + log4j_format="${log4j_format//%H/HH}" # Hour (00-23) + log4j_format="${log4j_format//%I/hh}" # Hour (01-12) + log4j_format="${log4j_format//%j/DDD}" # Day of the year + log4j_format="${log4j_format//%m/MM}" # Month + log4j_format="${log4j_format//%M/mm}" # Minute + log4j_format="${log4j_format//%p/a}" # AM or PM + log4j_format="${log4j_format//%S/ss}" # Second + log4j_format="${log4j_format//%y/yy}" # Year (last two digits) + log4j_format="${log4j_format//%Y/yyyy}" # Full year + log4j_format="${log4j_format//%Z/z}" # Time zone + log4j_format="${log4j_format//%F/yyyy-MM-dd}" # Full date + log4j_format="${log4j_format//%T/HH:mm:ss}" # Full time + log4j_format="${log4j_format//%%/%}" # A escaped % + + echo $log4j_format +}