You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@karaf.apache.org by jb...@apache.org on 2018/11/25 14:24:47 UTC
[karaf] branch karaf-4.1.x updated: [KARAF-5790] Add support of
log4j2 XML in logging delegate configuration
This is an automated email from the ASF dual-hosted git repository.
jbonofre pushed a commit to branch karaf-4.1.x
in repository https://gitbox.apache.org/repos/asf/karaf.git
The following commit(s) were added to refs/heads/karaf-4.1.x by this push:
new 3f43135 [KARAF-5790] Add support of log4j2 XML in logging delegate configuration
3f43135 is described below
commit 3f43135c317fbc68ee0aa92283cbd301607b05c1
Author: Jean-Baptiste Onofré <jb...@nanthrax.net>
AuthorDate: Sun Nov 25 15:24:19 2018 +0100
[KARAF-5790] Add support of log4j2 XML in logging delegate configuration
---
.../karaf/log/core/internal/LogServiceImpl.java | 8 +
.../log/core/internal/LogServiceLog4j2XmlImpl.java | 268 +++++++++++++++++++++
2 files changed, 276 insertions(+)
diff --git a/log/src/main/java/org/apache/karaf/log/core/internal/LogServiceImpl.java b/log/src/main/java/org/apache/karaf/log/core/internal/LogServiceImpl.java
index 9c32cfc..d0c52fb 100644
--- a/log/src/main/java/org/apache/karaf/log/core/internal/LogServiceImpl.java
+++ b/log/src/main/java/org/apache/karaf/log/core/internal/LogServiceImpl.java
@@ -51,6 +51,14 @@ public class LogServiceImpl implements LogService, PaxAppender {
else if (config.get("log4j2.rootLogger.level") != null) {
return new LogServiceLog4j2Impl(config);
}
+ else if (config.get("org.ops4j.pax.logging.log4j2.config.file") != null) {
+ String file = config.get("org.ops4j.pax.logging.log4j2.config.file").toString();
+ if (file.endsWith(".xml")) {
+ return new LogServiceLog4j2XmlImpl(file);
+ } else {
+ throw new IllegalStateException("Unsupported Log4j2 configuration type: " + file);
+ }
+ }
else {
throw new IllegalStateException("Unrecognized configuration");
}
diff --git a/log/src/main/java/org/apache/karaf/log/core/internal/LogServiceLog4j2XmlImpl.java b/log/src/main/java/org/apache/karaf/log/core/internal/LogServiceLog4j2XmlImpl.java
new file mode 100644
index 0000000..23a029f
--- /dev/null
+++ b/log/src/main/java/org/apache/karaf/log/core/internal/LogServiceLog4j2XmlImpl.java
@@ -0,0 +1,268 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.log.core.internal;
+
+import org.apache.karaf.log.core.Level;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+import javax.xml.XMLConstants;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
+import java.util.Collections;
+import java.util.Map;
+import java.util.TreeMap;
+
+public class LogServiceLog4j2XmlImpl implements LogServiceInternal {
+
+ private static final String ELEMENT_LOGGERS = "Loggers";
+ private static final String ELEMENT_ROOT = "Root";
+ private static final String ELEMENT_LOGGER = "Logger";
+ private static final String ATTRIBUTE_NAME = "name";
+ private static final String ATTRIBUTE_LEVEL = "level";
+ private static final String ELEMENT_CONFIGURATION = "Configuration";
+
+ private final Path path;
+
+ LogServiceLog4j2XmlImpl(String file) {
+ this.path = Paths.get(file);
+ }
+
+ public Map<String, String> getLevel(String logger) {
+ try {
+ Document doc = loadConfig(path);
+ Map<String, Element> loggers = getLoggers(doc);
+
+ Map<String, String> levels = new TreeMap<>();
+ for (Map.Entry<String, Element> e : loggers.entrySet()) {
+ String level = e.getValue().getAttribute(ATTRIBUTE_LEVEL);
+ if (level != null && !level.isEmpty()) {
+ levels.put(e.getKey(), level);
+ }
+ }
+
+ if (ALL_LOGGER.equals(logger)) {
+ return levels;
+ }
+ String l = logger;
+ String val;
+ for (; ; ) {
+ val = levels.get(l != null ? l : ROOT_LOGGER);
+ if (val != null || l == null) {
+ return Collections.singletonMap(logger, val);
+ }
+ int idx = l.lastIndexOf('.');
+ if (idx < 0) {
+ l = null;
+ } else {
+ l = l.substring(0, idx);
+ }
+ }
+ } catch (Exception e) {
+ throw new RuntimeException("Unable to retrieve level for logger", e);
+ }
+ }
+
+ public void setLevel(String logger, String level) {
+ try {
+ Document doc = loadConfig(path);
+ Map<String, Element> loggers = getLoggers(doc);
+
+ Element element = loggers.get(logger);
+ if (element != null) {
+ if (Level.isDefault(level)) {
+ element.removeAttribute(ATTRIBUTE_LEVEL);
+ } else {
+ element.setAttribute(ATTRIBUTE_LEVEL, level);
+ }
+ }
+ else if (!Level.isDefault(level)) {
+ Element docE = doc.getDocumentElement();
+ Element docLoggers = (Element) docE.getElementsByTagName(ELEMENT_LOGGERS).item(0);
+ boolean root = ROOT_LOGGER.equals(logger);
+ if (root) {
+ element = doc.createElement(ELEMENT_ROOT);
+ element.setAttribute(ATTRIBUTE_LEVEL, level);
+ } else {
+ element = doc.createElement(ELEMENT_LOGGER);
+ element.setAttribute(ATTRIBUTE_NAME, logger);
+ element.setAttribute(ATTRIBUTE_LEVEL, level);
+ }
+ insertIndented(docLoggers, element, root);
+ } else {
+ return;
+ }
+ try (OutputStream os = Files.newOutputStream(path, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING)) {
+ TransformerFactory tFactory = TransformerFactory.newInstance();
+ Transformer transformer = tFactory.newTransformer();
+ transformer.transform(new DOMSource(doc), new StreamResult(os));
+ }
+ } catch (Exception e) {
+ throw new RuntimeException("Unable to set level for logger", e);
+ }
+ }
+
+ /**
+ * Insert the given node at beginning or end of the given node,
+ * indenting it as needed.
+ */
+ static void insertIndented(Element loggers, Element element, boolean atBeginning) {
+ NodeList loggerElements = loggers.getElementsByTagName("*");
+ if (atBeginning && loggerElements.getLength() > 0) {
+ Node insertBefore = loggers.getFirstChild();
+ if (insertBefore != null) {
+ if (insertBefore.getNodeType() == Node.TEXT_NODE) {
+ String indent = loggers.getFirstChild().getTextContent();
+ Node node = loggers.getOwnerDocument().createTextNode(indent);
+ loggers.insertBefore(node, insertBefore);
+ }
+ loggers.insertBefore(element, insertBefore);
+ } else {
+ loggers.appendChild(element);
+ }
+ } else {
+ Node insertAfter = loggerElements.getLength() > 0 ? loggerElements.item(loggerElements.getLength() - 1) : null;
+ if (insertAfter != null) {
+ if (insertAfter.getPreviousSibling() != null && insertAfter.getPreviousSibling().getNodeType() == Node.TEXT_NODE) {
+ String indent = insertAfter.getPreviousSibling().getTextContent();
+ Node node = loggers.getOwnerDocument().createTextNode(indent);
+ if (insertAfter.getNextSibling() != null) {
+ loggers.insertBefore(node, insertAfter.getNextSibling());
+ insertAfter = node;
+ } else {
+ loggers.appendChild(node);
+ }
+ }
+ if (insertAfter.getNextSibling() != null) {
+ loggers.insertBefore(element, insertAfter.getNextSibling());
+ } else {
+ loggers.appendChild(element);
+ }
+ } else {
+ if (loggers.getPreviousSibling() != null && loggers.getPreviousSibling().getNodeType() == Node.TEXT_NODE) {
+ String indent = loggers.getPreviousSibling().getTextContent();
+ String prev = indent;
+ if (indent.endsWith("\t")) {
+ indent += "\t";
+ } else {
+ int nl = indent.lastIndexOf('\n');
+ if (nl >= 0) {
+ indent = indent + indent.substring(nl + 1);
+ } else {
+ indent += "\t";
+ }
+ }
+ if (loggers.getFirstChild() != null && loggers.getPreviousSibling().getNodeType() == Node.TEXT_NODE) {
+ loggers.removeChild(loggers.getFirstChild());
+ }
+ loggers.appendChild(loggers.getOwnerDocument().createTextNode(indent));
+ loggers.appendChild(element);
+ loggers.appendChild(loggers.getOwnerDocument().createTextNode(prev));
+ } else {
+ loggers.appendChild(element);
+ }
+ }
+ }
+
+ }
+
+ static Document loadConfig(Path path) throws Exception {
+ try (InputStream is = Files.newInputStream(path)) {
+ return loadConfig(path.toString(), is);
+ }
+ }
+
+ static Document loadConfig(String id, InputStream is) throws ParserConfigurationException, SAXException, IOException {
+ final InputSource source = new InputSource(is);
+ source.setPublicId(id);
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ factory.setNamespaceAware(true);
+ factory.setValidating(false);
+ factory.setExpandEntityReferences(false);
+
+ setFeature(factory, XMLConstants.FEATURE_SECURE_PROCESSING, true);
+ setFeature(factory, "http://xml.org/sax/features/external-general-entities", false);
+ setFeature(factory, "http://xml.org/sax/features/external-parameter-entities", false);
+ setFeature(factory, "http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
+ setFeature(factory, "http://apache.org/xml/features/xinclude/fixup-base-uris", true);
+ setFeature(factory, "http://apache.org/xml/features/xinclude/fixup-language", true);
+ tryCall(() -> factory.setXIncludeAware(true));
+ DocumentBuilder documentBuilder = factory.newDocumentBuilder();
+ return documentBuilder.parse(source);
+ }
+
+ private static void setFeature(DocumentBuilderFactory factory, String name, boolean b) {
+ tryCall(() -> factory.setFeature(name, b));
+ }
+
+ interface RunnableWithException {
+ void run() throws Exception;
+ }
+
+ private static void tryCall(RunnableWithException c) {
+ try {
+ c.run();
+ } catch (Exception e) {
+ // Ignore
+ }
+ }
+
+ private Map<String, Element> getLoggers(Document doc) {
+ Map<String, Element> loggers = new TreeMap<>();
+ Element docE = doc.getDocumentElement();
+ if (!ELEMENT_CONFIGURATION.equals(docE.getLocalName())) {
+ throw new IllegalArgumentException("Xml root document should be " + ELEMENT_CONFIGURATION);
+ }
+ NodeList children = docE.getElementsByTagName(ELEMENT_LOGGERS);
+ if (children.getLength() != 1) {
+ return Collections.emptyMap();
+ }
+ NodeList loggersList = children.item(0).getChildNodes();
+ for (int i = 0; i < loggersList.getLength(); i++) {
+ Node n = loggersList.item(i);
+ if (n instanceof Element) {
+ Element e = (Element) n;
+ if (ELEMENT_ROOT.equals(e.getLocalName())) {
+ loggers.put(ROOT_LOGGER, e);
+ } else if (ELEMENT_LOGGER.equals(e.getLocalName())) {
+ String name = e.getAttribute(ATTRIBUTE_NAME);
+ if (name != null) {
+ loggers.put(name, e);
+ }
+ }
+ }
+ }
+ return loggers;
+ }
+
+}
\ No newline at end of file