From 6e8e8cb8d7b69e42dbc4c3702f6778d58d4a2371 Mon Sep 17 00:00:00 2001 From: Michael Jeanson Date: Thu, 6 Jan 2022 14:36:46 -0500 Subject: [PATCH] Add a Log4j 2.x Java agent This adds a new agent to the LTTng-UST Java agents suite supporting the Log4j 2.x logging backend. This new agent can be enabled with the following configure options : $ export CLASSPATH=/path/to/log4j-core.jar:/path/to/log4j-api.jar $ ./configure --enable-java-agent-log4j2 This backport differs from the master branch for the '--enable-java-agent-all' option won't select this new agent since we wanted to avoid introducing a new dependency in existing configurations. The name of the new agent jar file is "lttng-ust-agent-log4j2.jar". It will be installed in the arch-agnostic "$prefix/share/java" path e.g: "/usr/share/java". It uses the same jni library "liblttng-ust-log4j-jni.so" as the Log4j 1.x agent. The agent was designed as a mostly drop-in replacement for applications upgrading from Log4j 1.x to 2.x. It requires no modification to the tracing configuration as it uses the same domain "-l / LOG4J" and the loglevels integer representations are converted to the Log4j 1.x values (excluding custom loglevels). The recommended way to use this agent with Log4j 2.x is to add an "Lttng" Appender with an arbiraty name and associate it with one or more Logger using an AppenderRef. For example, here is a basic log4j2 xml configuration that would send all logging statements exlusively to an lttng appender: More examples can be found in the 'doc/examples' directory. The implementation of the appender is based on this[1] great guide by Keith D. Gregory which is so much more detailed than the official documentation, my thanks to him. [1] https://www.kdgregory.com/index.php?page=logging.log4j2Plugins Change-Id: I34593c9a4c3140c8839cef8b58cc85745fe9f47f Signed-off-by: Michael Jeanson Signed-off-by: Mathieu Desnoyers --- .gitignore | 2 + README.md | 2 +- configure.ac | 39 +++- doc/examples/Makefile.am | 50 ++++- doc/examples/java-jul/Makefile | 2 +- .../{Hello.java => HelloLog4j.java} | 4 +- doc/examples/java-log4j/Makefile | 4 +- doc/examples/java-log4j/run | 8 +- doc/examples/java-log4j2-basic/.intree | 0 .../java-log4j2-basic/HelloLog4j2.java | 45 ++++ doc/examples/java-log4j2-basic/Makefile | 48 +++++ doc/examples/java-log4j2-basic/log4j2.xml | 16 ++ doc/examples/java-log4j2-basic/run | 38 ++++ .../java-log4j2-ctx/HelloLog4j2Ctx.java | 71 +++++++ doc/examples/java-log4j2-ctx/Makefile | 48 +++++ doc/examples/java-log4j2-ctx/log4j2.ctx1.xml | 16 ++ doc/examples/java-log4j2-ctx/log4j2.ctx2.xml | 16 ++ doc/examples/java-log4j2-ctx/run | 38 ++++ doc/examples/java-log4j2-prog/.intree | 0 .../java-log4j2-prog/HelloLog4j2Prog.java | 76 +++++++ doc/examples/java-log4j2-prog/Makefile | 48 +++++ doc/examples/java-log4j2-prog/run | 38 ++++ doc/java-agent.txt | 31 ++- liblttng-ust-java-agent/java/Makefile.am | 4 + .../java/lttng-ust-agent-all/Manifest.txt | 2 +- .../java/lttng-ust-agent-log4j2/Makefile.am | 60 ++++++ .../java/lttng-ust-agent-log4j2/Manifest.txt | 8 + .../ust/agent/log4j2/LttngLog4j2Agent.java | 108 ++++++++++ .../ust/agent/log4j2/LttngLog4j2Api.java | 21 ++ .../ust/agent/log4j2/LttngLogAppender.java | 198 ++++++++++++++++++ liblttng-ust-java-agent/jni/Makefile.am | 2 +- liblttng-ust-java-agent/jni/log4j/Makefile.am | 16 +- .../jni/log4j/lttng_ust_log4j.c | 4 +- .../jni/log4j/lttng_ust_log4j2.c | 129 ++++++++++++ .../jni/log4j/lttng_ust_log4j_tp.c | 16 ++ .../jni/log4j/lttng_ust_log4j_tp.h | 48 +++++ 36 files changed, 1220 insertions(+), 36 deletions(-) rename doc/examples/java-log4j/{Hello.java => HelloLog4j.java} (97%) create mode 100644 doc/examples/java-log4j2-basic/.intree create mode 100644 doc/examples/java-log4j2-basic/HelloLog4j2.java create mode 100644 doc/examples/java-log4j2-basic/Makefile create mode 100644 doc/examples/java-log4j2-basic/log4j2.xml create mode 100755 doc/examples/java-log4j2-basic/run create mode 100644 doc/examples/java-log4j2-ctx/HelloLog4j2Ctx.java create mode 100644 doc/examples/java-log4j2-ctx/Makefile create mode 100644 doc/examples/java-log4j2-ctx/log4j2.ctx1.xml create mode 100644 doc/examples/java-log4j2-ctx/log4j2.ctx2.xml create mode 100755 doc/examples/java-log4j2-ctx/run create mode 100644 doc/examples/java-log4j2-prog/.intree create mode 100644 doc/examples/java-log4j2-prog/HelloLog4j2Prog.java create mode 100644 doc/examples/java-log4j2-prog/Makefile create mode 100755 doc/examples/java-log4j2-prog/run create mode 100644 liblttng-ust-java-agent/java/lttng-ust-agent-log4j2/Makefile.am create mode 100644 liblttng-ust-java-agent/java/lttng-ust-agent-log4j2/Manifest.txt create mode 100644 liblttng-ust-java-agent/java/lttng-ust-agent-log4j2/org/lttng/ust/agent/log4j2/LttngLog4j2Agent.java create mode 100644 liblttng-ust-java-agent/java/lttng-ust-agent-log4j2/org/lttng/ust/agent/log4j2/LttngLog4j2Api.java create mode 100644 liblttng-ust-java-agent/java/lttng-ust-agent-log4j2/org/lttng/ust/agent/log4j2/LttngLogAppender.java create mode 100644 liblttng-ust-java-agent/jni/log4j/lttng_ust_log4j2.c create mode 100644 liblttng-ust-java-agent/jni/log4j/lttng_ust_log4j_tp.c create mode 100644 liblttng-ust-java-agent/jni/log4j/lttng_ust_log4j_tp.h diff --git a/.gitignore b/.gitignore index 2050e04f..00361c4b 100644 --- a/.gitignore +++ b/.gitignore @@ -88,6 +88,7 @@ tests/gcc-weak-hidden/test_gcc_weak_hidden # Java agent library *.class +META-INF lttng-ust-agent*.jar liblttng-ust-agent.jar classnoinst.stamp @@ -98,6 +99,7 @@ log4j-jni-header.stamp org_lttng_ust_agent_context_LttngContextApi.h org_lttng_ust_agent_jul_LttngJulApi.h org_lttng_ust_agent_log4j_LttngLog4jApi.h +org_lttng_ust_agent_log4j2_LttngLog4j2Api.h # Python agent python-lttngust/lttngust/__init__.py diff --git a/README.md b/README.md index fc2cc6c0..e611826a 100644 --- a/README.md +++ b/README.md @@ -230,7 +230,7 @@ This package contains the following elements: JAR library to provide an LTTng-UST logging back-end for Java applications using Java Util Logging or Log4j. (Configure with `--enable-java-agent-jul` or `--enable-java-agent-log4j` or - `--enable-java-agent-all`). + `--enable-java-agent-log4j2` or `--enable-java-agent-all`). - `liblttng-ust-libc-wrapper`: an example library that can be preloaded to instrument some calls to libc (currently `malloc()` and `free()`) and to POSIX threads (mutexes currently instrumented) in diff --git a/configure.ac b/configure.ac index 8a16ec76..019d9753 100644 --- a/configure.ac +++ b/configure.ac @@ -337,25 +337,39 @@ AS_HELP_STRING([--enable-java-agent-jul], [build the LTTng UST Java agent with J ]) AC_ARG_ENABLE([java-agent-log4j], [ -AS_HELP_STRING([--enable-java-agent-log4j], [build the LTTng UST Java agent with Log4j support [default=no]]) +AS_HELP_STRING([--enable-java-agent-log4j], [build the LTTng UST Java agent with Log4j 1.x support [default=no]]) ], [ java_agent_log4j=$enableval ], [ java_agent_log4j=no ]) +AC_ARG_ENABLE([java-agent-log4j2], [ +AS_HELP_STRING([--enable-java-agent-log4j2], [build the LTTng UST Java agent with Log4j 2.x support [default=no]]) +], [ + java_agent_log4j2=$enableval +], [ + java_agent_log4j2=no +]) + AC_ARG_ENABLE([java-agent-all], [ AS_HELP_STRING([--enable-java-agent-all], [build the LTTng UST Java agent with all supported backends [default=no]]) ], [ java_agent_jul=$enableval java_agent_log4j=$enableval + # This backport to a stable branch requires the explicit use of + # '--enable-java-agent-log4j2' to avoid introducing a new dependency in + # an existing configuration. + #java_agent_log4j2=$enableval ], [:]) AM_CONDITIONAL([BUILD_JAVA_AGENT], [test "x$java_agent_jul" = "xyes" || test "x$java_agent_log4j" = "xyes"]) AM_CONDITIONAL([BUILD_JAVA_AGENT_WITH_JUL], [test "x$java_agent_jul" = "xyes"]) AM_CONDITIONAL([BUILD_JAVA_AGENT_WITH_LOG4J], [test "x$java_agent_log4j" = "xyes"]) +AM_CONDITIONAL([BUILD_JAVA_AGENT_WITH_LOG4J2], [test "x$java_agent_log4j2" = "xyes"]) +AM_CONDITIONAL([BUILD_JAVA_AGENT_WITH_LOG4J_COMMON], [test "x$java_agent_log4j" = "xyes" || test "x$java_agent_log4j2" = "xyes"]) -AS_IF([test "x$jni_interface" = "xyes" || test "x$java_agent_jul" = "xyes" || test "x$java_agent_log4j" = "xyes"], [ +AS_IF([test "x$jni_interface" = "xyes" || test "x$java_agent_jul" = "xyes" || test "x$java_agent_log4j" = "xyes" || test "x$java_agent_log4j2" = "xyes"], [ AX_JAVA_OPTIONS AX_PROG_JAVAC AX_PROG_JAVA @@ -384,6 +398,21 @@ AS_IF([test "x$java_agent_log4j" = "xyes"], [ ]) ]) +# The log4j 2.x agent requires the log4j core and api jars in the classpath +AS_IF([test "x$java_agent_log4j2" = "xyes"], [ + AX_CHECK_CLASSPATH + AX_CHECK_CLASS([org.apache.logging.log4j.Logger]) + AX_CHECK_CLASS([org.apache.logging.log4j.core.Core]) + AS_IF([test "x$ac_cv_class_org_apache_logging_log4j_Logger" = "xno" || test "x$ac_cv_class_org_apache_logging_log4j_core_Core" = "xno"], [ + AC_MSG_ERROR([dnl +The UST Java agent support for log4j was requested but the Log4j classes were +not found. Please specify the location of the Log4j API and core 2.x jars via the Java CLASSPATH +environment variable, e.g. ./configure CLASSPATH="/path/to/log4j-core.jar:/path/to/log4j-api.jar" +Current CLASSPATH: "$CLASSPATH" + ]) + ]) +]) + # Option to build the python agent AC_ARG_ENABLE([python-agent], [ AS_HELP_STRING([--enable-python-agent], [build the LTTng UST Python agent [default=no]]) @@ -534,6 +563,7 @@ AC_CONFIG_FILES([ liblttng-ust-java-agent/java/lttng-ust-agent-common/Makefile liblttng-ust-java-agent/java/lttng-ust-agent-jul/Makefile liblttng-ust-java-agent/java/lttng-ust-agent-log4j/Makefile + liblttng-ust-java-agent/java/lttng-ust-agent-log4j2/Makefile liblttng-ust-java-agent/jni/Makefile liblttng-ust-java-agent/jni/common/Makefile liblttng-ust-java-agent/jni/jul/Makefile @@ -612,7 +642,10 @@ test "x$java_agent_jul" = xyes && value=1 || value=0 PPRINT_PROP_BOOL_CUSTOM([Java agent (JUL support)], $value, [use --enable-java-agent-jul]) test "x$java_agent_log4j" = xyes && value=1 || value=0 -PPRINT_PROP_BOOL_CUSTOM([Java agent (Log4j support)], $value, [use --enable-java-agent-log4j]) +PPRINT_PROP_BOOL_CUSTOM([Java agent (Log4j 1.x support)], $value, [use --enable-java-agent-log4j]) + +test "x$java_agent_log4j2" = xyes && value=1 || value=0 +PPRINT_PROP_BOOL_CUSTOM([Java agent (Log4j 2.x support)], $value, [use --enable-java-agent-log4j2]) test "x$jni_interface" = xyes && value=1 || value=0 PPRINT_PROP_BOOL_CUSTOM([JNI interface (JNI)], $value, [use --enable-jni-interface]) diff --git a/doc/examples/Makefile.am b/doc/examples/Makefile.am index dc18d650..23f28e85 100644 --- a/doc/examples/Makefile.am +++ b/doc/examples/Makefile.am @@ -23,11 +23,36 @@ endif if BUILD_JAVA_AGENT_WITH_LOG4J doc_examples_java_log4jdir = ${docdir}/examples/java-log4j dist_doc_examples_java_log4j_DATA = java-log4j/Makefile \ - java-log4j/Hello.java \ + java-log4j/HelloLog4j.java \ java-log4j/run SUBDIRS_LOG4J = java-log4j endif +if BUILD_JAVA_AGENT_WITH_LOG4J2 +doc_examples_java_log4j2_basicdir = ${docdir}/examples/java-log4j2-basic +dist_doc_examples_java_log4j2_basic_DATA = \ + java-log4j2-basic/Makefile \ + java-log4j2-basic/HelloLog4j2.java \ + java-log4j2-basic/log4j2.xml \ + java-log4j2-basic/run + +doc_examples_java_log4j2_ctxdir = ${docdir}/examples/java-log4j2-ctx +dist_doc_examples_java_log4j2_ctx_DATA = \ + java-log4j2-ctx/Makefile \ + java-log4j2-ctx/HelloLog4j2Ctx.java \ + java-log4j2-ctx/log4j2.ctx1.xml \ + java-log4j2-ctx/log4j2.ctx2.xml \ + java-log4j2-ctx/run + +doc_examples_java_log4j2_progdir = ${docdir}/examples/java-log4j2-prog +dist_doc_examples_java_log4j2_prog_DATA = \ + java-log4j2-prog/Makefile \ + java-log4j2-prog/HelloLog4j2Prog.java \ + java-log4j2-prog/run + +SUBDIRS_LOG4J2 = java-log4j2-basic java-log4j2-prog +endif + if BUILD_PYTHON_AGENT doc_examples_pythondir = ${docdir}/examples/python dist_doc_examples_python_DATA = python/hello.py @@ -107,7 +132,7 @@ endif all-local: $(AM_V_at)if [ x"$(srcdir)" != x"$(builddir)" ]; then \ - for subdir in $(SUBDIRS_PROXY) $(SUBDIRS_JUL) $(SUBDIRS_LOG4J) $(SUBDIRS_CMAKE); do \ + for subdir in $(SUBDIRS_PROXY) $(SUBDIRS_JUL) $(SUBDIRS_LOG4J) $(SUBDIRS_LOG4J2) $(SUBDIRS_CMAKE); do \ cp -pfR $(srcdir)/$$subdir $(builddir); \ chmod -R u+w $(builddir)/$$subdir; \ done; \ @@ -166,6 +191,18 @@ all-local: ) || exit 1; \ done; \ fi; \ + if [ x"$(SUBDIRS_LOG4J2)" != x"" ]; then \ + for subdir in $(SUBDIRS_LOG4J2); do \ + ( \ + cd $$subdir && \ + $(MAKE) all \ + CLASSPATH="$(CLASSPATH)" \ + JAVA_CLASSPATH_OVERRIDE_LOG4J2="../../../liblttng-ust-java-agent/java/lttng-ust-agent-log4j2" \ + JAVA_CLASSPATH_OVERRIDE_COMMON="../../../liblttng-ust-java-agent/java/lttng-ust-agent-common" \ + $(AM_MAKEFLAGS) \ + ) || exit 1; \ + done; \ + fi; \ if [ x"$(SUBDIRS_CMAKE)" != x"" ]; then \ for subdir in $(SUBDIRS_CMAKE); do \ ( \ @@ -208,6 +245,13 @@ clean-local: fi; \ done; \ fi; \ + if [ x"$(SUBDIRS_LOG4J2)" != x"" ]; then \ + for subdir in $(SUBDIRS_LOG4J2); do \ + if [ -d $$subdir ]; then \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) clean && cd ..) || exit 1; \ + fi; \ + done; \ + fi; \ if [ x"$(SUBDIRS_CMAKE)" != x"" ]; then \ for subdir in $(SUBDIRS_CMAKE); do \ if [ -d $$subdir ]; then \ @@ -216,7 +260,7 @@ clean-local: done; \ fi; \ if [ x"$(srcdir)" != x"$(builddir)" ]; then \ - for subdir in $(SUBDIRS_PROXY) $(SUBDIRS_JUL) $(SUBDIRS_LOG4J) $(SUBDIRS_CMAKE); do \ + for subdir in $(SUBDIRS_PROXY) $(SUBDIRS_JUL) $(SUBDIRS_LOG4J) $(SUBDIRS_LOG4J2) $(SUBDIRS_CMAKE); do \ rm -rf $(builddir)/$$subdir; \ done; \ fi; diff --git a/doc/examples/java-jul/Makefile b/doc/examples/java-jul/Makefile index 5a594ff3..8e3b4e59 100644 --- a/doc/examples/java-jul/Makefile +++ b/doc/examples/java-jul/Makefile @@ -48,4 +48,4 @@ classes: $(CLASSES:.java=.class) .PHONY: clean clean: - $(RM) *.class + $(RM) $(CLASSES:.java=.class) diff --git a/doc/examples/java-log4j/Hello.java b/doc/examples/java-log4j/HelloLog4j.java similarity index 97% rename from doc/examples/java-log4j/Hello.java rename to doc/examples/java-log4j/HelloLog4j.java index 2f1119c0..1870ef44 100644 --- a/doc/examples/java-log4j/Hello.java +++ b/doc/examples/java-log4j/HelloLog4j.java @@ -50,9 +50,9 @@ import org.lttng.ust.agent.log4j.LttngLogAppender; * @author Alexandre Montplaisir * @author Christian Babeux */ -public class Hello { +public class HelloLog4j { - private static final Logger HELLO_LOG = Logger.getLogger(Hello.class); + private static final Logger HELLO_LOG = Logger.getLogger(HelloLog4j.class); /** * Application start diff --git a/doc/examples/java-log4j/Makefile b/doc/examples/java-log4j/Makefile index 39c2e788..75df0986 100644 --- a/doc/examples/java-log4j/Makefile +++ b/doc/examples/java-log4j/Makefile @@ -45,7 +45,7 @@ JC = javac -classpath "$(CLASSPATH):$(LOG4J_CP):$(COMMON_CP):." .java.class: $(JC) $(JFLAGS) $*.java -CLASSES = Hello.java +CLASSES = HelloLog4j.java all: classes @@ -53,4 +53,4 @@ classes: $(CLASSES:.java=.class) .PHONY: clean clean: - $(RM) *.class + $(RM) $(CLASSES:.java=.class) diff --git a/doc/examples/java-log4j/run b/doc/examples/java-log4j/run index 997d0bdc..7624f514 100755 --- a/doc/examples/java-log4j/run +++ b/doc/examples/java-log4j/run @@ -6,7 +6,7 @@ # find the JNI Log4j library. # -DIR=`dirname $0` +DIR=$(dirname "$0") JARFILE_COMMON="lttng-ust-agent-common.jar" JARFILE_LOG4J="lttng-ust-agent-log4j.jar" JAVA_OPTIONS="" @@ -16,7 +16,7 @@ if [ "x$CLASSPATH" = "x" ]; then CLASSPATH="/usr/local/share/java/log4j.jar:/usr/share/java/log4j.jar" fi -cd $DIR +cd "$DIR" || exit 1 if [ -f "$DIR/.intree" ]; then CLASSPATH="$CLASSPATH:../../../liblttng-ust-java-agent/java/lttng-ust-agent-common/$JARFILE_COMMON" @@ -33,6 +33,4 @@ if [ "x$LIBPATH" != "x" ]; then JAVA_OPTIONS="$JAVA_OPTIONS -Djava.library.path=$LIBPATH" fi -java -classpath "$CLASSPATH:." $JAVA_OPTIONS Hello - -cd - +java -classpath "$CLASSPATH:." $JAVA_OPTIONS HelloLog4j diff --git a/doc/examples/java-log4j2-basic/.intree b/doc/examples/java-log4j2-basic/.intree new file mode 100644 index 00000000..e69de29b diff --git a/doc/examples/java-log4j2-basic/HelloLog4j2.java b/doc/examples/java-log4j2-basic/HelloLog4j2.java new file mode 100644 index 00000000..2aa10260 --- /dev/null +++ b/doc/examples/java-log4j2-basic/HelloLog4j2.java @@ -0,0 +1,45 @@ +/* + * SPDX-License-Identifier: MIT + * + * Copyright (C) 2022 EfficiOS Inc. + */ + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +/** + * Example application using the LTTng-UST Java log4j agent. + * + *

