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