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 | */ | |
7 | ||
8 | package org.lttng.ust.agent.log4j2; | |
9 | ||
10 | import java.util.Collection; | |
11 | import java.util.Map; | |
12 | import java.util.Set; | |
13 | import java.util.TreeSet; | |
14 | ||
15 | import org.apache.logging.log4j.LogManager; | |
16 | import org.apache.logging.log4j.core.Appender; | |
17 | import org.apache.logging.log4j.core.Logger; | |
18 | import org.apache.logging.log4j.core.LoggerContext; | |
19 | import org.apache.logging.log4j.core.impl.Log4jContextFactory; | |
20 | import org.apache.logging.log4j.core.selector.ContextSelector; | |
21 | import org.apache.logging.log4j.spi.LoggerContextFactory; | |
22 | import org.apache.logging.log4j.status.StatusLogger; | |
23 | import org.lttng.ust.agent.AbstractLttngAgent; | |
24 | ||
25 | /** | |
26 | * Agent implementation for Log4j 2.x. | |
27 | */ | |
28 | class LttngLog4j2Agent extends AbstractLttngAgent<LttngLogAppender> { | |
29 | ||
30 | private static LttngLog4j2Agent instance = null; | |
31 | ||
32 | private LttngLog4j2Agent() { | |
33 | super(Domain.LOG4J); | |
34 | } | |
35 | ||
36 | public static synchronized LttngLog4j2Agent getInstance() { | |
37 | if (instance == null) { | |
38 | instance = new LttngLog4j2Agent(); | |
39 | } | |
40 | return instance; | |
41 | } | |
42 | ||
43 | @Override | |
44 | public Collection<String> listAvailableEvents() { | |
45 | Set<String> eventNames = new TreeSet<>(); | |
46 | ||
47 | LoggerContextFactory contextFactory = LogManager.getFactory(); | |
48 | if (!(contextFactory instanceof Log4jContextFactory)) { | |
49 | /* Using a custom ContextFactory is not supported. */ | |
50 | StatusLogger.getLogger().error("Can't list events with custom ContextFactory"); | |
51 | return eventNames; | |
52 | } | |
53 | ||
54 | ContextSelector selector = ((Log4jContextFactory) contextFactory).getSelector(); | |
55 | ||
56 | for (LoggerContext logContext : selector.getLoggerContexts()) { | |
57 | Collection<? extends Logger> loggers = logContext.getLoggers(); | |
58 | for (Logger logger : loggers) { | |
59 | /* | |
60 | * Check if that logger has at least one LTTng log4j appender attached. | |
61 | */ | |
62 | if (hasLttngAppenderAttached(logger)) { | |
63 | eventNames.add(logger.getName()); | |
64 | } | |
65 | } | |
66 | } | |
67 | return eventNames; | |
68 | } | |
69 | ||
70 | /* | |
71 | * Check if a logger has an LttngLogAppender attached. | |
72 | * | |
73 | * @param logger the Logger to check, null returns false | |
74 | * @return true if the logger or its parent has at least one LttngLogAppender attached | |
75 | */ | |
76 | private static boolean hasLttngAppenderAttached(Logger logger) { | |
77 | ||
78 | if (logger == null) { | |
79 | return false; | |
80 | } | |
81 | ||
82 | /* | |
83 | * Check all the appenders associated with the logger and return true if one of | |
84 | * them is an LttngLogAppender. | |
85 | */ | |
86 | Map<String, Appender> appenders = logger.getAppenders(); | |
87 | for (Map.Entry<String, Appender> appender : appenders.entrySet()) { | |
88 | if (appender.getValue() instanceof LttngLogAppender) { | |
89 | return true; | |
90 | } | |
91 | } | |
92 | ||
93 | /* | |
94 | * A parent logger, if any, may be connected to an LTTng handler. In this case, | |
95 | * we will want to include this child logger in the output, since it will be | |
96 | * accessible by LTTng. | |
97 | * | |
98 | * Despite the doc, getParent can return null based on the implementation as of | |
99 | * log4j 2.17.1. | |
100 | * | |
101 | * The getParent function is there as a backward compat for 1.x. It is not clear | |
102 | * in which context it should be used. The cost of doing the lookup is minimal | |
103 | * and mimics what was done for the 1.x agent. | |
104 | */ | |
105 | return hasLttngAppenderAttached(logger.getParent()); | |
106 | ||
107 | } | |
108 | } |