+ * To obtain LTTng trace events, you should run the following sequence of + * commands: + *

+ * + *
    + *
  • $ lttng create
  • + *
  • $ lttng enable-event -l -a
  • + *
  • $ lttng start
  • + *
  • (run this program)
  • + *
  • $ lttng stop
  • + *
  • $ lttng view
  • + *
  • $ lttng destroy
  • + *
+ * + */ +public class HelloLog4j2 { + + private static final Logger logger = LogManager.getLogger(HelloLog4j2.class); + + /** + * Application start + * + * @param args Command-line arguments + */ + public static void main(String args[]) { + + /* Trigger some tracing events using the Log4j Logger created before. */ + logger.info("Basic config: Hello World, the answer is " + 42); + logger.info("Basic config: Another info event"); + logger.error("Basic config: An error event"); + } +} diff --git a/doc/examples/java-log4j2-basic/Makefile b/doc/examples/java-log4j2-basic/Makefile new file mode 100644 index 00000000..a14b0a72 --- /dev/null +++ b/doc/examples/java-log4j2-basic/Makefile @@ -0,0 +1,48 @@ +# SPDX-License-Identifier: LGPL-2.1-only +# +# Copyright (C) 2014 Christian Babeux +# +# This Makefile is not using automake so that users may see how to build a +# program with tracepoint provider probes as stand-alone shared objects. +# +# This makefile is purposefully kept simple to support GNU and BSD make. +# + +# Required JAR files for Log4j 2.x +JARFILE_LOG4J2=lttng-ust-agent-log4j2.jar +JARFILE_COMMON=lttng-ust-agent-common.jar + +# If system classpath is empty, try to guess log4j location +ifeq "$(CLASSPATH)" "" + CLASSPATH=/usr/local/share/java/log4j.jar:/usr/share/java/log4j.jar +endif + +# Check if the top level makefile overrides the Log4j Jar file's path. +ifeq "$(JAVA_CLASSPATH_OVERRIDE_LOG4J2)" "" + LOG4J2_CP = /usr/local/share/java/$(JARFILE_LOG4J2):/usr/share/java/$(JARFILE_LOG4J2) +else + LOG4J2_CP = $(JAVA_CLASSPATH_OVERRIDE_LOG4J2)/$(JARFILE_LOG4J2) +endif + +# Check if the top level makefile overrides the Common Jar file's path. +ifeq "$(JAVA_CLASSPATH_OVERRIDE_COMMON)" "" + COMMON_CP = /usr/local/share/java/$(JARFILE_COMMON):/usr/share/java/$(JARFILE_COMMON) +else + COMMON_CP = $(JAVA_CLASSPATH_OVERRIDE_COMMON)/$(JARFILE_COMMON) +endif + +JFLAGS = -g +JC = javac -classpath "$(CLASSPATH):$(LOG4J2_CP):$(COMMON_CP):." +.SUFFIXES: .java .class +.java.class: + $(JC) $(JFLAGS) $*.java + +CLASSES = HelloLog4j2.java + +all: classes + +classes: $(CLASSES:.java=.class) + +.PHONY: clean +clean: + $(RM) $(CLASSES:.java=.class) diff --git a/doc/examples/java-log4j2-basic/log4j2.xml b/doc/examples/java-log4j2-basic/log4j2.xml new file mode 100644 index 00000000..2dc83dd3 --- /dev/null +++ b/doc/examples/java-log4j2-basic/log4j2.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/doc/examples/java-log4j2-basic/run b/doc/examples/java-log4j2-basic/run new file mode 100755 index 00000000..e3656566 --- /dev/null +++ b/doc/examples/java-log4j2-basic/run @@ -0,0 +1,38 @@ +#!/bin/bash +# +# SPDX-License-Identifier: MIT + +# +# The -cp path should be changed to the lttng ust agent jar file on your system +# or locally to the project. Same goes for the Java library path in order to +# find the JNI Log4j library. +# + +DIR=$(dirname "$0") +JARFILE_COMMON="lttng-ust-agent-common.jar" +JARFILE_LOG4J2="lttng-ust-agent-log4j2.jar" +JAVA_OPTIONS="" + +# If system classpath is empty, try to guess log4j location +if [ "x$CLASSPATH" = "x" ]; then + CLASSPATH="/usr/local/share/java/log4j-core.jar:/usr/share/java/log4j-core.jar:/usr/local/share/java/log4j-api.jar:/usr/share/java/log4j-api.jar" +fi + +cd "$DIR" || exit 1 + +if [ -f "$DIR/.intree" ]; then + CLASSPATH="$CLASSPATH:../../../lib/lttng-ust-java-agent/java/lttng-ust-agent-common/$JARFILE_COMMON" + CLASSPATH="$CLASSPATH:../../../lib/lttng-ust-java-agent/java/lttng-ust-agent-log4j2/$JARFILE_LOG4J2" + LIBPATH="../../../lib/lttng-ust-java-agent/jni/log4j/.libs" +else + CLASSPATH="$CLASSPATH:/usr/local/share/java/$JARFILE_COMMON:/usr/share/java/$JARFILE_COMMON" + CLASSPATH="$CLASSPATH:/usr/local/share/java/$JARFILE_LOG4J2:/usr/share/java/$JARFILE_LOG4J2" + # Use system defined java.library.path + #LIBPATH="/usr/local/lib:/usr/lib" +fi + +if [ "x$LIBPATH" != "x" ]; then + JAVA_OPTIONS="$JAVA_OPTIONS -Djava.library.path=$LIBPATH" +fi + +java -classpath "$CLASSPATH:." $JAVA_OPTIONS HelloLog4j2 diff --git a/doc/examples/java-log4j2-ctx/HelloLog4j2Ctx.java b/doc/examples/java-log4j2-ctx/HelloLog4j2Ctx.java new file mode 100644 index 00000000..b9532231 --- /dev/null +++ b/doc/examples/java-log4j2-ctx/HelloLog4j2Ctx.java @@ -0,0 +1,71 @@ +/* + * SPDX-License-Identifier: MIT + * + * Copyright (C) 2022 EfficiOS Inc. + */ + +import java.net.URI; +import java.util.ArrayList; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.core.LoggerContext; + +/** + * Example application using the LTTng-UST Java log4j agent. + * + *

