Logical AND/OR in conditional processing of configuration files #991
Replies: 10 comments 1 reply
|
Hi @yanivnahoum Thank you for the question. I am afraid composition of boolean operation is not supported. At this time, you would need to write a condition class yourself. If you need any help with how to do that, just let me know. I'd be happy to guide you. |
|
Thanks @ceki! I would love some help - I tried to get Joran to instantiate my |
|
@yanivnahoum Can you post a sample XML of what you have in mind? In any case, to populate a list of some property, say foo, you would need a public setter method called addFoo(SomeType foo) in your java component and in XML you would add foo properties with repeated elements. SomeType can be a simple property type such as String, Integer etc, or a POJO type with properties of its own, or an Enum or a type having a static method called "valueOf(String)" For example, appenders take one or more filters: Joran (logback's configuration system) will automatically bind the various filters to the appender as appenders extend the However, in certain cases, depending on your use case, Joran's binding mechanism may not suffice and you would need to extend Joran itself which is a bit more involved (but feasible). Anyway, do post an example of what you would like to write in XML. |
|
I was thinking of something like in the original question above, or even simpler to begin with (AND only): <condition class="com.a.b.CompositeCondition">
<condition class="ch.qos.logback.core.boolex.IsPropertyDefinedCondition">
<key>KUBERNETES_SERVICE_HOST</key>
</condition>
<condition class="ch.qos.logback.core.boolex.IsPropertyNullCondition">
<key>GITHUB_ACTIONS</key>
</condition>
</condition>And public class CompositeCondition extends PropertyConditionBase {
private final List<PropertyCondition> conditions = new ArrayList<>();
public void addCondition(PropertyCondition condition) {
conditions.add(condition);
}
@Override
public boolean evaluate() {
boolean result = true;
for (PropertyCondition condition : conditions) {
result = result && condition.evaluate();
}
return result;
}
}I'm attempting to apply it in my appender that chooses its encoder based on the condition (this works with a simple PropertyCondition): <include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
<condition class="com.a.b.CompositeCondition">
<condition class="ch.qos.logback.core.boolex.IsPropertyDefinedCondition">
<key>KUBERNETES_SERVICE_HOST</key>
</condition>
<condition class="ch.qos.logback.core.boolex.IsPropertyNullCondition">
<key>GITHUB_ACTIONS</key>
</condition>
</condition>
<if>
<then>
<encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
<!-- omitted for brevity -->
</encoder>
</then>
<else>
<encoder>
<pattern>%clr(%d){faint} %clr(%-5level) %clr(${appName:-app}){yellow} %clr(%thread){magenta} %clr(%logger{30}){cyan} %msg %clr(%mdc){green,bold} %clr(%kvp){blue}%n</pattern>
</encoder>
</else>
</if>
</appender>This causes logback to load the the appender incorrectly: |
|
@yanivnahoum Joran (logback's configuration system) reads the XML and invokes "actions". Actions transform XML elements into For elements unknown beforehand, there is a catch all Coming back to your example, the To make use of the implicit injection mechanism, you would need to rename the inner More below. |
|
Assuming CompositeCondition only is used for "AND" composition, here is an implementation: public class CompositeCondition extends PropertyConditionBase {
private final List<String> propertyDefinedKeys = new ArrayList<>();
private final List<String> propertyIsNullKeys = new ArrayList<>();
public void addPropertyDefined(String key) {
propertyDefinedKeys.add(key);
}
public void addPropertyIsNull(String key) {
propertyIsNullKeys.add(key);
}
@Override
public boolean evaluate() {
for(String key: propertyDefinedKeys) {
if(!isDefined(key))
return false;
}
for(String key: propertyIsNullKeys) {
if(!isNull(key))
return false;
}
return true;
}
}Here is the corresponding XML snippet: <include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
<condition class="com.a.b.CompositeCondition">
<propertyDefined>KUBERNETES_SERVICE_HOST</propertyDefined>
<propertyIsNull>GITHUB_ACTIONS</propertyIsNull>
</condition>
<if>
<then>
<encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
<!-- omitted for brevity -->
</encoder>
</then>
<else>
<encoder>
<pattern>%clr(%d){faint} %clr(%-5level) %clr(${appName:-app}){yellow} %clr(%thread){magenta} %clr(%logger{30}){cyan} %msg %clr(%mdc){green,bold} %clr(%kvp){blue}%n</pattern>
</encoder>
</else>
</if>
</appender>I have not tested the above, so let me know if you run into problems. In order to support more general NOT, AND OR logic, you could use a linked list (which preserves order) and traverse the linked list to evaluate the result. |
|
I think the best way to provide the desired functionality would be to write a logical expression parser. |
|
Thanks ceki - this works!
Isn't that what we had before logback 1.5.20 where we used Janino for that? <if condition='isDefined("KUBERNETES_SERVICE_HOST") && isNull("GITHUB_ACTIONS")'> |
|
@yanivnahoum Good question. Janino is an embedded Java compiler that supports the full power of the Java language. This comes with certain security considerations. In contrast, a simple logical expression parser that only supports basic operators like !, &&, and ||, along with a handful of predefined functions, introduces no security risks. See commit 859f5a1 and |
|
ExpressionPropertyCondition is part of logback version 1.5.24 released earlier today. |
Uh oh!
There was an error while loading. Please reload this page.
Is there a simple way to evaluate multiple PropertyConditions using a logical AND or OR?
Example: "property x is defined and property y is null"
Pseudo code:
All reactions