Commit | Line | Data |
---|---|---|
464c4756 MJ |
1 | /* |
2 | * SPDX-License-Identifier: LGPL-2.1-only | |
3 | * | |
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> | |
7 | */ | |
8 | ||
9 | package org.lttng.ust.agent.log4j2; | |
10 | ||
11 | import java.io.IOException; | |
12 | import java.util.Collection; | |
13 | import java.util.Map; | |
14 | import java.util.Map.Entry; | |
15 | import java.util.concurrent.TimeUnit; | |
16 | import java.util.concurrent.atomic.AtomicLong; | |
17 | ||
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; | |
8063c7b0 | 28 | import org.apache.logging.log4j.message.Message; |
464c4756 MJ |
29 | import org.lttng.ust.agent.ILttngHandler; |
30 | import org.lttng.ust.agent.context.ContextInfoSerializer; | |
31 | ||
32 | /** | |
33 | * LTTng-UST Log4j 2.x log handler. | |
34 | * | |
35 | * Applications can attach this appender to their | |
36 | * {@link org.apache.log4j.Logger} to have it generate UST events from logging | |
37 | * events received through the logger. | |
38 | * | |
39 | * It sends its events to UST via the JNI library "liblttng-ust-log4j-jni.so". | |
40 | * Make sure this library is available before using this appender. | |
41 | * | |
42 | */ | |
43 | @Plugin(name = LttngLogAppender.PLUGIN_NAME, category = Core.CATEGORY_NAME, elementType = Appender.ELEMENT_TYPE, printObject = false) | |
44 | public final class LttngLogAppender extends AbstractAppender implements ILttngHandler { | |
45 | ||
46 | /** | |
47 | * The name of the appender in the configuration. | |
48 | */ | |
49 | public static final String PLUGIN_NAME = "Lttng"; | |
50 | ||
51 | private static final String SHARED_OBJECT_NAME = "lttng-ust-log4j-jni"; | |
52 | ||
53 | /** | |
54 | * Number of events logged (really sent through JNI) by this handler | |
55 | */ | |
56 | private final AtomicLong eventCount = new AtomicLong(0); | |
57 | ||
58 | private final LttngLog4j2Agent agent; | |
59 | ||
60 | /** | |
61 | * Constructor | |
62 | * | |
63 | * @param name The name of the Appender. | |
64 | * @param filter The Filter or null. | |
65 | * @param ignoreExceptions If {@code "true"} (default) exceptions encountered | |
66 | * when appending events are logged; otherwise they are | |
67 | * propagated to the caller. | |
68 | * | |
69 | * @throws IOException This handler requires the lttng-ust-log4j-jni.so | |
70 | * native library, through which it will send the | |
71 | * trace events. This exception is thrown if this | |
72 | * library cannot be found. | |
73 | * @throws SecurityException We will forward any SecurityExcepion that may be | |
74 | * thrown when trying to load the JNI library. | |
75 | */ | |
76 | protected LttngLogAppender(String name, Filter filter, boolean ignoreExceptions) | |
77 | throws IOException, SecurityException { | |
78 | ||
79 | super(name, filter, null, ignoreExceptions, Property.EMPTY_ARRAY); | |
80 | ||
81 | /* Initialize LTTng UST tracer. */ | |
82 | try { | |
83 | System.loadLibrary(SHARED_OBJECT_NAME); // $NON-NLS-1$ | |
84 | } catch (UnsatisfiedLinkError e) { | |
85 | throw new IOException(e); | |
86 | } | |
87 | ||
88 | /* Register to the relevant agent. */ | |
89 | agent = LttngLog4j2Agent.getInstance(); | |
90 | agent.registerHandler(this); | |
91 | } | |
92 | ||
93 | /** | |
94 | * Create an LttngLogAppender. | |
95 | * | |
96 | * @param name The name of the Appender, null returns null. | |
97 | * @param ignoreExceptions If {@code "true"} (default) exceptions encountered | |
98 | * when appending events are logged; otherwise they are | |
99 | * propagated to the caller. | |
100 | * @param filter The Filter or null. | |
101 | * | |
102 | * @return A new LttngLogAppender, null if the name was null. | |
103 | * | |
104 | * @throws IOException This handler requires the lttng-ust-log4j-jni.so | |
105 | * native library, through which it will send the | |
106 | * trace events. This exception is thrown if this | |
107 | * library cannot be found. | |
108 | * @throws SecurityException We will forward any SecurityExcepion that may be | |
109 | * thrown when trying to load the JNI library. | |
110 | */ | |
111 | @PluginFactory | |
112 | public static LttngLogAppender createAppender(@PluginAttribute("name") String name, | |
113 | @PluginAttribute("ignoreExceptions") Boolean ignoreExceptions, @PluginElement("Filters") Filter filter) | |
114 | throws IOException, SecurityException { | |
115 | ||
116 | if (name == null) { | |
117 | LOGGER.error("No name provided for LttngLogAppender"); | |
118 | return null; | |
119 | } | |
120 | ||
121 | if (ignoreExceptions == null) { | |
122 | ignoreExceptions = true; | |
123 | } | |
124 | ||
125 | return new LttngLogAppender(name, filter, ignoreExceptions); | |
126 | } | |
127 | ||
128 | @Override | |
129 | public synchronized void close() { | |
130 | agent.unregisterHandler(this); | |
131 | } | |
132 | ||
133 | @Override | |
134 | public void stop() { | |
135 | close(); | |
136 | super.stop(); | |
137 | ||
138 | getStatusLogger().debug("Appender Lttng stopped"); | |
139 | } | |
140 | ||
141 | @Override | |
142 | public boolean stop(final long timeout, final TimeUnit timeUnit) { | |
143 | close(); | |
144 | boolean status = super.stop(timeout, timeUnit); | |
145 | ||
146 | getStatusLogger().debug("Appender Lttng stopped with status " + status); | |
147 | ||
148 | return status; | |
149 | } | |
150 | ||
151 | /** | |
152 | * Get the number of events logged by this handler so far. This means the number | |
153 | * of events actually sent through JNI to UST. | |
154 | * | |
155 | * @return The number of events logged so far | |
156 | */ | |
157 | @Override | |
158 | public long getEventCount() { | |
159 | return eventCount.get(); | |
160 | } | |
161 | ||
162 | @Override | |
163 | public void append(LogEvent event) { | |
164 | /* | |
165 | * Check if the current message should be logged, according to the UST session | |
166 | * settings. | |
167 | */ | |
8063c7b0 MJ |
168 | String loggername = event.getLoggerName(); |
169 | if (loggername == null || !agent.isEventEnabled(loggername)) { | |
464c4756 MJ |
170 | return; |
171 | } | |
172 | ||
8063c7b0 MJ |
173 | /* |
174 | * Default value if the Message is null. | |
175 | */ | |
176 | String message = ""; | |
177 | ||
178 | Message eventMessage = event.getMessage(); | |
179 | if (eventMessage != null) { | |
180 | message = eventMessage.getFormattedMessage(); | |
181 | } | |
182 | ||
464c4756 MJ |
183 | /* |
184 | * Default values if the StackTraceElement is null. | |
185 | */ | |
186 | String classname = ""; | |
187 | String methodname = ""; | |
188 | String filename = ""; | |
189 | int line = -1; | |
190 | ||
191 | StackTraceElement ste = event.getSource(); | |
192 | if (ste != null) { | |
193 | classname = ste.getClassName(); | |
194 | methodname = ste.getMethodName(); | |
195 | filename = ste.getFileName(); | |
196 | line = ste.getLineNumber(); | |
197 | } | |
198 | ||
199 | /* Retrieve all the requested context information we can find. */ | |
200 | Collection<Entry<String, Map<String, Integer>>> enabledContexts = agent.getEnabledAppContexts(); | |
201 | ContextInfoSerializer.SerializedContexts contextInfo = ContextInfoSerializer | |
202 | .queryAndSerializeRequestedContexts(enabledContexts); | |
203 | ||
204 | eventCount.incrementAndGet(); | |
205 | ||
8063c7b0 MJ |
206 | LttngLog4j2Api.tracepointWithContext(message, loggername, classname, methodname, filename, line, |
207 | event.getTimeMillis(), event.getLevel().intLevel(), event.getThreadName(), | |
464c4756 MJ |
208 | contextInfo.getEntriesArray(), contextInfo.getStringsArray()); |
209 | } | |
210 | } |