Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug Fix: Script Resolution fix #3394

Closed
wants to merge 12 commits into from
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* 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.appender;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.junit.jupiter.api.Assertions.assertNotNull;

import org.apache.logging.log4j.core.test.junit.LoggerContextSource;
import org.apache.logging.log4j.spi.ExtendedLogger;
import org.apache.logging.log4j.core.script.ScriptRef; // Correct import for ScriptRef
import org.apache.logging.log4j.core.config.Configuration; // Import for Configuration
import org.junit.jupiter.api.Test;

@LoggerContextSource("log4j-script-ref-test.xml")
class ScriptConfigurationTest {

@Test
void testScriptRefConfiguration(final Configuration configuration) {
// Verify that the main Scripts element is initialized
assertNotNull(configuration.getScriptManager(), "ScriptManager should not be null");

// Verify a ScriptRef element is correctly resolved
ScriptRef scriptRef = (ScriptRef) configuration.getScriptManager().getScript("ExampleScriptRef");
assertNotNull(scriptRef, "ScriptRef should not be null");
assertThat(scriptRef.getLanguage(), containsString("groovy"));
assertThat(scriptRef.getScriptText(), containsString("return \"Hello, Log4j!\";"));

// Ensure that the script executes correctly
Object result = configuration.getScriptManager().execute("ExampleScriptRef", null);
assertNotNull(result, "Script execution result should not be null");
assertThat(result.toString(), containsString("Hello, Log4j!"));

// Verify that the console appender is initialized
final ConsoleAppender consoleAppender = (ConsoleAppender) configuration.getAppender("Console");
assertNotNull(consoleAppender, "Console appender should be initialized");

// Verify that the log messages are printed to the console
ExtendedLogger logger = configuration.getLoggerContext().getLogger(ScriptConfigurationTest.class);
logger.info("Test message");

// Capture console output (depending on test framework, this might need a mock or special handling)
// Check if the expected log message is printed in the console output
assertThat(consoleAppender.getLayout().toString(), containsString("Test message"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
<Appenders>
<List name="List">
<PatternLayout pattern="[%-5level] %c{1.} %msg%n"/>

</List>
</Appenders>
<Loggers>
Expand Down
18 changes: 18 additions & 0 deletions log4j-core-test/src/test/resources/log4j-script-ref-test.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN" name="TestConfig">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} %-5level %msg%n" />
</Console>
</Appenders>
<Loggers>
<Root level="info">
<AppenderRef ref="Console" />
</Root>
</Loggers>
<Scripts>
<Script name="ExampleScriptRef" language="groovy">
return "Hello, Log4j!";
</Script>
</Scripts>
</Configuration>
Original file line number Diff line number Diff line change
Expand Up @@ -574,6 +574,7 @@ protected void preConfigure(final Node node) {
/**
* Process conditions by evaluating them and including the children of conditions that are true
* and discarding those that are not.
*
* @param node The node to evaluate.
*/
protected void processConditionals(final Node node) {
Expand Down Expand Up @@ -625,6 +626,7 @@ protected void processConditionals(final Node node) {
/**
* Handle Select nodes. This finds the first child condition that returns true and attaches its children
* to the parent of the Select Node. Other Nodes are discarded.
*
* @param selectNode The Select Node.
* @param type The PluginType of the Select Node.
* @return The list of Nodes to be added to the parent.
Expand Down Expand Up @@ -687,28 +689,35 @@ protected void doConfigure() {
configurationStrSubstitutor.setVariableResolver(interpolator);
}

for (final Node node : rootNode.getChildren()) {
if ("Scripts".equalsIgnoreCase(node.getName())) {
createConfiguration(node, null);
if (node.getObject() != null) {
for (final AbstractScript script : node.getObject(AbstractScript[].class)) {
if (script instanceof ScriptRef) {
LOGGER.error(
"Script reference to {} not added. Scripts definition cannot contain script references",
script.getName());
} else if (scriptManager != null) {
scriptManager.addScript(script);
}
}
}
break;
}
}

boolean setLoggers = false;
boolean setRoot = false;
for (final Node child : rootNode.getChildren()) {
if ("Properties".equalsIgnoreCase(child.getName())) {
// We already used this node
continue;
if ("Properties".equalsIgnoreCase(child.getName()) || "Scripts".equalsIgnoreCase(child.getName())) {
continue; // Skip already processed nodes
}
createConfiguration(child, null);
if (child.getObject() == null) {
continue;
}
if ("Scripts".equalsIgnoreCase(child.getName())) {
for (final AbstractScript script : child.getObject(AbstractScript[].class)) {
if (script instanceof ScriptRef) {
LOGGER.error(
"Script reference to {} not added. Scripts definition cannot contain script references",
script.getName());
} else if (scriptManager != null) {
scriptManager.addScript(script);
}
}
} else if ("Appenders".equalsIgnoreCase(child.getName())) {
if ("Appenders".equalsIgnoreCase(child.getName())) {
appenders = child.getObject();
} else if (child.isInstanceOf(Filter.class)) {
addFilter(child.getObject(Filter.class));
Expand Down Expand Up @@ -895,7 +904,8 @@ public ReliabilityStrategy getReliabilityStrategy(final LoggerConfig loggerConfi
* <p>
* Note: This method is not used when configuring via configuration. It is primarily used by unit tests.
* </p>
* @param logger The Logger the Appender will be associated with.
*
* @param logger The Logger the Appender will be associated with.
* @param appender The Appender.
*/
@Override
Expand Down Expand Up @@ -925,6 +935,7 @@ public synchronized void addLoggerAppender(
* <p>
* Note: This method is not used when configuring via configuration. It is primarily used by unit tests.
* </p>
*
* @param logger The Logger the Footer will be associated with.
* @param filter The Filter.
*/
Expand All @@ -950,7 +961,8 @@ public synchronized void addLoggerFilter(final org.apache.logging.log4j.core.Log
* <p>
* Note: This method is not used when configuring via configuration. It is primarily used by unit tests.
* </p>
* @param logger The Logger the Appender will be associated with.
*
* @param logger The Logger the Appender will be associated with.
* @param additive True if the LoggerConfig should be additive, false otherwise.
*/
@Override
Expand Down Expand Up @@ -1101,6 +1113,7 @@ public void createConfiguration(final Node node, final LogEvent event) {

/**
* This method is used by Arbiters to create specific children.
*
* @param type The PluginType.
* @param node The Node.
* @return The created object or null;
Expand Down Expand Up @@ -1144,8 +1157,9 @@ public Object createPluginObject(final PluginType<?> type, final Node node) {
* Although the happy path works, more work still needs to be done to log incorrect parameters. These will generally
* result in unhelpful InvocationTargetExceptions.
* </p>
* @param type the type of plugin to create.
* @param node the corresponding configuration node for this plugin to create.
*
* @param type the type of plugin to create.
* @param node the corresponding configuration node for this plugin to create.
* @param event the LogEvent that spurred the creation of this plugin
* @return the created plugin object or {@code null} if there was an error setting it up.
* @see org.apache.logging.log4j.core.config.plugins.util.PluginBuilder
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@
import org.apache.logging.log4j.status.StatusLogger;

/**
* Returns the onMatch result if the script returns True and returns the onMismatch value otherwise.
* Returns the onMatch result if the script returns True and returns the
* onMismatch value otherwise.
*/
@Plugin(name = "ScriptFilter", category = Node.CATEGORY, elementType = Filter.ELEMENT_TYPE, printObject = true)
public final class ScriptFilter extends AbstractFilter {
Expand Down Expand Up @@ -122,10 +123,12 @@ public String toString() {

/**
* Creates the ScriptFilter.
* @param script The script to run. The script must return a boolean value. Either script or scriptFile must be
* provided.
* @param match The action to take if a match occurs.
* @param mismatch The action to take if no match occurs.
*
* @param script The script to run. The script must return a boolean
* value. Either script or scriptFile must be
* provided.
* @param match The action to take if a match occurs.
* @param mismatch The action to take if no match occurs.
* @param configuration the configuration
* @return A ScriptFilter.
*/
Expand Down