Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id 29F07200D1E for ; Wed, 18 Oct 2017 14:01:55 +0200 (CEST) Received: by cust-asf.ponee.io (Postfix) id 2137C160BEB; Wed, 18 Oct 2017 12:01:55 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id E471B1609EE for ; Wed, 18 Oct 2017 14:01:53 +0200 (CEST) Received: (qmail 28772 invoked by uid 500); 18 Oct 2017 12:01:53 -0000 Mailing-List: contact commits-help@karaf.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@karaf.apache.org Delivered-To: mailing list commits@karaf.apache.org Received: (qmail 28761 invoked by uid 99); 18 Oct 2017 12:01:53 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 18 Oct 2017 12:01:53 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id ECB8DDF9FE; Wed, 18 Oct 2017 12:01:52 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: gnodet@apache.org To: commits@karaf.apache.org Date: Wed, 18 Oct 2017 12:01:52 -0000 Message-Id: <10816785bf4b438ba7685b5eceb5e412@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [1/2] karaf git commit: [KARAF-5354] The log:get and log:set commands should support etc/log4j2.xml configuration archived-at: Wed, 18 Oct 2017 12:01:55 -0000 Repository: karaf Updated Branches: refs/heads/master 48f26e60d -> b52309a36 [KARAF-5354] The log:get and log:set commands should support etc/log4j2.xml configuration Project: http://git-wip-us.apache.org/repos/asf/karaf/repo Commit: http://git-wip-us.apache.org/repos/asf/karaf/commit/b52309a3 Tree: http://git-wip-us.apache.org/repos/asf/karaf/tree/b52309a3 Diff: http://git-wip-us.apache.org/repos/asf/karaf/diff/b52309a3 Branch: refs/heads/master Commit: b52309a363c46de844e047d391a8d392ea1ee3f9 Parents: 965ac92 Author: Guillaume Nodet Authored: Wed Oct 18 13:59:05 2017 +0200 Committer: Guillaume Nodet Committed: Wed Oct 18 14:01:21 2017 +0200 ---------------------------------------------------------------------- .../karaf/log/core/internal/LogServiceImpl.java | 8 + .../core/internal/LogServiceLog4j2XmlImpl.java | 265 +++++++++++++++++++ .../internal/LogServiceLog4j2XmlImplTest.java | 151 +++++++++++ 3 files changed, 424 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/karaf/blob/b52309a3/log/src/main/java/org/apache/karaf/log/core/internal/LogServiceImpl.java ---------------------------------------------------------------------- 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"); } http://git-wip-us.apache.org/repos/asf/karaf/blob/b52309a3/log/src/main/java/org/apache/karaf/log/core/internal/LogServiceLog4j2XmlImpl.java ---------------------------------------------------------------------- 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..843bf51 --- /dev/null +++ b/log/src/main/java/org/apache/karaf/log/core/internal/LogServiceLog4j2XmlImpl.java @@ -0,0 +1,265 @@ +/* + * 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.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 getLevel(String logger) { + try { + Document doc = loadConfig(path); + Map loggers = getLoggers(doc); + + Map levels = new TreeMap<>(); + for (Map.Entry 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 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, "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 getLoggers(Document doc) { + Map 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; + } + +} http://git-wip-us.apache.org/repos/asf/karaf/blob/b52309a3/log/src/test/java/org/apache/karaf/log/core/internal/LogServiceLog4j2XmlImplTest.java ---------------------------------------------------------------------- diff --git a/log/src/test/java/org/apache/karaf/log/core/internal/LogServiceLog4j2XmlImplTest.java b/log/src/test/java/org/apache/karaf/log/core/internal/LogServiceLog4j2XmlImplTest.java new file mode 100644 index 0000000..294e3f0 --- /dev/null +++ b/log/src/test/java/org/apache/karaf/log/core/internal/LogServiceLog4j2XmlImplTest.java @@ -0,0 +1,151 @@ +/* + * 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.junit.Test; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import javax.xml.transform.OutputKeys; +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.ByteArrayInputStream; +import java.io.OutputStream; +import java.io.StringWriter; +import java.nio.file.Files; +import java.nio.file.StandardOpenOption; + +import static org.junit.Assert.assertEquals; + +public class LogServiceLog4j2XmlImplTest { + + @Test + public void testInsertIndentedTabs() throws Exception { + String xml = "\n" + + "\t\n" + + "\t\n" + + ""; + + String out = insertIndented(xml, false); + assertEquals( + "\n" + + "\t\n" + + "\t\t\n" + + "\t\n" + + "", out); + out = insertIndented(xml, true); + assertEquals( + "\n" + + "\t\n" + + "\t\t\n" + + "\t\n" + + "", out); + } + + @Test + public void testInsertIndentedSpaces() throws Exception { + String xml = "\n" + + " \n" + + " \n" + + ""; + + String out = insertIndented(xml, false); + assertEquals( + "\n" + + " \n" + + " \n" + + " \n" + + "", out); + out = insertIndented(xml, true); + assertEquals( + "\n" + + " \n" + + " \n" + + " \n" + + "", out); + } + + @Test + public void testInsertIndentedTabsWithRoot() throws Exception { + String xml = "\n" + + "\t\n" + + "\t\t\n" + + "\t\n" + + ""; + + String out = insertIndented(xml, false); + assertEquals( + "\n" + + "\t\n" + + "\t\t\n" + + "\t\t\n" + + "\t\n" + + "", out); + out = insertIndented(xml, true); + assertEquals( + "\n" + + "\t\n" + + "\t\t\n" + + "\t\t\n" + + "\t\n" + + "", out); + } + + @Test + public void testInsertIndentedSpacesWithRoot() throws Exception { + String xml = "\n" + + " \n" + + " \n" + + " \n" + + ""; + + String out = insertIndented(xml, false); + assertEquals( + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + "", out); + out = insertIndented(xml, true); + assertEquals( + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + "", out); + } + + private String insertIndented(String xml, boolean atBeginning) throws Exception { + Document doc = LogServiceLog4j2XmlImpl.loadConfig(null, new ByteArrayInputStream(xml.getBytes())); + Element element = doc.createElement("Logger"); + LogServiceLog4j2XmlImpl.insertIndented( + (Element) doc.getDocumentElement().getElementsByTagName("Loggers").item(0), + element, + atBeginning); + try (StringWriter os = new StringWriter()) { + TransformerFactory tFactory = TransformerFactory.newInstance(); + Transformer transformer = tFactory.newTransformer(); + transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); + transformer.transform(new DOMSource(doc, "the.xml"), new StreamResult(os)); + return os.toString(); + } + } +}