This is an automated email from the ASF dual-hosted git repository.
mattsicker pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git
The following commit(s) were added to refs/heads/main by this push:
new d48acf3b6d Remove Jackson JSON configuration
d48acf3b6d is described below
commit d48acf3b6d8a3fc339fe20fc7aa0f466186062b0
Author: Matt Sicker <[email protected]>
AuthorDate: Fri Dec 1 15:49:36 2023 -0600
Remove Jackson JSON configuration
This merges the Jackson JSON and YAML configuration plugins into just a
YAML plugin as JSON is already supported by a built-in parser. This also
moves the built-in parser plugin to the package where the Jackson JSON
plugins were.
---
.../log4j/core/config/jason/JsonConfiguration.java | 238 -------------------
.../config/jason/JsonConfigurationFactory.java | 46 ----
.../log4j/core/config/jason/package-info.java | 22 --
.../log4j/core/config/json/JsonConfiguration.java | 251 +++++++++------------
.../core/config/json/JsonConfigurationFactory.java | 40 +---
.../log4j/core/config/json/package-info.java | 4 +-
.../log4j/core/config/yaml/YamlConfiguration.java | 222 +++++++++++++++++-
.../log4j/core/config/yaml/package-info.java | 4 +-
.../.3.x.x/remove_jackson_json_config.xml | 27 +++
9 files changed, 369 insertions(+), 485 deletions(-)
diff --git
a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/jason/JsonConfiguration.java
b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/jason/JsonConfiguration.java
deleted file mode 100644
index 54eb85bdb3..0000000000
---
a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/jason/JsonConfiguration.java
+++ /dev/null
@@ -1,238 +0,0 @@
-/*
- * 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.logging.log4j.core.config.jason;
-
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.concurrent.atomic.AtomicInteger;
-import org.apache.logging.log4j.core.LoggerContext;
-import org.apache.logging.log4j.core.config.AbstractConfiguration;
-import org.apache.logging.log4j.core.config.Configuration;
-import org.apache.logging.log4j.core.config.ConfigurationSource;
-import org.apache.logging.log4j.core.config.LoggerConfig;
-import org.apache.logging.log4j.core.config.Reconfigurable;
-import org.apache.logging.log4j.core.config.status.StatusConfiguration;
-import org.apache.logging.log4j.plugins.Node;
-import org.apache.logging.log4j.plugins.model.PluginType;
-import org.apache.logging.log4j.util.Cast;
-import org.apache.logging.log4j.util.JsonReader;
-
-public class JsonConfiguration extends AbstractConfiguration implements
Reconfigurable {
-
- private final List<Status> statuses = new ArrayList<>();
- private Map<String, Object> root;
-
- public JsonConfiguration(final LoggerContext loggerContext, final
ConfigurationSource configurationSource) {
- super(loggerContext, configurationSource);
- try {
- final byte[] bytes;
- try (final var configStream =
configurationSource.getInputStream()) {
- bytes = configStream.readAllBytes();
- root = Cast.cast(JsonReader.read(new String(bytes,
StandardCharsets.UTF_8)));
- }
- if (root.size() == 1) {
- for (final Object value : root.values()) {
- root = Cast.cast(value);
- }
- }
- processAttributes(rootNode, root);
- final StatusConfiguration statusConfig = new
StatusConfiguration().setStatus(getDefaultStatus());
- final AtomicInteger monitorIntervalSeconds = new AtomicInteger();
-
- rootNode.getAttributes().forEach((key, value) -> {
- if ("status".equalsIgnoreCase(key)) {
- statusConfig.setStatus(value);
- } else if ("dest".equalsIgnoreCase(key)) {
- statusConfig.setDestination(value);
- } else if ("shutdownHook".equalsIgnoreCase(key)) {
- isShutdownHookEnabled = !"disable".equalsIgnoreCase(value);
- } else if ("shutdownTimeout".equalsIgnoreCase(key)) {
- shutdownTimeoutMillis = Long.parseLong(value);
- } else if ("verbose".equalsIgnoreCase(key)) {
- statusConfig.setVerbosity(value);
- } else if ("packages".equalsIgnoreCase(key)) {
- LOGGER.warn("The packages attribute is no longer
supported");
- } else if ("name".equalsIgnoreCase(key)) {
- setName(value);
- } else if ("monitorInterval".equalsIgnoreCase(key)) {
- monitorIntervalSeconds.setOpaque(Integer.parseInt(value));
- } else if ("advertiser".equalsIgnoreCase(key)) {
- createAdvertiser(value, configurationSource, bytes,
"application/json");
- }
- });
- initializeWatchers(this, configurationSource,
monitorIntervalSeconds.getOpaque());
- statusConfig.initialize();
- if (getName() == null) {
- setName(configurationSource.getLocation());
- }
- } catch (final Exception e) {
- LOGGER.error("Error parsing {}",
configurationSource.getLocation(), e);
- }
- }
-
- @Override
- public void setup() {
- final List<Node> children = rootNode.getChildren();
- root.forEach((key, value) -> {
- if (value instanceof Map) {
- LOGGER.debug("Processing node for object {}", key);
- children.add(constructNode(key, rootNode, Cast.cast(value)));
- }
- });
- LOGGER.debug("Completed parsing configuration");
- if (statuses.size() > 0) {
- for (final var s : statuses) {
- LOGGER.error("Error processing element {}: {}", s.name,
s.errorType);
- }
- }
- }
-
- private Node constructNode(final String key, final Node parent, final
Map<String, Object> value) {
- final PluginType<?> pluginType = corePlugins.get(key);
- final Node node = new Node(parent, key, pluginType);
- processAttributes(node, value);
- final int size = node.getChildren().size();
- value.forEach((k, v) -> {
- if (isValueType(v)) {
- LOGGER.debug("Node {} is of type {}", k, v != null ?
v.getClass() : null);
- return;
- }
- if (pluginType == null) {
- statuses.add(new Status(v, k, ErrorType.CLASS_NOT_FOUND));
- return;
- }
- if (v instanceof List<?>) {
- LOGGER.debug("Processing node for array {}", k);
- ((List<?>) v).forEach(object -> {
- if (object instanceof Map<?, ?>) {
- final Map<String, Object> map = Cast.cast(object);
- final String type = getType(map).orElse(k);
- final PluginType<?> entryType = corePlugins.get(type);
- final Node child = new Node(node, k, entryType);
- processAttributes(child, map);
- if (type.equalsIgnoreCase(k)) {
- LOGGER.debug("Processing {}[{}]", k, size);
- } else {
- LOGGER.debug("Processing {} {}[{}]", type, k,
size);
- }
- map.forEach((itemKey, itemValue) -> {
- if (itemValue instanceof Map<?, ?>) {
- LOGGER.debug("Processing node for object {}",
itemKey);
- child.addChild(constructNode(itemKey, child,
Cast.cast(itemValue)));
- } else if (itemValue instanceof List<?>) {
- final List<?> list = (List<?>) itemValue;
- LOGGER.debug("Processing array for object {}",
itemKey);
- list.forEach(
- subValue ->
child.addChild(constructNode(itemKey, child, Cast.cast(subValue))));
- }
- });
- node.addChild(child);
- }
- });
- } else {
- LOGGER.debug("Processing node for object {}", k);
- node.addChild(constructNode(k, node, Cast.cast(v)));
- }
- });
-
- final String t;
- if (pluginType == null) {
- t = "null";
- } else {
- t = pluginType.getElementType() + ':' +
pluginType.getPluginClass();
- }
-
- final String p = node.getParent() == null
- ? "null"
- : node.getParent().getName() == null
- ? LoggerConfig.ROOT
- : node.getParent().getName();
- LOGGER.debug("Returning {} with parent {} of type {}", node.getName(),
p, t);
- return node;
- }
-
- @Override
- public Configuration reconfigure() {
- try {
- final ConfigurationSource configurationSource =
- getConfigurationSource().resetInputStream();
- if (configurationSource == null) {
- return null;
- }
- return new JsonConfiguration(getLoggerContext(),
configurationSource);
- } catch (final IOException e) {
- LOGGER.error("Cannot locate file {}", getConfigurationSource(), e);
- }
- return null;
- }
-
- private static boolean isValueType(final Object value) {
- return !(value instanceof Map<?, ?> || value instanceof List<?>);
- }
-
- private static void processAttributes(final Node parent, final Map<String,
Object> node) {
- final Map<String, String> attributes = parent.getAttributes();
- node.forEach((key, value) -> {
- if (!key.equalsIgnoreCase("type") && isValueType(value)) {
- attributes.put(key, String.valueOf(value));
- }
- });
- }
-
- private static Optional<String> getType(final Map<String, Object> node) {
- for (final Map.Entry<String, Object> entry : node.entrySet()) {
- if (entry.getKey().equalsIgnoreCase("type")) {
- final Object value = entry.getValue();
- if (isValueType(value)) {
- return Optional.of(String.valueOf(value));
- }
- }
- }
- return Optional.empty();
- }
-
- /**
- * The error that occurred.
- */
- private enum ErrorType {
- CLASS_NOT_FOUND
- }
-
- /**
- * Status for recording errors.
- */
- private static final class Status {
- private final Object node;
- private final String name;
- private final ErrorType errorType;
-
- private Status(final Object node, final String name, final ErrorType
errorType) {
- this.node = node;
- this.name = name;
- this.errorType = errorType;
- }
-
- @Override
- public String toString() {
- return "Status{" + "node=" + node + ", name='" + name + '\'' + ",
errorType=" + errorType + '}';
- }
- }
-}
diff --git
a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/jason/JsonConfigurationFactory.java
b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/jason/JsonConfigurationFactory.java
deleted file mode 100644
index c0b992d1e2..0000000000
---
a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/jason/JsonConfigurationFactory.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * 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.logging.log4j.core.config.jason;
-
-import org.apache.logging.log4j.core.LoggerContext;
-import org.apache.logging.log4j.core.config.Configuration;
-import org.apache.logging.log4j.core.config.ConfigurationFactory;
-import org.apache.logging.log4j.core.config.ConfigurationSource;
-import org.apache.logging.log4j.core.config.Order;
-import org.apache.logging.log4j.plugins.Namespace;
-import org.apache.logging.log4j.plugins.Plugin;
-
-@Namespace(ConfigurationFactory.NAMESPACE)
-@Plugin("JsonConfigurationFactory")
-@Order(6)
-public class JsonConfigurationFactory extends ConfigurationFactory {
-
- /**
- * The file extensions supported by this factory.
- */
- private static final String[] SUFFIXES = new String[] {".json", ".jsn"};
-
- @Override
- protected String[] getSupportedTypes() {
- return SUFFIXES;
- }
-
- @Override
- public Configuration getConfiguration(final LoggerContext loggerContext,
final ConfigurationSource source) {
- return new JsonConfiguration(loggerContext, source);
- }
-}
diff --git
a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/jason/package-info.java
b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/jason/package-info.java
deleted file mode 100644
index 64e0a011db..0000000000
---
a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/jason/package-info.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * 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.
- */
-@Export
-@Version("1.0.0")
-package org.apache.logging.log4j.core.config.jason;
-
-import org.osgi.annotation.bundle.Export;
-import org.osgi.annotation.versioning.Version;
diff --git
a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/json/JsonConfiguration.java
b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/json/JsonConfiguration.java
index bfdd3a8a88..0698962d20 100644
---
a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/json/JsonConfiguration.java
+++
b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/json/JsonConfiguration.java
@@ -16,16 +16,13 @@
*/
package org.apache.logging.log4j.core.config.json;
-import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import java.io.ByteArrayInputStream;
import java.io.IOException;
-import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
-import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.atomic.AtomicInteger;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.config.AbstractConfiguration;
import org.apache.logging.log4j.core.config.Configuration;
@@ -35,37 +32,32 @@ import org.apache.logging.log4j.core.config.Reconfigurable;
import org.apache.logging.log4j.core.config.status.StatusConfiguration;
import org.apache.logging.log4j.plugins.Node;
import org.apache.logging.log4j.plugins.model.PluginType;
+import org.apache.logging.log4j.util.Cast;
+import org.apache.logging.log4j.util.JsonReader;
-/**
- * Creates a Node hierarchy from a JSON file.
- */
public class JsonConfiguration extends AbstractConfiguration implements
Reconfigurable {
- private final List<Status> status = new ArrayList<>();
- private JsonNode root;
+ private final List<Status> statuses = new ArrayList<>();
+ private Map<String, Object> root;
- public JsonConfiguration(final LoggerContext loggerContext, final
ConfigurationSource configSource) {
- super(loggerContext, configSource);
- final byte[] buffer;
+ public JsonConfiguration(final LoggerContext loggerContext, final
ConfigurationSource configurationSource) {
+ super(loggerContext, configurationSource);
try {
- try (final InputStream configStream =
configSource.getInputStream()) {
- buffer = configStream.readAllBytes();
+ final byte[] bytes;
+ try (final var configStream =
configurationSource.getInputStream()) {
+ bytes = configStream.readAllBytes();
+ root = Cast.cast(JsonReader.read(new String(bytes,
StandardCharsets.UTF_8)));
}
- final InputStream is = new ByteArrayInputStream(buffer);
- root = getObjectMapper().readTree(is);
if (root.size() == 1) {
- for (final JsonNode node : root) {
- root = node;
+ for (final Object value : root.values()) {
+ root = Cast.cast(value);
}
}
processAttributes(rootNode, root);
final StatusConfiguration statusConfig = new
StatusConfiguration().setStatus(getDefaultStatus());
- int monitorIntervalSeconds = 0;
- for (final Map.Entry<String, String> entry :
- rootNode.getAttributes().entrySet()) {
- final String key = entry.getKey();
- final String value =
getConfigurationStrSubstitutor().replace(entry.getValue());
- // TODO: this duplicates a lot of the XmlConfiguration
constructor
+ final AtomicInteger monitorIntervalSeconds = new AtomicInteger();
+
+ rootNode.getAttributes().forEach((key, value) -> {
if ("status".equalsIgnoreCase(key)) {
statusConfig.setStatus(value);
} else if ("dest".equalsIgnoreCase(key)) {
@@ -74,126 +66,98 @@ public class JsonConfiguration extends
AbstractConfiguration implements Reconfig
isShutdownHookEnabled = !"disable".equalsIgnoreCase(value);
} else if ("shutdownTimeout".equalsIgnoreCase(key)) {
shutdownTimeoutMillis = Long.parseLong(value);
- } else if ("verbose".equalsIgnoreCase(entry.getKey())) {
+ } else if ("verbose".equalsIgnoreCase(key)) {
statusConfig.setVerbosity(value);
} else if ("packages".equalsIgnoreCase(key)) {
LOGGER.warn("The packages attribute is no longer
supported");
} else if ("name".equalsIgnoreCase(key)) {
setName(value);
} else if ("monitorInterval".equalsIgnoreCase(key)) {
- monitorIntervalSeconds = Integer.parseInt(value);
+ monitorIntervalSeconds.setOpaque(Integer.parseInt(value));
} else if ("advertiser".equalsIgnoreCase(key)) {
- createAdvertiser(value, configSource, buffer,
"application/json");
+ createAdvertiser(value, configurationSource, bytes,
"application/json");
}
- }
- initializeWatchers(this, configSource, monitorIntervalSeconds);
+ });
+ initializeWatchers(this, configurationSource,
monitorIntervalSeconds.getOpaque());
statusConfig.initialize();
if (getName() == null) {
- setName(configSource.getLocation());
+ setName(configurationSource.getLocation());
}
- } catch (final Exception ex) {
- LOGGER.error("Error parsing " + configSource.getLocation(), ex);
+ } catch (final Exception e) {
+ LOGGER.error("Error parsing {}",
configurationSource.getLocation(), e);
}
}
- protected ObjectMapper getObjectMapper() {
- return new ObjectMapper().configure(JsonParser.Feature.ALLOW_COMMENTS,
true);
- }
-
@Override
public void setup() {
- final Iterator<Map.Entry<String, JsonNode>> iter = root.fields();
final List<Node> children = rootNode.getChildren();
- while (iter.hasNext()) {
- final Map.Entry<String, JsonNode> entry = iter.next();
- final JsonNode n = entry.getValue();
- if (n.isObject()) {
- LOGGER.debug("Processing node for object {}", entry.getKey());
- children.add(constructNode(entry.getKey(), rootNode, n));
- } else if (n.isArray()) {
- LOGGER.error("Arrays are not supported at the root
configuration.");
+ root.forEach((key, value) -> {
+ if (value instanceof Map) {
+ LOGGER.debug("Processing node for object {}", key);
+ children.add(constructNode(key, rootNode, Cast.cast(value)));
}
- }
+ });
LOGGER.debug("Completed parsing configuration");
- if (status.size() > 0) {
- for (final Status s : status) {
+ if (statuses.size() > 0) {
+ for (final var s : statuses) {
LOGGER.error("Error processing element {}: {}", s.name,
s.errorType);
}
}
}
- @Override
- public Configuration reconfigure() {
- try {
- final ConfigurationSource source =
getConfigurationSource().resetInputStream();
- if (source == null) {
- return null;
+ private Node constructNode(final String key, final Node parent, final
Map<String, Object> value) {
+ final PluginType<?> pluginType = corePlugins.get(key);
+ final Node node = new Node(parent, key, pluginType);
+ processAttributes(node, value);
+ final int size = node.getChildren().size();
+ value.forEach((k, v) -> {
+ if (isValueType(v)) {
+ LOGGER.debug("Node {} is of type {}", k, v != null ?
v.getClass() : null);
+ return;
}
- return new JsonConfiguration(getLoggerContext(), source);
- } catch (final IOException ex) {
- LOGGER.error("Cannot locate file {}", getConfigurationSource(),
ex);
- }
- return null;
- }
-
- private Node constructNode(final String name, final Node parent, final
JsonNode jsonNode) {
- final PluginType<?> type = corePlugins.get(name);
- final Node node = new Node(parent, name, type);
- processAttributes(node, jsonNode);
- final Iterator<Map.Entry<String, JsonNode>> iter = jsonNode.fields();
- final List<Node> children = node.getChildren();
- while (iter.hasNext()) {
- final Map.Entry<String, JsonNode> entry = iter.next();
- final JsonNode n = entry.getValue();
- if (n.isArray() || n.isObject()) {
- if (type == null) {
- status.add(new Status(name, n, ErrorType.CLASS_NOT_FOUND));
- }
- if (n.isArray()) {
- LOGGER.debug("Processing node for array {}",
entry.getKey());
- for (int i = 0; i < n.size(); ++i) {
- final String pluginType = getType(n.get(i),
entry.getKey());
- final PluginType<?> entryType =
corePlugins.get(pluginType);
- final Node item = new Node(node, entry.getKey(),
entryType);
- processAttributes(item, n.get(i));
- if (pluginType.equals(entry.getKey())) {
- LOGGER.debug("Processing {}[{}]", entry.getKey(),
i);
+ if (pluginType == null) {
+ statuses.add(new Status(v, k, ErrorType.CLASS_NOT_FOUND));
+ return;
+ }
+ if (v instanceof List<?>) {
+ LOGGER.debug("Processing node for array {}", k);
+ ((List<?>) v).forEach(object -> {
+ if (object instanceof Map<?, ?>) {
+ final Map<String, Object> map = Cast.cast(object);
+ final String type = getType(map).orElse(k);
+ final PluginType<?> entryType = corePlugins.get(type);
+ final Node child = new Node(node, k, entryType);
+ processAttributes(child, map);
+ if (type.equalsIgnoreCase(k)) {
+ LOGGER.debug("Processing {}[{}]", k, size);
} else {
- LOGGER.debug("Processing {} {}[{}]", pluginType,
entry.getKey(), i);
+ LOGGER.debug("Processing {} {}[{}]", type, k,
size);
}
- final Iterator<Map.Entry<String, JsonNode>> itemIter =
- n.get(i).fields();
- final List<Node> itemChildren = item.getChildren();
- while (itemIter.hasNext()) {
- final Map.Entry<String, JsonNode> itemEntry =
itemIter.next();
- if (itemEntry.getValue().isObject()) {
- LOGGER.debug("Processing node for object {}",
itemEntry.getKey());
-
itemChildren.add(constructNode(itemEntry.getKey(), item, itemEntry.getValue()));
- } else if (itemEntry.getValue().isArray()) {
- final JsonNode array = itemEntry.getValue();
- final String entryName = itemEntry.getKey();
- LOGGER.debug("Processing array for object {}",
entryName);
- for (int j = 0; j < array.size(); ++j) {
- itemChildren.add(constructNode(entryName,
item, array.get(j)));
- }
+ map.forEach((itemKey, itemValue) -> {
+ if (itemValue instanceof Map<?, ?>) {
+ LOGGER.debug("Processing node for object {}",
itemKey);
+ child.addChild(constructNode(itemKey, child,
Cast.cast(itemValue)));
+ } else if (itemValue instanceof List<?>) {
+ final List<?> list = (List<?>) itemValue;
+ LOGGER.debug("Processing array for object {}",
itemKey);
+ list.forEach(
+ subValue ->
child.addChild(constructNode(itemKey, child, Cast.cast(subValue))));
}
- }
- children.add(item);
+ });
+ node.addChild(child);
}
- } else {
- LOGGER.debug("Processing node for object {}",
entry.getKey());
- children.add(constructNode(entry.getKey(), node, n));
- }
+ });
} else {
- LOGGER.debug("Node {} is of type {}", entry.getKey(),
n.getNodeType());
+ LOGGER.debug("Processing node for object {}", k);
+ node.addChild(constructNode(k, node, Cast.cast(v)));
}
- }
+ });
final String t;
- if (type == null) {
+ if (pluginType == null) {
t = "null";
} else {
- t = type.getElementType() + ':' + type.getPluginClass();
+ t = pluginType.getElementType() + ':' +
pluginType.getPluginClass();
}
final String p = node.getParent() == null
@@ -205,37 +169,44 @@ public class JsonConfiguration extends
AbstractConfiguration implements Reconfig
return node;
}
- private String getType(final JsonNode node, final String name) {
- final Iterator<Map.Entry<String, JsonNode>> iter = node.fields();
- while (iter.hasNext()) {
- final Map.Entry<String, JsonNode> entry = iter.next();
- if (entry.getKey().equalsIgnoreCase("type")) {
- final JsonNode n = entry.getValue();
- if (n.isValueNode()) {
- return n.asText();
- }
+ @Override
+ public Configuration reconfigure() {
+ try {
+ final ConfigurationSource configurationSource =
+ getConfigurationSource().resetInputStream();
+ if (configurationSource == null) {
+ return null;
}
+ return new JsonConfiguration(getLoggerContext(),
configurationSource);
+ } catch (final IOException e) {
+ LOGGER.error("Cannot locate file {}", getConfigurationSource(), e);
}
- return name;
+ return null;
}
- private void processAttributes(final Node parent, final JsonNode node) {
- final Map<String, String> attrs = parent.getAttributes();
- final Iterator<Map.Entry<String, JsonNode>> iter = node.fields();
- while (iter.hasNext()) {
- final Map.Entry<String, JsonNode> entry = iter.next();
- if (!entry.getKey().equalsIgnoreCase("type")) {
- final JsonNode n = entry.getValue();
- if (n.isValueNode()) {
- attrs.put(entry.getKey(), n.asText());
- }
+ private static boolean isValueType(final Object value) {
+ return !(value instanceof Map<?, ?> || value instanceof List<?>);
+ }
+
+ private static void processAttributes(final Node parent, final Map<String,
Object> node) {
+ final Map<String, String> attributes = parent.getAttributes();
+ node.forEach((key, value) -> {
+ if (!key.equalsIgnoreCase("type") && isValueType(value)) {
+ attributes.put(key, String.valueOf(value));
}
- }
+ });
}
- @Override
- public String toString() {
- return getClass().getSimpleName() + "[location=" +
getConfigurationSource() + "]";
+ private static Optional<String> getType(final Map<String, Object> node) {
+ for (final Map.Entry<String, Object> entry : node.entrySet()) {
+ if (entry.getKey().equalsIgnoreCase("type")) {
+ final Object value = entry.getValue();
+ if (isValueType(value)) {
+ return Optional.of(String.valueOf(value));
+ }
+ }
+ }
+ return Optional.empty();
}
/**
@@ -248,20 +219,20 @@ public class JsonConfiguration extends
AbstractConfiguration implements Reconfig
/**
* Status for recording errors.
*/
- private static class Status {
- private final JsonNode node;
+ private static final class Status {
+ private final Object node;
private final String name;
private final ErrorType errorType;
- public Status(final String name, final JsonNode node, final ErrorType
errorType) {
- this.name = name;
+ private Status(final Object node, final String name, final ErrorType
errorType) {
this.node = node;
+ this.name = name;
this.errorType = errorType;
}
@Override
public String toString() {
- return "Status [name=" + name + ", errorType=" + errorType + ",
node=" + node + "]";
+ return "Status{" + "node=" + node + ", name='" + name + '\'' + ",
errorType=" + errorType + '}';
}
}
}
diff --git
a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/json/JsonConfigurationFactory.java
b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/json/JsonConfigurationFactory.java
index 9ac8bb8607..048b0a9f62 100644
---
a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/json/JsonConfigurationFactory.java
+++
b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/json/JsonConfigurationFactory.java
@@ -20,8 +20,13 @@ import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.ConfigurationFactory;
import org.apache.logging.log4j.core.config.ConfigurationSource;
-import org.apache.logging.log4j.core.util.Loader;
+import org.apache.logging.log4j.core.config.Order;
+import org.apache.logging.log4j.plugins.Namespace;
+import org.apache.logging.log4j.plugins.Plugin;
+@Namespace(ConfigurationFactory.NAMESPACE)
+@Plugin("JsonConfigurationFactory")
+@Order(6)
public class JsonConfigurationFactory extends ConfigurationFactory {
/**
@@ -29,42 +34,13 @@ public class JsonConfigurationFactory extends
ConfigurationFactory {
*/
private static final String[] SUFFIXES = new String[] {".json", ".jsn"};
- private static final String[] dependencies = new String[] {
- "com.fasterxml.jackson.databind.ObjectMapper",
- "com.fasterxml.jackson.databind.JsonNode",
- "com.fasterxml.jackson.core.JsonParser"
- };
-
- private final boolean isActive;
-
- public JsonConfigurationFactory() {
- for (final String dependency : dependencies) {
- if (!Loader.isClassAvailable(dependency)) {
- LOGGER.debug(
- "Missing dependencies for Json support,
ConfigurationFactory {} is inactive",
- getClass().getName());
- isActive = false;
- return;
- }
- }
- isActive = true;
- }
-
@Override
- protected boolean isActive() {
- return isActive;
+ protected String[] getSupportedTypes() {
+ return SUFFIXES;
}
@Override
public Configuration getConfiguration(final LoggerContext loggerContext,
final ConfigurationSource source) {
- if (!isActive) {
- return null;
- }
return new JsonConfiguration(loggerContext, source);
}
-
- @Override
- public String[] getSupportedTypes() {
- return SUFFIXES;
- }
}
diff --git
a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/json/package-info.java
b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/json/package-info.java
index 9d9e5d60eb..22142cfdaa 100644
---
a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/json/package-info.java
+++
b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/json/package-info.java
@@ -15,10 +15,10 @@
* limitations under the license.
*/
/**
- * Classes and interfaces supporting configuration of Log4j 2 with JSON.
+ * Classes supporting configuration of Log4j with JSON.
*/
@Export
-@Version("2.20.1")
+@Version("3.0.0")
package org.apache.logging.log4j.core.config.json;
import org.osgi.annotation.bundle.Export;
diff --git
a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/yaml/YamlConfiguration.java
b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/yaml/YamlConfiguration.java
index b147eed4c5..8260d48461 100644
---
a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/yaml/YamlConfiguration.java
+++
b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/yaml/YamlConfiguration.java
@@ -17,25 +17,112 @@
package org.apache.logging.log4j.core.config.yaml;
import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
+import java.io.ByteArrayInputStream;
import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.config.AbstractConfiguration;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.ConfigurationSource;
-import org.apache.logging.log4j.core.config.json.JsonConfiguration;
+import org.apache.logging.log4j.core.config.LoggerConfig;
+import org.apache.logging.log4j.core.config.Reconfigurable;
+import org.apache.logging.log4j.core.config.status.StatusConfiguration;
+import org.apache.logging.log4j.plugins.Node;
+import org.apache.logging.log4j.plugins.model.PluginType;
-public class YamlConfiguration extends JsonConfiguration {
+/**
+ * Creates a Node hierarchy from a YAML file.
+ */
+public class YamlConfiguration extends AbstractConfiguration implements
Reconfigurable {
+
+ private final List<Status> status = new ArrayList<>();
+ private JsonNode root;
public YamlConfiguration(final LoggerContext loggerContext, final
ConfigurationSource configSource) {
super(loggerContext, configSource);
+ final byte[] buffer;
+ try {
+ try (final InputStream configStream =
configSource.getInputStream()) {
+ buffer = configStream.readAllBytes();
+ }
+ final InputStream is = new ByteArrayInputStream(buffer);
+ root = getObjectMapper().readTree(is);
+ if (root.size() == 1) {
+ for (final JsonNode node : root) {
+ root = node;
+ }
+ }
+ processAttributes(rootNode, root);
+ final StatusConfiguration statusConfig = new
StatusConfiguration().setStatus(getDefaultStatus());
+ int monitorIntervalSeconds = 0;
+ for (final Map.Entry<String, String> entry :
+ rootNode.getAttributes().entrySet()) {
+ final String key = entry.getKey();
+ final String value =
getConfigurationStrSubstitutor().replace(entry.getValue());
+ // TODO: this duplicates a lot of the XmlConfiguration
constructor
+ if ("status".equalsIgnoreCase(key)) {
+ statusConfig.setStatus(value);
+ } else if ("dest".equalsIgnoreCase(key)) {
+ statusConfig.setDestination(value);
+ } else if ("shutdownHook".equalsIgnoreCase(key)) {
+ isShutdownHookEnabled = !"disable".equalsIgnoreCase(value);
+ } else if ("shutdownTimeout".equalsIgnoreCase(key)) {
+ shutdownTimeoutMillis = Long.parseLong(value);
+ } else if ("verbose".equalsIgnoreCase(entry.getKey())) {
+ statusConfig.setVerbosity(value);
+ } else if ("packages".equalsIgnoreCase(key)) {
+ LOGGER.warn("The packages attribute is no longer
supported");
+ } else if ("name".equalsIgnoreCase(key)) {
+ setName(value);
+ } else if ("monitorInterval".equalsIgnoreCase(key)) {
+ monitorIntervalSeconds = Integer.parseInt(value);
+ } else if ("advertiser".equalsIgnoreCase(key)) {
+ createAdvertiser(value, configSource, buffer,
"application/json");
+ }
+ }
+ initializeWatchers(this, configSource, monitorIntervalSeconds);
+ statusConfig.initialize();
+ if (getName() == null) {
+ setName(configSource.getLocation());
+ }
+ } catch (final Exception ex) {
+ LOGGER.error("Error parsing " + configSource.getLocation(), ex);
+ }
}
- @Override
protected ObjectMapper getObjectMapper() {
return new ObjectMapper(new
YAMLFactory()).configure(JsonParser.Feature.ALLOW_COMMENTS, true);
}
+ @Override
+ public void setup() {
+ final Iterator<Map.Entry<String, JsonNode>> iter = root.fields();
+ final List<Node> children = rootNode.getChildren();
+ while (iter.hasNext()) {
+ final Map.Entry<String, JsonNode> entry = iter.next();
+ final JsonNode n = entry.getValue();
+ if (n.isObject()) {
+ LOGGER.debug("Processing node for object {}", entry.getKey());
+ children.add(constructNode(entry.getKey(), rootNode, n));
+ } else if (n.isArray()) {
+ LOGGER.error("Arrays are not supported at the root
configuration.");
+ }
+ }
+ LOGGER.debug("Completed parsing configuration");
+ if (status.size() > 0) {
+ for (final Status s : status) {
+ LOGGER.error("Error processing element {}: {}", s.name,
s.errorType);
+ }
+ }
+ }
+
@Override
public Configuration reconfigure() {
try {
@@ -49,4 +136,133 @@ public class YamlConfiguration extends JsonConfiguration {
}
return null;
}
+
+ private Node constructNode(final String name, final Node parent, final
JsonNode jsonNode) {
+ final PluginType<?> type = corePlugins.get(name);
+ final Node node = new Node(parent, name, type);
+ processAttributes(node, jsonNode);
+ final Iterator<Map.Entry<String, JsonNode>> iter = jsonNode.fields();
+ final List<Node> children = node.getChildren();
+ while (iter.hasNext()) {
+ final Map.Entry<String, JsonNode> entry = iter.next();
+ final JsonNode n = entry.getValue();
+ if (n.isArray() || n.isObject()) {
+ if (type == null) {
+ status.add(new Status(name, n, ErrorType.CLASS_NOT_FOUND));
+ }
+ if (n.isArray()) {
+ LOGGER.debug("Processing node for array {}",
entry.getKey());
+ for (int i = 0; i < n.size(); ++i) {
+ final String pluginType = getType(n.get(i),
entry.getKey());
+ final PluginType<?> entryType =
corePlugins.get(pluginType);
+ final Node item = new Node(node, entry.getKey(),
entryType);
+ processAttributes(item, n.get(i));
+ if (pluginType.equals(entry.getKey())) {
+ LOGGER.debug("Processing {}[{}]", entry.getKey(),
i);
+ } else {
+ LOGGER.debug("Processing {} {}[{}]", pluginType,
entry.getKey(), i);
+ }
+ final Iterator<Map.Entry<String, JsonNode>> itemIter =
+ n.get(i).fields();
+ final List<Node> itemChildren = item.getChildren();
+ while (itemIter.hasNext()) {
+ final Map.Entry<String, JsonNode> itemEntry =
itemIter.next();
+ if (itemEntry.getValue().isObject()) {
+ LOGGER.debug("Processing node for object {}",
itemEntry.getKey());
+
itemChildren.add(constructNode(itemEntry.getKey(), item, itemEntry.getValue()));
+ } else if (itemEntry.getValue().isArray()) {
+ final JsonNode array = itemEntry.getValue();
+ final String entryName = itemEntry.getKey();
+ LOGGER.debug("Processing array for object {}",
entryName);
+ for (int j = 0; j < array.size(); ++j) {
+ itemChildren.add(constructNode(entryName,
item, array.get(j)));
+ }
+ }
+ }
+ children.add(item);
+ }
+ } else {
+ LOGGER.debug("Processing node for object {}",
entry.getKey());
+ children.add(constructNode(entry.getKey(), node, n));
+ }
+ } else {
+ LOGGER.debug("Node {} is of type {}", entry.getKey(),
n.getNodeType());
+ }
+ }
+
+ final String t;
+ if (type == null) {
+ t = "null";
+ } else {
+ t = type.getElementType() + ':' + type.getPluginClass();
+ }
+
+ final String p = node.getParent() == null
+ ? "null"
+ : node.getParent().getName() == null
+ ? LoggerConfig.ROOT
+ : node.getParent().getName();
+ LOGGER.debug("Returning {} with parent {} of type {}", node.getName(),
p, t);
+ return node;
+ }
+
+ private String getType(final JsonNode node, final String name) {
+ final Iterator<Map.Entry<String, JsonNode>> iter = node.fields();
+ while (iter.hasNext()) {
+ final Map.Entry<String, JsonNode> entry = iter.next();
+ if (entry.getKey().equalsIgnoreCase("type")) {
+ final JsonNode n = entry.getValue();
+ if (n.isValueNode()) {
+ return n.asText();
+ }
+ }
+ }
+ return name;
+ }
+
+ private void processAttributes(final Node parent, final JsonNode node) {
+ final Map<String, String> attrs = parent.getAttributes();
+ final Iterator<Map.Entry<String, JsonNode>> iter = node.fields();
+ while (iter.hasNext()) {
+ final Map.Entry<String, JsonNode> entry = iter.next();
+ if (!entry.getKey().equalsIgnoreCase("type")) {
+ final JsonNode n = entry.getValue();
+ if (n.isValueNode()) {
+ attrs.put(entry.getKey(), n.asText());
+ }
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + "[location=" +
getConfigurationSource() + "]";
+ }
+
+ /**
+ * The error that occurred.
+ */
+ private enum ErrorType {
+ CLASS_NOT_FOUND
+ }
+
+ /**
+ * Status for recording errors.
+ */
+ private static class Status {
+ private final JsonNode node;
+ private final String name;
+ private final ErrorType errorType;
+
+ public Status(final String name, final JsonNode node, final ErrorType
errorType) {
+ this.name = name;
+ this.node = node;
+ this.errorType = errorType;
+ }
+
+ @Override
+ public String toString() {
+ return "Status [name=" + name + ", errorType=" + errorType + ",
node=" + node + "]";
+ }
+ }
}
diff --git
a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/yaml/package-info.java
b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/yaml/package-info.java
index c476f1d82c..09276828bf 100644
---
a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/yaml/package-info.java
+++
b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/yaml/package-info.java
@@ -15,10 +15,10 @@
* limitations under the license.
*/
/**
- * Classes and interfaces supporting configuration of Log4j 2 with YAML.
+ * Classes supporting configuration of Log4j with YAML.
*/
@Export
-@Version("2.20.1")
+@Version("3.0.0")
package org.apache.logging.log4j.core.config.yaml;
import org.osgi.annotation.bundle.Export;
diff --git a/src/changelog/.3.x.x/remove_jackson_json_config.xml
b/src/changelog/.3.x.x/remove_jackson_json_config.xml
new file mode 100644
index 0000000000..d0a25102b3
--- /dev/null
+++ b/src/changelog/.3.x.x/remove_jackson_json_config.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+<entry xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns="http://logging.apache.org/log4j/changelog"
+ xsi:schemaLocation="http://logging.apache.org/log4j/changelog
https://logging.apache.org/log4j/changelog-0.1.2.xsd"
+ type="removed">
+ <author id="mattsicker"/>
+ <description format="asciidoc">
+ Remove Jackson-based JSON configuration support. JSON configuration files
are now handled through
+ a built-in JSON parser.
+ </description>
+</entry>