2 * SPDX-License-Identifier: LGPL-2.1-only
4 * Copyright (C) 2015-2022 EfficiOS Inc.
5 * Copyright (C) 2015 Alexandre Montplaisir <alexmonthy@efficios.com>
6 * Copyright (C) 2014 Christian Babeux <christian.babeux@efficios.com>
9 package org
.lttng
.ust
.agent
.log4j2
;
11 import java
.io
.IOException
;
12 import java
.util
.Collection
;
14 import java
.util
.Map
.Entry
;
15 import java
.util
.concurrent
.TimeUnit
;
16 import java
.util
.concurrent
.atomic
.AtomicLong
;
18 import org
.apache
.logging
.log4j
.core
.Appender
;
19 import org
.apache
.logging
.log4j
.core
.Core
;
20 import org
.apache
.logging
.log4j
.core
.Filter
;
21 import org
.apache
.logging
.log4j
.core
.LogEvent
;
22 import org
.apache
.logging
.log4j
.core
.appender
.AbstractAppender
;
23 import org
.apache
.logging
.log4j
.core
.config
.Property
;
24 import org
.apache
.logging
.log4j
.core
.config
.plugins
.Plugin
;
25 import org
.apache
.logging
.log4j
.core
.config
.plugins
.PluginAttribute
;
26 import org
.apache
.logging
.log4j
.core
.config
.plugins
.PluginElement
;
27 import org
.apache
.logging
.log4j
.core
.config
.plugins
.PluginFactory
;
28 import org
.apache
.logging
.log4j
.message
.Message
;
29 import org
.lttng
.ust
.agent
.ILttngAgent
.Domain
;
30 import org
.lttng
.ust
.agent
.ILttngHandler
;
31 import org
.lttng
.ust
.agent
.context
.ContextInfoSerializer
;
34 * LTTng-UST Log4j 2.x log handler.
36 * Applications can attach this appender to their
37 * {@link org.apache.log4j.Logger} to have it generate UST events from logging
38 * events received through the logger.
40 * It sends its events to UST via the JNI library "liblttng-ust-log4j-jni.so".
41 * Make sure this library is available before using this appender.
44 @Plugin(name
= LttngLogAppender
.PLUGIN_NAME
, category
= Core
.CATEGORY_NAME
, elementType
= Appender
.ELEMENT_TYPE
, printObject
= false)
45 public final class LttngLogAppender
extends AbstractAppender
implements ILttngHandler
{
48 * The name of the appender in the configuration.
50 public static final String PLUGIN_NAME
= "Lttng";
52 private static final String SHARED_OBJECT_NAME
= "lttng-ust-log4j-jni";
55 * Number of events logged (really sent through JNI) by this handler
57 private final AtomicLong eventCount
= new AtomicLong(0);
59 private final LttngLog4j2Agent agent
;
64 * @param name The name of the Appender.
65 * @param domain The LTTng-UST agent domain 'LOG4J' / 'LOG4J2'.
66 * @param filter The Filter or null.
67 * @param ignoreExceptions If {@code "true"} exceptions encountered when
68 * appending events are logged; otherwise they are
69 * propagated to the caller.
71 * @throws IOException This handler requires the
72 * lttng-ust-log4j-jni.so native library,
73 * through which it will send the trace events.
74 * This exception is thrown if this library
76 * @throws IllegalArgumentException If the provided domain is unsupported.
77 * @throws SecurityException We will forward any SecurityExcepion that
78 * may be thrown when trying to load the JNI
81 protected LttngLogAppender(String name
, LttngLog4j2Agent
.Domain domain
, Filter filter
, boolean ignoreExceptions
)
82 throws IOException
, IllegalArgumentException
, SecurityException
{
84 super(name
, filter
, null, ignoreExceptions
, Property
.EMPTY_ARRAY
);
86 /* Initialize LTTng UST tracer. */
88 System
.loadLibrary(SHARED_OBJECT_NAME
); // $NON-NLS-1$
89 } catch (UnsatisfiedLinkError e
) {
90 throw new IOException(e
);
93 /* Register to the relevant agent. */
94 if (domain
== LttngLog4j2Agent
.Domain
.LOG4J
) {
95 agent
= LttngLog4j2Agent
.getLog4j1Instance();
96 } else if (domain
== LttngLog4j2Agent
.Domain
.LOG4J2
) {
97 agent
= LttngLog4j2Agent
.getLog4j2Instance();
99 throw new IllegalArgumentException("Unsupported domain '" + domain
+ "'");
102 agent
.registerHandler(this);
106 * Create an LttngLogAppender.
108 * @param name The name of the Appender, null returns null.
109 * @param domain The LTTng-UST agent domain 'LOG4J' / 'LOG4J2'.
110 * @param ignoreExceptions If {@code "true"} (default) exceptions encountered
111 * when appending events are logged; otherwise they are
112 * propagated to the caller.
113 * @param filter The Filter or null.
115 * @return A new LttngLogAppender, null if the name was null or the domain is
119 public static LttngLogAppender
createAppender(@PluginAttribute("name") String name
,
120 @PluginAttribute("domain") String domain
, @PluginAttribute("ignoreExceptions") Boolean ignoreExceptions
,
121 @PluginElement("Filters") Filter filter
) {
124 LOGGER
.error("No name provided for LttngLogAppender");
128 if (domain
== null) {
129 LOGGER
.error("No domain provided for LttngLogAppender");
133 if (ignoreExceptions
== null) {
134 ignoreExceptions
= true;
137 /* Parse the domain string */
138 LttngLog4j2Agent
.Domain parsedDomain
;
140 parsedDomain
= LttngLog4j2Agent
.Domain
.valueOf(domain
.toUpperCase());
141 } catch (IllegalArgumentException e
) {
142 LOGGER
.error("Invalid domain '{}' for LttngLogAppender", domain
);
146 /* Create the appender and handle the possible failures. */
147 LttngLogAppender newAppender
;
149 newAppender
= new LttngLogAppender(name
, parsedDomain
, filter
, ignoreExceptions
);
150 } catch (IllegalArgumentException e
) {
151 LOGGER
.error("Invalid domain '{}' for LttngLogAppender", parsedDomain
);
153 } catch (SecurityException e
) {
154 LOGGER
.error("Security error trying to load '{}' JNI library for LttngLogAppender", SHARED_OBJECT_NAME
);
156 } catch (IOException e
) {
157 LOGGER
.error("Failed to load '{}' JNI library for LttngLogAppender", SHARED_OBJECT_NAME
);
165 public synchronized void close() {
166 agent
.unregisterHandler(this);
174 getStatusLogger().debug("Appender Lttng stopped");
178 public boolean stop(final long timeout
, final TimeUnit timeUnit
) {
180 boolean status
= super.stop(timeout
, timeUnit
);
182 getStatusLogger().debug("Appender Lttng stopped with status " + status
);
188 * Get the number of events logged by this handler so far. This means the number
189 * of events actually sent through JNI to UST.
191 * @return The number of events logged so far
194 public long getEventCount() {
195 return eventCount
.get();
199 public void append(LogEvent event
) {
201 * Check if the current message should be logged, according to the UST session
204 String loggername
= event
.getLoggerName();
205 if (loggername
== null || !agent
.isEventEnabled(loggername
)) {
210 * Default value if the Message is null.
214 Message eventMessage
= event
.getMessage();
215 if (eventMessage
!= null) {
216 message
= eventMessage
.getFormattedMessage();
220 * Default values if the StackTraceElement is null.
222 String classname
= "";
223 String methodname
= "";
224 String filename
= "";
227 StackTraceElement ste
= event
.getSource();
229 classname
= ste
.getClassName();
230 methodname
= ste
.getMethodName();
231 filename
= ste
.getFileName();
232 line
= ste
.getLineNumber();
235 /* Retrieve all the requested context information we can find. */
236 Collection
<Entry
<String
, Map
<String
, Integer
>>> enabledContexts
= agent
.getEnabledAppContexts();
237 ContextInfoSerializer
.SerializedContexts contextInfo
= ContextInfoSerializer
238 .queryAndSerializeRequestedContexts(enabledContexts
);
240 eventCount
.incrementAndGet();
242 LttngLog4j2Api
.tracepointWithContext(message
, loggername
, classname
, methodname
, filename
, line
,
243 event
.getTimeMillis(), event
.getLevel().intLevel(), event
.getThreadName(),
244 contextInfo
.getEntriesArray(), contextInfo
.getStringsArray(), agent
.getDomain() == Domain
.LOG4J
);