+ * To obtain LTTng trace events, you should run the following sequence of + * commands: + *

+ * + *
    + *
  • $ lttng create
  • + *
  • $ lttng enable-event -l -a
  • + *
  • $ lttng start
  • + *
  • (run this program)
  • + *
  • $ lttng stop
  • + *
  • $ lttng view
  • + *
  • $ lttng destroy
  • + *
+ * + */ +public class HelloLog4j2Ctx { + + /** + * Application start + * + * @param args Command-line arguments + */ + public static void main(String args[]) { + + URI configFileUri1 = URI.create("./log4j2.ctx1.xml"); + URI configFileUri2 = URI.create("./log4j2.ctx2.xml"); + + LoggerContext loggerContext1 = (LoggerContext) LogManager.getContext(ClassLoader.getSystemClassLoader(), false, + configFileUri1); + LoggerContext loggerContext2 = (LoggerContext) LogManager.getContext(ClassLoader.getSystemClassLoader(), false, + configFileUri2); + + /* Loggers in different contexts with the same name. */ + Logger logger1ctx1 = loggerContext1.getLogger(HelloLog4j2Ctx.class); + Logger logger1ctx2 = loggerContext2.getLogger(HelloLog4j2Ctx.class); + + Logger logger2ctx1 = loggerContext1.getLogger("Logger2"); + Logger logger3ctx2 = loggerContext2.getLogger("Logger3"); + + ArrayList loggers = new ArrayList(); + + loggers.add(logger1ctx1); + loggers.add(logger1ctx2); + loggers.add(logger2ctx1); + loggers.add(logger3ctx2); + + for (Logger logger : loggers) { + /* Trigger some tracing events using the Log4j Logger created before. */ + logger.info("Context config: Hello World, the answer is " + 42); + logger.info("Context config: Another info event"); + logger.error("Context config: An error event"); + } + } +} diff --git a/doc/examples/java-log4j2-ctx/Makefile b/doc/examples/java-log4j2-ctx/Makefile new file mode 100644 index 00000000..931854ec --- /dev/null +++ b/doc/examples/java-log4j2-ctx/Makefile @@ -0,0 +1,48 @@ +# SPDX-License-Identifier: LGPL-2.1-only +# +# Copyright (C) 2014 Christian Babeux +# +# This Makefile is not using automake so that users may see how to build a +# program with tracepoint provider probes as stand-alone shared objects. +# +# This makefile is purposefully kept simple to support GNU and BSD make. +# + +# Required JAR files for Log4j 2.x +JARFILE_LOG4J2=lttng-ust-agent-log4j2.jar +JARFILE_COMMON=lttng-ust-agent-common.jar + +# If system classpath is empty, try to guess log4j location +ifeq "$(CLASSPATH)" "" + CLASSPATH=/usr/local/share/java/log4j.jar:/usr/share/java/log4j.jar +endif + +# Check if the top level makefile overrides the Log4j Jar file's path. +ifeq "$(JAVA_CLASSPATH_OVERRIDE_LOG4J2)" "" + LOG4J2_CP = /usr/local/share/java/$(JARFILE_LOG4J2):/usr/share/java/$(JARFILE_LOG4J2) +else + LOG4J2_CP = $(JAVA_CLASSPATH_OVERRIDE_LOG4J2)/$(JARFILE_LOG4J2) +endif + +# Check if the top level makefile overrides the Common Jar file's path. +ifeq "$(JAVA_CLASSPATH_OVERRIDE_COMMON)" "" + COMMON_CP = /usr/local/share/java/$(JARFILE_COMMON):/usr/share/java/$(JARFILE_COMMON) +else + COMMON_CP = $(JAVA_CLASSPATH_OVERRIDE_COMMON)/$(JARFILE_COMMON) +endif + +JFLAGS = -g +JC = javac -classpath "$(CLASSPATH):$(LOG4J2_CP):$(COMMON_CP):." +.SUFFIXES: .java .class +.java.class: + $(JC) $(JFLAGS) $*.java + +CLASSES = HelloLog4j2Ctx.java + +all: classes + +classes: $(CLASSES:.java=.class) + +.PHONY: clean +clean: + $(RM) $(CLASSES:.java=.class) diff --git a/doc/examples/java-log4j2-ctx/log4j2.ctx1.xml b/doc/examples/java-log4j2-ctx/log4j2.ctx1.xml new file mode 100644 index 00000000..d59ce770 --- /dev/null +++ b/doc/examples/java-log4j2-ctx/log4j2.ctx1.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/doc/examples/java-log4j2-ctx/log4j2.ctx2.xml b/doc/examples/java-log4j2-ctx/log4j2.ctx2.xml new file mode 100644 index 00000000..c62af2e5 --- /dev/null +++ b/doc/examples/java-log4j2-ctx/log4j2.ctx2.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/doc/examples/java-log4j2-ctx/run b/doc/examples/java-log4j2-ctx/run new file mode 100755 index 00000000..319e3eb2 --- /dev/null +++ b/doc/examples/java-log4j2-ctx/run @@ -0,0 +1,38 @@ +#!/bin/bash +# +# SPDX-License-Identifier: MIT + +# +# The -cp path should be changed to the lttng ust agent jar file on your system +# or locally to the project. Same goes for the Java library path in order to +# find the JNI Log4j library. +# + +DIR=$(dirname "$0") +JARFILE_COMMON="lttng-ust-agent-common.jar" +JARFILE_LOG4J2="lttng-ust-agent-log4j2.jar" +JAVA_OPTIONS="" + +# If system classpath is empty, try to guess log4j location +if [ "x$CLASSPATH" = "x" ]; then + CLASSPATH="/usr/local/share/java/log4j.jar:/usr/share/java/log4j.jar" +fi + +cd "$DIR" || exit 1 + +if [ -f "$DIR/.intree" ]; then + CLASSPATH="$CLASSPATH:../../../lib/lttng-ust-java-agent/java/lttng-ust-agent-common/$JARFILE_COMMON" + CLASSPATH="$CLASSPATH:../../../lib/lttng-ust-java-agent/java/lttng-ust-agent-log4j2/$JARFILE_LOG4J2" + LIBPATH="../../../lib/lttng-ust-java-agent/jni/log4j/.libs" +else + CLASSPATH="$CLASSPATH:/usr/local/share/java/$JARFILE_COMMON:/usr/share/java/$JARFILE_COMMON" + CLASSPATH="$CLASSPATH:/usr/local/share/java/$JARFILE_LOG4J2:/usr/share/java/$JARFILE_LOG4J2" + # Use system defined java.library.path + #LIBPATH="/usr/local/lib:/usr/lib" +fi + +if [ "x$LIBPATH" != "x" ]; then + JAVA_OPTIONS="$JAVA_OPTIONS -Djava.library.path=$LIBPATH" +fi + +java -classpath "$CLASSPATH:." $JAVA_OPTIONS HelloLog4j2Ctx diff --git a/doc/examples/java-log4j2-prog/.intree b/doc/examples/java-log4j2-prog/.intree new file mode 100644 index 00000000..e69de29b diff --git a/doc/examples/java-log4j2-prog/HelloLog4j2Prog.java b/doc/examples/java-log4j2-prog/HelloLog4j2Prog.java new file mode 100644 index 00000000..685eebfa --- /dev/null +++ b/doc/examples/java-log4j2-prog/HelloLog4j2Prog.java @@ -0,0 +1,76 @@ +/* + * SPDX-License-Identifier: MIT + * + * Copyright (C) 2022 EfficiOS Inc. + */ + +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.core.config.Configurator; +import org.apache.logging.log4j.core.config.builder.api.AppenderComponentBuilder; +import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilder; +import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilderFactory; +import org.apache.logging.log4j.core.config.builder.api.LayoutComponentBuilder; +import org.apache.logging.log4j.core.config.builder.api.RootLoggerComponentBuilder; +import org.apache.logging.log4j.core.config.builder.impl.BuiltConfiguration; + +/** + * Example application using the LTTng-UST Java log4j agent. + * + *

