1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package ch.qos.logback.classic.joran;
16
17 import ch.qos.logback.classic.Level;
18 import ch.qos.logback.classic.Logger;
19 import ch.qos.logback.classic.LoggerContext;
20 import ch.qos.logback.core.Context;
21 import ch.qos.logback.core.joran.JoranConstants;
22 import ch.qos.logback.core.joran.spi.JoranException;
23 import ch.qos.logback.core.model.util.VariableSubstitutionsHelper;
24 import ch.qos.logback.core.spi.ContextAwareBase;
25 import ch.qos.logback.core.spi.ErrorCodes;
26
27 import java.io.File;
28 import java.io.FileInputStream;
29 import java.io.IOException;
30 import java.io.InputStream;
31 import java.net.URL;
32 import java.net.URLConnection;
33 import java.util.*;
34
35 import static ch.qos.logback.core.CoreConstants.DOT;
36 import static ch.qos.logback.core.joran.JoranConstants.NULL;
37
38 public class PropertiesConfigurator extends ContextAwareBase {
39
40 static Comparator<String> LENGTH_COMPARATOR = new Comparator<String>() {
41 @Override
42 public int compare(String o1, String o2) {
43 int len1 = o1 == null ? 0 : o1.length();
44 int len2 = o2 == null ? 0 : o2.length();
45
46 int diff = len2 - len1;
47 if (diff != 0) {
48 return diff;
49 } else {
50 return o2.compareTo(o1);
51 }
52 }
53 };
54
55 static final String LOGBACK_PREFIX = "logback";
56 static final String LOGBACK_ROOT_LOGGER_PREFIX = LOGBACK_PREFIX + DOT + "root";
57 static final int LOGBACK_ROOT_LOGGER_PREFIX_LENGTH = LOGBACK_ROOT_LOGGER_PREFIX.length();
58
59 public static final String LOGBACK_LOGGER_PREFIX = LOGBACK_PREFIX + DOT + "logger" + DOT;
60 static final int LOGBACK_LOGGER_PREFIX_LENGTH = LOGBACK_LOGGER_PREFIX.length();
61
62 VariableSubstitutionsHelper variableSubstitutionsHelper;
63
64 LoggerContext getLoggerContext() {
65 return (LoggerContext) getContext();
66 }
67
68 @Override
69 public void setContext(Context context) {
70 super.setContext(context);
71 }
72
73 public void doConfigure(URL url) throws JoranException {
74 try {
75 URLConnection urlConnection = url.openConnection();
76
77
78 urlConnection.setUseCaches(false);
79 InputStream in = urlConnection.getInputStream();
80 doConfigure(in);
81 } catch (IOException ioe) {
82 String errMsg = "Could not open URL [" + url + "].";
83 addError(errMsg, ioe);
84 throw new JoranException(errMsg, ioe);
85 }
86 }
87
88 public void doConfigure(File file) throws JoranException {
89 try (FileInputStream fileInputStream = new FileInputStream(file)) {
90 doConfigure(fileInputStream);
91 } catch (IOException e) {
92 throw new JoranException("Failed to load file " + file, e);
93 }
94 }
95
96 public void doConfigure(String filename) throws JoranException {
97 doConfigure(new File(filename));
98 }
99
100 public void doConfigure(InputStream inputStream) throws JoranException {
101 Properties props = new Properties();
102 try {
103 props.load(inputStream);
104 } catch (IOException e) {
105 throw new JoranException("Failed to load from input stream", e);
106 } finally {
107 close(inputStream);
108 }
109
110 doConfigure(props);
111 }
112
113 private void close(InputStream inputStream) throws JoranException {
114 if (inputStream != null) {
115 try {
116 inputStream.close();
117 } catch (IOException e) {
118 throw new JoranException("failed to close stream", e);
119 }
120 }
121 }
122
123 void doConfigure(Properties properties) {
124 Map<String, String> variablesMap = extractVariablesMap(properties);
125 Map<String, String> instructionMap = extractLogbackInstructionMap(properties);
126
127 this.variableSubstitutionsHelper = new VariableSubstitutionsHelper(context, variablesMap);
128 configureLoggers(instructionMap);
129 configureRootLogger(instructionMap);
130 }
131
132 void configureRootLogger(Map<String, String> instructionMap) {
133 String val = subst(instructionMap.get(LOGBACK_ROOT_LOGGER_PREFIX));
134 if (val != null) {
135 setLevel(org.slf4j.Logger.ROOT_LOGGER_NAME, val);
136 }
137 }
138
139 void configureLoggers(Map<String, String> instructionMap) {
140 for (String key : instructionMap.keySet()) {
141 if (key.startsWith(LOGBACK_LOGGER_PREFIX)) {
142 String loggerName = key.substring(LOGBACK_LOGGER_PREFIX_LENGTH);
143 String value = subst(instructionMap.get(key));
144 setLevel(loggerName, value);
145 }
146 }
147 }
148
149 private void setLevel(String loggerName, String levelStr) {
150 Logger logger = getLoggerContext().getLogger(loggerName);
151
152 if (JoranConstants.INHERITED.equalsIgnoreCase(levelStr) || NULL.equalsIgnoreCase(levelStr)) {
153 if (Logger.ROOT_LOGGER_NAME.equalsIgnoreCase(loggerName)) {
154 addError(ErrorCodes.ROOT_LEVEL_CANNOT_BE_SET_TO_NULL);
155 } else {
156 addInfo("Setting level of logger [" + loggerName + "] to null, i.e. INHERITED");
157 logger.setLevel(null);
158 }
159 } else {
160 Level level = Level.toLevel(levelStr);
161 logger.setLevel(level);
162 }
163 }
164
165 private Map<String, String> extractVariablesMap(Properties properties) {
166 Map<String, String> variablesMap = new HashMap<>();
167 for (String key : properties.stringPropertyNames()) {
168 if (key != null && !key.startsWith(LOGBACK_PREFIX)) {
169 variablesMap.put(key, properties.getProperty(key));
170 }
171 }
172
173 return variablesMap;
174 }
175
176 private Map<String, String> extractLogbackInstructionMap(Properties properties) {
177 Map<String, String> instructionMap = new TreeMap<>(LENGTH_COMPARATOR);
178 for (String key : properties.stringPropertyNames()) {
179 if (key != null && key.startsWith(LOGBACK_PREFIX)) {
180 instructionMap.put(key, properties.getProperty(key));
181 }
182 }
183 return instructionMap;
184 }
185
186 public String subst(String ref) {
187
188 String substituted = variableSubstitutionsHelper.subst(ref);
189 if (ref != null && !ref.equals(substituted)) {
190 addInfo("value \"" + substituted + "\" substituted for \"" + ref + "\"");
191 }
192 return substituted;
193 }
194
195 }