+ * To obtain LTTng trace events, you should run the following sequence of + * commands: + *

+ * + *
    + *
  • $ lttng create
  • + *
  • $ lttng enable-event -l -a
  • + *
  • $ lttng start
  • + *
  • (run this program)
  • + *
  • $ lttng stop
  • + *
  • $ lttng view
  • + *
  • $ lttng destroy
  • + *
+ * + */ +public class HelloLog4j2Prog { + + /** + * Application start + * + * @param args Command-line arguments + */ + public static void main(String args[]) { + + ConfigurationBuilder builder = ConfigurationBuilderFactory.newConfigurationBuilder(); + + /* Layout */ + LayoutComponentBuilder standardLayout = builder.newLayout("PatternLayout"); + standardLayout.addAttribute("pattern", "%d [%t] %-5level: %msg%n%throwable"); + + /* Create a console appender */ + AppenderComponentBuilder appenderBuilder = builder.newAppender("Stdout", "CONSOLE"); + appenderBuilder.add(standardLayout); + builder.add(appenderBuilder); + + /* Create an Lttng appender */ + appenderBuilder = builder.newAppender("Lttng", "LTTNG"); + builder.add(appenderBuilder); + + /* Create a root logger with both appenders attached */ + RootLoggerComponentBuilder rootLogger = builder.newRootLogger(Level.DEBUG); + rootLogger.add(builder.newAppenderRef("Stdout")); + rootLogger.add(builder.newAppenderRef("Lttng")); + builder.add(rootLogger); + + Configurator.initialize(builder.build()); + + Logger logger = LogManager.getLogger(HelloLog4j2Prog.class); + + /* Trigger some tracing events using the Log4j Logger created before. */ + logger.info("Prog config: Hello World, the answer is " + 42); + logger.info("Prog config: Another info event"); + logger.error("Prog config: An error event"); + } +} diff --git a/doc/examples/java-log4j2-prog/Makefile b/doc/examples/java-log4j2-prog/Makefile new file mode 100644 index 00000000..1e2c1080 --- /dev/null +++ b/doc/examples/java-log4j2-prog/Makefile @@ -0,0 +1,48 @@ +# SPDX-License-Identifier: LGPL-2.1-only +# +# Copyright (C) 2014 Christian Babeux +# +# This Makefile is not using automake so that users may see how to build a +# program with tracepoint provider probes as stand-alone shared objects. +# +# This makefile is purposefully kept simple to support GNU and BSD make. +# + +# Required JAR files for Log4j 2.x +JARFILE_LOG4J2=lttng-ust-agent-log4j2.jar +JARFILE_COMMON=lttng-ust-agent-common.jar + +# If system classpath is empty, try to guess log4j location +ifeq "$(CLASSPATH)" "" + CLASSPATH=/usr/local/share/java/log4j.jar:/usr/share/java/log4j.jar +endif + +# Check if the top level makefile overrides the Log4j Jar file's path. +ifeq "$(JAVA_CLASSPATH_OVERRIDE_LOG4J2)" "" + LOG4J2_CP = /usr/local/share/java/$(JARFILE_LOG4J2):/usr/share/java/$(JARFILE_LOG4J2) +else + LOG4J2_CP = $(JAVA_CLASSPATH_OVERRIDE_LOG4J2)/$(JARFILE_LOG4J2) +endif + +# Check if the top level makefile overrides the Common Jar file's path. +ifeq "$(JAVA_CLASSPATH_OVERRIDE_COMMON)" "" + COMMON_CP = /usr/local/share/java/$(JARFILE_COMMON):/usr/share/java/$(JARFILE_COMMON) +else + COMMON_CP = $(JAVA_CLASSPATH_OVERRIDE_COMMON)/$(JARFILE_COMMON) +endif + +JFLAGS = -g +JC = javac -classpath "$(CLASSPATH):$(LOG4J2_CP):$(COMMON_CP):." +.SUFFIXES: .java .class +.java.class: + $(JC) $(JFLAGS) $*.java + +CLASSES = HelloLog4j2Prog.java + +all: classes + +classes: $(CLASSES:.java=.class) + +.PHONY: clean +clean: + $(RM) $(CLASSES:.java=.class) diff --git a/doc/examples/java-log4j2-prog/run b/doc/examples/java-log4j2-prog/run new file mode 100755 index 00000000..c6b84630 --- /dev/null +++ b/doc/examples/java-log4j2-prog/run @@ -0,0 +1,38 @@ +#!/bin/bash +# +# SPDX-License-Identifier: MIT + +# +# The -cp path should be changed to the lttng ust agent jar file on your system +# or locally to the project. Same goes for the Java library path in order to +# find the JNI Log4j library. +# + +DIR=$(dirname "$0") +JARFILE_COMMON="lttng-ust-agent-common.jar" +JARFILE_LOG4J2="lttng-ust-agent-log4j2.jar" +JAVA_OPTIONS="" + +# If system classpath is empty, try to guess log4j location +if [ "x$CLASSPATH" = "x" ]; then + CLASSPATH="/usr/local/share/java/log4j.jar:/usr/share/java/log4j.jar" +fi + +cd "$DIR" || exit 1 + +if [ -f "$DIR/.intree" ]; then + CLASSPATH="$CLASSPATH:../../../lib/lttng-ust-java-agent/java/lttng-ust-agent-common/$JARFILE_COMMON" + CLASSPATH="$CLASSPATH:../../../lib/lttng-ust-java-agent/java/lttng-ust-agent-log4j2/$JARFILE_LOG4J2" + LIBPATH="../../../lib/lttng-ust-java-agent/jni/log4j/.libs" +else + CLASSPATH="$CLASSPATH:/usr/local/share/java/$JARFILE_COMMON:/usr/share/java/$JARFILE_COMMON" + CLASSPATH="$CLASSPATH:/usr/local/share/java/$JARFILE_LOG4J2:/usr/share/java/$JARFILE_LOG4J2" + # Use system defined java.library.path + #LIBPATH="/usr/local/lib:/usr/lib" +fi + +if [ "x$LIBPATH" != "x" ]; then + JAVA_OPTIONS="$JAVA_OPTIONS -Djava.library.path=$LIBPATH" +fi + +java -classpath "$CLASSPATH:." $JAVA_OPTIONS HelloLog4j2Prog diff --git a/doc/java-agent.txt b/doc/java-agent.txt index ae653820..75ce336c 100644 --- a/doc/java-agent.txt +++ b/doc/java-agent.txt @@ -8,15 +8,20 @@ The agent can be built in three different configurations: $ ./configure --enable-java-agent-jul -2) Java agent with Log4j support: +2) Java agent with Log4j 1.x support: $ export CLASSPATH=$CLASSPATH:/path/to/log4j.jar $ ./configure --enable-java-agent-log4j -3) Java agent with JUL + Log4j support +3) Java agent with Log4j 2.x support: -$ export CLASSPATH=$CLASSPATH:/path/to/log4j.jar -$ ./configure --enable-java-agent-all +$ export CLASSPATH=$CLASSPATH:/path/to/log4j-core.jar:/path/to/log4j-api.jar +$ ./configure --enable-java-agent-log4j2 + +4) Java agent with JUL + Log4j 1.x + Log4j 2.x support + +$ export CLASSPATH=$CLASSPATH:/path/to/log4j.jar:/path/to/log4j-core.jar:/path/to/log4j-api.jar +$ ./configure --enable-java-agent-all --enable-java-agent-log4j2 To build the agent with log4j support, make sure that the log4j jar is in your Java classpath. @@ -25,24 +30,28 @@ The configure script will automatically detect the appropriate Java binaries to use in order to build the Java agent. Enabling the JUL support will build a "lttng-ust-agent-jul.jar" file. Enabling -the log4j support will build a "lttng-ust-agent-log4j.jar". Both of these jars +the log4j 1.x support will build a "lttng-ust-agent-log4j.jar" and enabling +log4j 2.x support will build a "lttng-ust-agent-log4j2.jar". All of these jars depend on a third "lttng-ust-agent-common.jar", which will always be built. All these archives will be installed in the arch-agnostic "$prefix/share/java" path, e.g: "/usr/share/java". You need to make sure the .jar for the logging -API you want to use (either lttng-ust-agent-jul.jar or -log4j.jar) is on your +API you want to use (either "lttng-ust-agent-jul.jar", +"lttng-ust-agent-log4j.jar" or "lttng-ust-agent-log4j2.jar") is on your application's classpath. -Both logging libraries also require an architecture-specific shared object -(e.g: "liblttng-ust-jul-jni.so"), which is installed by the build system when -doing "make install". Make sure that your Java application can find this shared -object, by using the "java.library.path" property if necessary. +The logging libraries require an architecture-specific shared object, +"liblttng-ust-jul-jni.so" for JUL and "liblttng-ust-jul-log4j.so" for both +Log4j 1.x and 2.x, which are installed by the build system when doing "make +install". Make sure that your Java application can find this shared object, by +using the "java.library.path" property if necessary. In order to use UST tracing in your Java application, you simply need to instantiate a LttngLogHandler or a LttngLogAppender (for JUL or Log4j, respectively), then attach it to a JUL or Log4j Logger class. -Refer to the code examples in examples/java-jul/ and examples/java-log4j/. +Refer to the code examples in "examples/java-jul/", "examples/java-log4j/" and +"examples/java-log4j2-*/". LTTng session daemon agents will be initialized as needed. If no session daemon is available, the execution will continue and the agents will retry connecting diff --git a/liblttng-ust-java-agent/java/Makefile.am b/liblttng-ust-java-agent/java/Makefile.am index 1720d313..b8c23992 100644 --- a/liblttng-ust-java-agent/java/Makefile.am +++ b/liblttng-ust-java-agent/java/Makefile.am @@ -7,3 +7,7 @@ endif if BUILD_JAVA_AGENT_WITH_LOG4J SUBDIRS += lttng-ust-agent-log4j endif + +if BUILD_JAVA_AGENT_WITH_LOG4J2 +SUBDIRS += lttng-ust-agent-log4j2 +endif diff --git a/liblttng-ust-java-agent/java/lttng-ust-agent-all/Manifest.txt b/liblttng-ust-java-agent/java/lttng-ust-agent-all/Manifest.txt index e09c85ec..c752478c 100644 --- a/liblttng-ust-java-agent/java/lttng-ust-agent-all/Manifest.txt +++ b/liblttng-ust-java-agent/java/lttng-ust-agent-all/Manifest.txt @@ -5,4 +5,4 @@ Specification-Vendor: LTTng Project Implementation-Title: org.lttng.ust.agent.all Implementation-Version: 1.0.0 Implementation-Vendor: LTTng Project -Class-Path: lttng-ust-agent-common.jar lttng-ust-agent-jul.jar lttng-ust-agent-log4j.jar +Class-Path: lttng-ust-agent-common.jar lttng-ust-agent-jul.jar lttng-ust-agent-log4j.jar lttng-ust-agent-log4j2.jar diff --git a/liblttng-ust-java-agent/java/lttng-ust-agent-log4j2/Makefile.am b/liblttng-ust-java-agent/java/lttng-ust-agent-log4j2/Makefile.am new file mode 100644 index 00000000..639ae534 --- /dev/null +++ b/liblttng-ust-java-agent/java/lttng-ust-agent-log4j2/Makefile.am @@ -0,0 +1,60 @@ +# SPDX-License-Identifier: LGPL-2.1-only + +JAVAROOT = . + +# Add the UST agent common jar to the classpath +AM_JAVACFLAGS = -classpath $(CLASSPATH):$(builddir)/../lttng-ust-agent-common/lttng-ust-agent-common.jar + +# Process the log4j2 annotations of our custom appender +AM_JAVACFLAGS += -processor org.apache.logging.log4j.core.config.plugins.processor.PluginProcessor + +pkgpath = org/lttng/ust/agent/log4j2 + +jarfile_version = 1.0.0 +jarfile_manifest = $(srcdir)/Manifest.txt +jarfile_symlink = lttng-ust-agent-log4j2.jar +jarfile = lttng-ust-agent-log4j2-$(jarfile_version).jar + +jardir = $(datadir)/java + +log4jjniout = ../../jni/log4j + +dist_noinst_JAVA = \ + $(pkgpath)/LttngLog4j2Agent.java \ + $(pkgpath)/LttngLog4j2Api.java \ + $(pkgpath)/LttngLogAppender.java + +dist_noinst_DATA = $(jarfile_manifest) + +jar_DATA = $(jarfile) + +classes = $(pkgpath)/*.class + +$(jarfile): classnoinst.stamp + $(JAR) cfm $(JARFLAGS) $@ $(jarfile_manifest) META-INF $(classes) && rm -f $(jarfile_symlink) && $(LN_S) $@ $(jarfile_symlink) + +if !HAVE_JAVAH +# If we don't have javah, assume we are running openjdk >= 10 and use javac +# to generate the jni header file. +AM_JAVACFLAGS += -h $(log4jjniout) +else +log4j-jni-header.stamp: $(dist_noinst_JAVA) + $(JAVAH) -classpath $(CLASSPATH):$(srcdir) -d $(log4jjniout) $(JAVAHFLAGS) org.lttng.ust.agent.log4j2.LttngLog4j2Api && \ + echo "Log4j JNI header generated" > log4j-jni-header.stamp + +all-local: log4j-jni-header.stamp +endif + +install-data-hook: + cd $(DESTDIR)/$(jardir) && rm -f $(jarfile_symlink) && $(LN_S) $(jarfile) $(jarfile_symlink) + +uninstall-hook: + cd $(DESTDIR)/$(jardir) && rm -f $(jarfile_symlink) + +clean-local: + rm -rf META-INF + +CLEANFILES = *.jar \ + $(classes) \ + log4j-jni-header.stamp \ + $(log4jjniout)/org_lttng_ust_agent_log4j2_LttngLog4j2Api.h diff --git a/liblttng-ust-java-agent/java/lttng-ust-agent-log4j2/Manifest.txt b/liblttng-ust-java-agent/java/lttng-ust-agent-log4j2/Manifest.txt new file mode 100644 index 00000000..1cd1b74f --- /dev/null +++ b/liblttng-ust-java-agent/java/lttng-ust-agent-log4j2/Manifest.txt @@ -0,0 +1,8 @@ +Name: org/lttng/ust/agent/log4j2/ +Specification-Title: LTTng UST Java Agent Log4J 2.x Integration +Specification-Version: 1.0.0 +Specification-Vendor: LTTng Project +Implementation-Title: org.lttng.ust.agent.log4j2 +Implementation-Version: 1.0.0 +Implementation-Vendor: LTTng Project +Class-Path: lttng-ust-agent-common.jar diff --git a/liblttng-ust-java-agent/java/lttng-ust-agent-log4j2/org/lttng/ust/agent/log4j2/LttngLog4j2Agent.java b/liblttng-ust-java-agent/java/lttng-ust-agent-log4j2/org/lttng/ust/agent/log4j2/LttngLog4j2Agent.java new file mode 100644 index 00000000..cb7c35ad --- /dev/null +++ b/liblttng-ust-java-agent/java/lttng-ust-agent-log4j2/org/lttng/ust/agent/log4j2/LttngLog4j2Agent.java @@ -0,0 +1,108 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (C) 2015-2022 EfficiOS Inc. + * Copyright (C) 2015 Alexandre Montplaisir + */ + +package org.lttng.ust.agent.log4j2; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.core.Appender; +import org.apache.logging.log4j.core.Logger; +import org.apache.logging.log4j.core.LoggerContext; +import org.apache.logging.log4j.core.impl.Log4jContextFactory; +import org.apache.logging.log4j.core.selector.ContextSelector; +import org.apache.logging.log4j.spi.LoggerContextFactory; +import org.apache.logging.log4j.status.StatusLogger; +import org.lttng.ust.agent.AbstractLttngAgent; + +/** + * Agent implementation for Log4j 2.x. + */ +class LttngLog4j2Agent extends AbstractLttngAgent { + + private static LttngLog4j2Agent instance = null; + + private LttngLog4j2Agent() { + super(Domain.LOG4J); + } + + public static synchronized LttngLog4j2Agent getInstance() { + if (instance == null) { + instance = new LttngLog4j2Agent(); + } + return instance; + } + + @Override + public Collection listAvailableEvents() { + Set eventNames = new TreeSet<>(); + + LoggerContextFactory contextFactory = LogManager.getFactory(); + if (!(contextFactory instanceof Log4jContextFactory)) { + /* Using a custom ContextFactory is not supported. */ + StatusLogger.getLogger().error("Can't list events with custom ContextFactory"); + return eventNames; + } + + ContextSelector selector = ((Log4jContextFactory) contextFactory).getSelector(); + + for (LoggerContext logContext : selector.getLoggerContexts()) { + Collection loggers = logContext.getLoggers(); + for (Logger logger : loggers) { + /* + * Check if that logger has at least one LTTng log4j appender attached. + */ + if (hasLttngAppenderAttached(logger)) { + eventNames.add(logger.getName()); + } + } + } + return eventNames; + } + + /* + * Check if a logger has an LttngLogAppender attached. + * + * @param logger the Logger to check, null returns false + * @return true if the logger or its parent has at least one LttngLogAppender attached + */ + private static boolean hasLttngAppenderAttached(Logger logger) { + + if (logger == null) { + return false; + } + + /* + * Check all the appenders associated with the logger and return true if one of + * them is an LttngLogAppender. + */ + Map appenders = logger.getAppenders(); + for (Map.Entry appender : appenders.entrySet()) { + if (appender.getValue() instanceof LttngLogAppender) { + return true; + } + } + + /* + * A parent logger, if any, may be connected to an LTTng handler. In this case, + * we will want to include this child logger in the output, since it will be + * accessible by LTTng. + * + * Despite the doc, getParent can return null based on the implementation as of + * log4j 2.17.1. + * + * The getParent function is there as a backward compat for 1.x. It is not clear + * in which context it should be used. The cost of doing the lookup is minimal + * and mimics what was done for the 1.x agent. + */ + return hasLttngAppenderAttached(logger.getParent()); + + } +} diff --git a/liblttng-ust-java-agent/java/lttng-ust-agent-log4j2/org/lttng/ust/agent/log4j2/LttngLog4j2Api.java b/liblttng-ust-java-agent/java/lttng-ust-agent-log4j2/org/lttng/ust/agent/log4j2/LttngLog4j2Api.java new file mode 100644 index 00000000..617d0d11 --- /dev/null +++ b/liblttng-ust-java-agent/java/lttng-ust-agent-log4j2/org/lttng/ust/agent/log4j2/LttngLog4j2Api.java @@ -0,0 +1,21 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (C) 2016 EfficiOS Inc. + * Copyright (C) 2016 Alexandre Montplaisir + */ + +package org.lttng.ust.agent.log4j2; + +/** + * Virtual class containing the Java side of the LTTng-log4j JNI API methods. + */ +final class LttngLog4j2Api { + + private LttngLog4j2Api() { + } + + static native void tracepointWithContext(String message, String loggerName, String className, String methodName, + String fileName, int lineNumber, long timeStamp, int logLevel, String threadName, byte[] contextEntries, + byte[] contextStrings); +} diff --git a/liblttng-ust-java-agent/java/lttng-ust-agent-log4j2/org/lttng/ust/agent/log4j2/LttngLogAppender.java b/liblttng-ust-java-agent/java/lttng-ust-agent-log4j2/org/lttng/ust/agent/log4j2/LttngLogAppender.java new file mode 100644 index 00000000..9c093fa8 --- /dev/null +++ b/liblttng-ust-java-agent/java/lttng-ust-agent-log4j2/org/lttng/ust/agent/log4j2/LttngLogAppender.java @@ -0,0 +1,198 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (C) 2015-2022 EfficiOS Inc. + * Copyright (C) 2015 Alexandre Montplaisir + * Copyright (C) 2014 Christian Babeux + */ + +package org.lttng.ust.agent.log4j2; + +import java.io.IOException; +import java.util.Collection; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; + +import org.apache.logging.log4j.core.Appender; +import org.apache.logging.log4j.core.Core; +import org.apache.logging.log4j.core.Filter; +import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.core.appender.AbstractAppender; +import org.apache.logging.log4j.core.config.Property; +import org.apache.logging.log4j.core.config.plugins.Plugin; +import org.apache.logging.log4j.core.config.plugins.PluginAttribute; +import org.apache.logging.log4j.core.config.plugins.PluginElement; +import org.apache.logging.log4j.core.config.plugins.PluginFactory; +import org.lttng.ust.agent.ILttngHandler; +import org.lttng.ust.agent.context.ContextInfoSerializer; + +/** + * LTTng-UST Log4j 2.x log handler. + * + * Applications can attach this appender to their + * {@link org.apache.log4j.Logger} to have it generate UST events from logging + * events received through the logger. + * + * It sends its events to UST via the JNI library "liblttng-ust-log4j-jni.so". + * Make sure this library is available before using this appender. + * + */ +@Plugin(name = LttngLogAppender.PLUGIN_NAME, category = Core.CATEGORY_NAME, elementType = Appender.ELEMENT_TYPE, printObject = false) +public final class LttngLogAppender extends AbstractAppender implements ILttngHandler { + + /** + * The name of the appender in the configuration. + */ + public static final String PLUGIN_NAME = "Lttng"; + + private static final String SHARED_OBJECT_NAME = "lttng-ust-log4j-jni"; + + /** + * Number of events logged (really sent through JNI) by this handler + */ + private final AtomicLong eventCount = new AtomicLong(0); + + private final LttngLog4j2Agent agent; + + /** + * Constructor + * + * @param name The name of the Appender. + * @param filter The Filter or null. + * @param ignoreExceptions If {@code "true"} (default) exceptions encountered + * when appending events are logged; otherwise they are + * propagated to the caller. + * + * @throws IOException This handler requires the lttng-ust-log4j-jni.so + * native library, through which it will send the + * trace events. This exception is thrown if this + * library cannot be found. + * @throws SecurityException We will forward any SecurityExcepion that may be + * thrown when trying to load the JNI library. + */ + protected LttngLogAppender(String name, Filter filter, boolean ignoreExceptions) + throws IOException, SecurityException { + + super(name, filter, null, ignoreExceptions, Property.EMPTY_ARRAY); + + /* Initialize LTTng UST tracer. */ + try { + System.loadLibrary(SHARED_OBJECT_NAME); // $NON-NLS-1$ + } catch (UnsatisfiedLinkError e) { + throw new IOException(e); + } + + /* Register to the relevant agent. */ + agent = LttngLog4j2Agent.getInstance(); + agent.registerHandler(this); + } + + /** + * Create an LttngLogAppender. + * + * @param name The name of the Appender, null returns null. + * @param ignoreExceptions If {@code "true"} (default) exceptions encountered + * when appending events are logged; otherwise they are + * propagated to the caller. + * @param filter The Filter or null. + * + * @return A new LttngLogAppender, null if the name was null. + * + * @throws IOException This handler requires the lttng-ust-log4j-jni.so + * native library, through which it will send the + * trace events. This exception is thrown if this + * library cannot be found. + * @throws SecurityException We will forward any SecurityExcepion that may be + * thrown when trying to load the JNI library. + */ + @PluginFactory + public static LttngLogAppender createAppender(@PluginAttribute("name") String name, + @PluginAttribute("ignoreExceptions") Boolean ignoreExceptions, @PluginElement("Filters") Filter filter) + throws IOException, SecurityException { + + if (name == null) { + LOGGER.error("No name provided for LttngLogAppender"); + return null; + } + + if (ignoreExceptions == null) { + ignoreExceptions = true; + } + + return new LttngLogAppender(name, filter, ignoreExceptions); + } + + @Override + public synchronized void close() { + agent.unregisterHandler(this); + } + + @Override + public void stop() { + close(); + super.stop(); + + getStatusLogger().debug("Appender Lttng stopped"); + } + + @Override + public boolean stop(final long timeout, final TimeUnit timeUnit) { + close(); + boolean status = super.stop(timeout, timeUnit); + + getStatusLogger().debug("Appender Lttng stopped with status " + status); + + return status; + } + + /** + * Get the number of events logged by this handler so far. This means the number + * of events actually sent through JNI to UST. + * + * @return The number of events logged so far + */ + @Override + public long getEventCount() { + return eventCount.get(); + } + + @Override + public void append(LogEvent event) { + /* + * Check if the current message should be logged, according to the UST session + * settings. + */ + if (!agent.isEventEnabled(event.getLoggerName())) { + return; + } + + /* + * Default values if the StackTraceElement is null. + */ + String classname = ""; + String methodname = ""; + String filename = ""; + int line = -1; + + StackTraceElement ste = event.getSource(); + if (ste != null) { + classname = ste.getClassName(); + methodname = ste.getMethodName(); + filename = ste.getFileName(); + line = ste.getLineNumber(); + } + + /* Retrieve all the requested context information we can find. */ + Collection>> enabledContexts = agent.getEnabledAppContexts(); + ContextInfoSerializer.SerializedContexts contextInfo = ContextInfoSerializer + .queryAndSerializeRequestedContexts(enabledContexts); + + eventCount.incrementAndGet(); + + LttngLog4j2Api.tracepointWithContext(event.getMessage().getFormattedMessage(), event.getLoggerName(), classname, + methodname, filename, line, event.getTimeMillis(), event.getLevel().intLevel(), event.getThreadName(), + contextInfo.getEntriesArray(), contextInfo.getStringsArray()); + } +} diff --git a/liblttng-ust-java-agent/jni/Makefile.am b/liblttng-ust-java-agent/jni/Makefile.am index dae51200..e481d840 100644 --- a/liblttng-ust-java-agent/jni/Makefile.am +++ b/liblttng-ust-java-agent/jni/Makefile.am @@ -4,6 +4,6 @@ if BUILD_JAVA_AGENT_WITH_JUL SUBDIRS += jul endif -if BUILD_JAVA_AGENT_WITH_LOG4J +if BUILD_JAVA_AGENT_WITH_LOG4J_COMMON SUBDIRS += log4j endif diff --git a/liblttng-ust-java-agent/jni/log4j/Makefile.am b/liblttng-ust-java-agent/jni/log4j/Makefile.am index 5e5fe0fc..d3a88b45 100644 --- a/liblttng-ust-java-agent/jni/log4j/Makefile.am +++ b/liblttng-ust-java-agent/jni/log4j/Makefile.am @@ -1,10 +1,20 @@ AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include $(JNI_CPPFLAGS) lib_LTLIBRARIES = liblttng-ust-log4j-jni.la -liblttng_ust_log4j_jni_la_SOURCES = lttng_ust_log4j.c \ - lttng_ust_log4j.h +liblttng_ust_log4j_jni_la_SOURCES = \ + lttng_ust_log4j_tp.c \ + lttng_ust_log4j_tp.h +nodist_liblttng_ust_log4j_jni_la_SOURCES = -nodist_liblttng_ust_log4j_jni_la_SOURCES = org_lttng_ust_agent_log4j_LttngLog4jApi.h +if BUILD_JAVA_AGENT_WITH_LOG4J +liblttng_ust_log4j_jni_la_SOURCES += lttng_ust_log4j.c +nodist_liblttng_ust_log4j_jni_la_SOURCES += org_lttng_ust_agent_log4j_LttngLog4jApi.h +endif + +if BUILD_JAVA_AGENT_WITH_LOG4J2 +liblttng_ust_log4j_jni_la_SOURCES += lttng_ust_log4j2.c +nodist_liblttng_ust_log4j_jni_la_SOURCES += org_lttng_ust_agent_log4j2_LttngLog4j2Api.h +endif liblttng_ust_log4j_jni_la_LIBADD = -lc \ -L$(top_builddir)/liblttng-ust/.libs \ diff --git a/liblttng-ust-java-agent/jni/log4j/lttng_ust_log4j.c b/liblttng-ust-java-agent/jni/log4j/lttng_ust_log4j.c index 72eca1d4..f33dbc9c 100644 --- a/liblttng-ust-java-agent/jni/log4j/lttng_ust_log4j.c +++ b/liblttng-ust-java-agent/jni/log4j/lttng_ust_log4j.c @@ -20,9 +20,7 @@ #define _LGPL_SOURCE #include "org_lttng_ust_agent_log4j_LttngLog4jApi.h" -#define TRACEPOINT_DEFINE -#define TRACEPOINT_CREATE_PROBES -#include "lttng_ust_log4j.h" +#include "lttng_ust_log4j_tp.h" #include "../common/lttng_ust_context.h" /* diff --git a/liblttng-ust-java-agent/jni/log4j/lttng_ust_log4j2.c b/liblttng-ust-java-agent/jni/log4j/lttng_ust_log4j2.c new file mode 100644 index 00000000..68616bf4 --- /dev/null +++ b/liblttng-ust-java-agent/jni/log4j/lttng_ust_log4j2.c @@ -0,0 +1,129 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (C) 2016-2022 EfficiOS Inc. + * Copyright (C) 2016 Alexandre Montplaisir + * Copyright (C) 2011-2012 Mathieu Desnoyers + */ + +#define _LGPL_SOURCE +#include "org_lttng_ust_agent_log4j2_LttngLog4j2Api.h" +#include "lttng_ust_log4j_tp.h" +#include "../common/lttng_ust_context.h" + +/* + * Those are an exact map from the class org.apache.log4j.Level. + */ +enum loglevel_log4j1 { + LOGLEVEL_LOG4J1_OFF = INT32_MAX, + LOGLEVEL_LOG4J1_FATAL = 50000, + LOGLEVEL_LOG4J1_ERROR = 40000, + LOGLEVEL_LOG4J1_WARN = 30000, + LOGLEVEL_LOG4J1_INFO = 20000, + LOGLEVEL_LOG4J1_DEBUG = 10000, + LOGLEVEL_LOG4J1_TRACE = 5000, + LOGLEVEL_LOG4J1_ALL = INT32_MIN, +}; + +/* + * Those are an exact map from the class + * org.apache.logging.log4j.spi.StandardLevel. + */ +enum loglevel_log4j2 { + LOGLEVEL_LOG4J2_OFF = 0, + LOGLEVEL_LOG4J2_FATAL = 100, + LOGLEVEL_LOG4J2_ERROR = 200, + LOGLEVEL_LOG4J2_WARN = 300, + LOGLEVEL_LOG4J2_INFO = 400, + LOGLEVEL_LOG4J2_DEBUG = 500, + LOGLEVEL_LOG4J2_TRACE = 600, + LOGLEVEL_LOG4J2_ALL = INT32_MAX, +}; + +/* + * The integer values of the loglevels has obviously changed in log4j2, + * translate them to the values of log4j1 since they are exposed in the API of + * lttng-tools. + * + * Custom loglevels might pose a problem when using ranges. + */ +static jint loglevel_2x_to_1x(jint loglevel) +{ + switch (loglevel) { + case LOGLEVEL_LOG4J2_OFF: + return LOGLEVEL_LOG4J1_OFF; + case LOGLEVEL_LOG4J2_FATAL: + return LOGLEVEL_LOG4J1_FATAL; + case LOGLEVEL_LOG4J2_ERROR: + return LOGLEVEL_LOG4J1_ERROR; + case LOGLEVEL_LOG4J2_WARN: + return LOGLEVEL_LOG4J1_WARN; + case LOGLEVEL_LOG4J2_INFO: + return LOGLEVEL_LOG4J1_INFO; + case LOGLEVEL_LOG4J2_DEBUG: + return LOGLEVEL_LOG4J1_DEBUG; + case LOGLEVEL_LOG4J2_TRACE: + return LOGLEVEL_LOG4J1_TRACE; + case LOGLEVEL_LOG4J2_ALL: + return LOGLEVEL_LOG4J1_ALL; + default: + /* Handle custom loglevels. */ + return loglevel; + } +} + +/* + * Tracepoint used by Java applications using the log4j 2.x handler. + */ +JNIEXPORT void JNICALL Java_org_lttng_ust_agent_log4j2_LttngLog4j2Api_tracepointWithContext(JNIEnv *env, + jobject jobj __attribute__((unused)), + jstring message, + jstring loggerName, + jstring className, + jstring methodName, + jstring fileName, + jint lineNumber, + jlong timeStamp, + jint logLevel, + jstring threadName, + jbyteArray context_info_entries, + jbyteArray context_info_strings) +{ + jboolean iscopy; + const char *msg_cstr = (*env)->GetStringUTFChars(env, message, &iscopy); + const char *logger_name_cstr = (*env)->GetStringUTFChars(env, loggerName, &iscopy); + const char *class_name_cstr = (*env)->GetStringUTFChars(env, className, &iscopy); + const char *method_name_cstr = (*env)->GetStringUTFChars(env, methodName, &iscopy); + const char *file_name_cstr = (*env)->GetStringUTFChars(env, fileName, &iscopy); + const char *thread_name_cstr = (*env)->GetStringUTFChars(env, threadName, &iscopy); + signed char *context_info_entries_array; + signed char *context_info_strings_array; + + /* + * Write these to the TLS variables, so that the UST callbacks in + * lttng_ust_context.c can access them. + */ + context_info_entries_array = (*env)->GetByteArrayElements(env, context_info_entries, &iscopy); + lttng_ust_context_info_tls.ctx_entries = (struct lttng_ust_jni_ctx_entry *) context_info_entries_array; + lttng_ust_context_info_tls.ctx_entries_len = (*env)->GetArrayLength(env, context_info_entries); + context_info_strings_array = (*env)->GetByteArrayElements(env, context_info_strings, &iscopy); + lttng_ust_context_info_tls.ctx_strings = context_info_strings_array; + lttng_ust_context_info_tls.ctx_strings_len = (*env)->GetArrayLength(env, context_info_strings); + + tracepoint(lttng_log4j, event, msg_cstr, logger_name_cstr, + class_name_cstr, method_name_cstr, file_name_cstr, + lineNumber, timeStamp, loglevel_2x_to_1x(logLevel), thread_name_cstr); + + lttng_ust_context_info_tls.ctx_entries = NULL; + lttng_ust_context_info_tls.ctx_entries_len = 0; + lttng_ust_context_info_tls.ctx_strings = NULL; + lttng_ust_context_info_tls.ctx_strings_len = 0; + (*env)->ReleaseStringUTFChars(env, message, msg_cstr); + (*env)->ReleaseStringUTFChars(env, loggerName, logger_name_cstr); + (*env)->ReleaseStringUTFChars(env, className, class_name_cstr); + (*env)->ReleaseStringUTFChars(env, methodName, method_name_cstr); + (*env)->ReleaseStringUTFChars(env, fileName, file_name_cstr); + (*env)->ReleaseStringUTFChars(env, threadName, thread_name_cstr); + (*env)->ReleaseByteArrayElements(env, context_info_entries, context_info_entries_array, 0); + (*env)->ReleaseByteArrayElements(env, context_info_strings, context_info_strings_array, 0); +} diff --git a/liblttng-ust-java-agent/jni/log4j/lttng_ust_log4j_tp.c b/liblttng-ust-java-agent/jni/log4j/lttng_ust_log4j_tp.c new file mode 100644 index 00000000..ac31e2c4 --- /dev/null +++ b/liblttng-ust-java-agent/jni/log4j/lttng_ust_log4j_tp.c @@ -0,0 +1,16 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (C) 2016 EfficiOS Inc. + * Copyright (C) 2016 Alexandre Montplaisir + * Copyright (C) 2011-2012 Mathieu Desnoyers + */ + +#define _LGPL_SOURCE + +#define TRACEPOINT_HIDDEN_DEFINITION +#define TRACEPOINT_PROVIDER_HIDDEN_DEFINITION + +#define TRACEPOINT_DEFINE +#define TRACEPOINT_CREATE_PROBES +#include "lttng_ust_log4j_tp.h" diff --git a/liblttng-ust-java-agent/jni/log4j/lttng_ust_log4j_tp.h b/liblttng-ust-java-agent/jni/log4j/lttng_ust_log4j_tp.h new file mode 100644 index 00000000..5aab2d98 --- /dev/null +++ b/liblttng-ust-java-agent/jni/log4j/lttng_ust_log4j_tp.h @@ -0,0 +1,48 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (C) 2011 Mathieu Desnoyers + */ + +#undef TRACEPOINT_PROVIDER +#define TRACEPOINT_PROVIDER lttng_log4j + +#if !defined(_TRACEPOINT_LTTNG_UST_LOG4J_H) || defined(TRACEPOINT_HEADER_MULTI_READ) +#define _TRACEPOINT_LTTNG_UST_LOG4J_H + +#include + +/* + * Tracepoint used by Java applications using the log4j log appender. + */ +TRACEPOINT_EVENT(lttng_log4j, event, + TP_ARGS( + const char *, msg, + const char *, logger_name, + const char *, class_name, + const char *, method_name, + const char *, file_name, + int, line_number, + long, timestamp, + int, log_level, + const char *, thread_name), + TP_FIELDS( + ctf_string(msg, msg) + ctf_string(logger_name, logger_name) + ctf_string(class_name, class_name) + ctf_string(method_name, method_name) + ctf_string(filename, file_name) + ctf_integer(int, line_number, line_number) + ctf_integer(long, timestamp, timestamp) + ctf_integer(int, int_loglevel, log_level) + ctf_string(thread_name, thread_name) + ) +) + +#endif /* _TRACEPOINT_LTTNG_UST_LOG4J_H */ + +#undef TRACEPOINT_INCLUDE +#define TRACEPOINT_INCLUDE "./lttng_ust_log4j_tp.h" + +/* This part must be outside protection */ +#include -- 2.34.1