Skip to content

Commit 987159e

Browse files
committed
Add springProfile SanityChecker
Signed-off-by: Dmitry Sulman <[email protected]>
1 parent 737ecd9 commit 987159e

File tree

9 files changed

+110
-23
lines changed

9 files changed

+110
-23
lines changed

logback-access-reactor-netty-spring-boot-starter/src/main/kotlin/io/github/dmitrysulman/logback/access/reactor/netty/autoconfigure/ReactorNettyAccessLogFactoryAutoConfiguration.kt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package io.github.dmitrysulman.logback.access.reactor.netty.autoconfigure
22

33
import io.github.dmitrysulman.logback.access.reactor.netty.ReactorNettyAccessLogFactory
4-
import io.github.dmitrysulman.logback.access.reactor.netty.joran.ReactorNettyJoranConfigurator
4+
import io.github.dmitrysulman.logback.access.reactor.netty.joran.LogbackAccessJoranConfigurator
55
import org.springframework.boot.autoconfigure.AutoConfiguration
66
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
77
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass
@@ -16,23 +16,23 @@ import org.springframework.util.ResourceUtils
1616
import reactor.netty.http.server.HttpServer
1717

1818
/**
19-
* [Auto-configuration][EnableAutoConfiguration] for a Logback Access Reactor Netty integration.
19+
* [Auto-configuration][EnableAutoConfiguration] for the Logback Access integration with Reactor Netty.
2020
*/
2121
@AutoConfiguration
2222
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
2323
@ConditionalOnClass(HttpServer::class)
2424
@ConditionalOnProperty(prefix = "logback.access.reactor.netty", name = ["enabled"], havingValue = "true", matchIfMissing = true)
25-
@EnableConfigurationProperties(LogbackAccessReactorNettyProperties::class)
25+
@EnableConfigurationProperties(ReactorNettyAccessLogProperties::class)
2626
class ReactorNettyAccessLogFactoryAutoConfiguration {
2727
@Bean
2828
@ConditionalOnMissingBean
2929
fun reactorNettyAccessLogFactory(
30-
properties: LogbackAccessReactorNettyProperties,
30+
properties: ReactorNettyAccessLogProperties,
3131
resourceLoader: ResourceLoader,
3232
environment: Environment,
3333
) = ReactorNettyAccessLogFactory(
3434
getConfigUrl(properties, resourceLoader),
35-
ReactorNettyJoranConfigurator(environment),
35+
LogbackAccessJoranConfigurator(environment),
3636
properties.debug ?: false,
3737
)
3838

@@ -42,7 +42,7 @@ class ReactorNettyAccessLogFactoryAutoConfiguration {
4242
ReactorNettyAccessLogWebServerFactoryCustomizer(true, reactorNettyAccessLogFactory)
4343

4444
private fun getConfigUrl(
45-
properties: LogbackAccessReactorNettyProperties,
45+
properties: ReactorNettyAccessLogProperties,
4646
resourceLoader: ResourceLoader,
4747
) = properties.config?.let { ResourceUtils.getURL(it) }
4848
?: getDefaultConfigurationResource(resourceLoader).url
Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,23 @@ package io.github.dmitrysulman.logback.access.reactor.netty.autoconfigure
22

33
import org.springframework.boot.context.properties.ConfigurationProperties
44

5+
/**
6+
* [@ConfigurationProperties][ConfigurationProperties] for the Logback Access integration with Reactor Netty.
7+
*/
58
@ConfigurationProperties("logback.access.reactor.netty")
6-
class LogbackAccessReactorNettyProperties {
9+
class ReactorNettyAccessLogProperties {
710
/**
8-
* Enable Logback Access Reactor Netty auto-configuration
11+
* Enable Logback Access Reactor Netty auto-configuration.
912
*/
1013
var enabled: Boolean? = null
1114

1215
/**
13-
* Config file name
16+
* Config file name.
1417
*/
1518
var config: String? = null
1619

1720
/**
18-
* Enable debug mode
21+
* Enable debug mode.
1922
*/
2023
var debug: Boolean? = null
2124
}

logback-access-reactor-netty-spring-boot-starter/src/main/kotlin/io/github/dmitrysulman/logback/access/reactor/netty/autoconfigure/ReactorNettyAccessLogWebServerFactoryCustomizer.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ import io.github.dmitrysulman.logback.access.reactor.netty.ReactorNettyAccessLog
44
import org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory
55
import org.springframework.boot.web.server.WebServerFactoryCustomizer
66

7+
/**
8+
* [WebServerFactoryCustomizer] of the [NettyReactiveWebServerFactory] for the Logback Access integration.
9+
*/
710
class ReactorNettyAccessLogWebServerFactoryCustomizer(
811
private val enableAccessLog: Boolean,
912
private val reactorNettyAccessLogFactory: ReactorNettyAccessLogFactory,
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,8 @@ import java.util.function.Supplier
1212
* Extended version of the Logback Access [JoranConfigurator] that adds support of `<springProfile>` tags.
1313
*
1414
* See [SpringBootJoranConfigurator](https://github.com/spring-projects/spring-boot/blob/main/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/logback/SpringBootJoranConfigurator.java).
15-
*
1615
*/
17-
class ReactorNettyJoranConfigurator(
16+
class LogbackAccessJoranConfigurator(
1817
private val environment: Environment,
1918
) : JoranConfigurator() {
2019
override fun addElementSelectorAndActionAssociations(rs: RuleStore) {
@@ -25,6 +24,7 @@ class ReactorNettyJoranConfigurator(
2524

2625
override fun sanityCheck(topModel: Model) {
2726
super.sanityCheck(topModel)
27+
performCheck(LogbackAccessSpringProfileWithinSecondPhaseElementSanityChecker(), topModel)
2828
}
2929

3030
override fun addModelHandlerAssociations(defaultProcessor: DefaultProcessor) {
@@ -38,7 +38,7 @@ class ReactorNettyJoranConfigurator(
3838
super.buildModelInterpretationContext()
3939
modelInterpretationContext.configuratorSupplier =
4040
Supplier {
41-
ReactorNettyJoranConfigurator(environment).also { it.context = this.context }
41+
LogbackAccessJoranConfigurator(environment).also { it.context = this.context }
4242
}
4343
}
4444
}

logback-access-reactor-netty-spring-boot-starter/src/main/kotlin/io/github/dmitrysulman/logback/access/reactor/netty/joran/LogbackAccessSpringProfileModelHandler.kt

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,13 @@ class LogbackAccessSpringProfileModelHandler(
2323
mic: ModelInterpretationContext,
2424
model: Model,
2525
) {
26-
if (model is LogbackAccessSpringProfileModel) {
27-
val profiles =
28-
model.name
29-
.split(",")
30-
.map { OptionHelper.substVars(it.trim(), mic, context) }
31-
if (profiles.isEmpty() || !environment.matchesProfiles(*profiles.toTypedArray())) {
32-
model.deepMarkAsSkipped()
33-
}
26+
val profiles =
27+
(model as LogbackAccessSpringProfileModel)
28+
.name
29+
.split(",")
30+
.map { OptionHelper.substVars(it.trim(), mic, context) }
31+
if (profiles.isEmpty() || !environment.matchesProfiles(*profiles.toTypedArray())) {
32+
model.deepMarkAsSkipped()
3433
}
3534
}
3635
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package io.github.dmitrysulman.logback.access.reactor.netty.joran
2+
3+
import ch.qos.logback.core.joran.sanity.SanityChecker
4+
import ch.qos.logback.core.model.AppenderModel
5+
import ch.qos.logback.core.model.Model
6+
import ch.qos.logback.core.spi.ContextAwareBase
7+
8+
/**
9+
* [SanityChecker] to ensure that `springProfile` elements are not nested
10+
* within second-phase elements.
11+
*
12+
* See [SpringProfileIfNestedWithinSecondPhaseElementSanityChecker](https://github.com/spring-projects/spring-boot/blob/main/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/logback/SpringProfileIfNestedWithinSecondPhaseElementSanityChecker.java).
13+
*/
14+
class LogbackAccessSpringProfileWithinSecondPhaseElementSanityChecker :
15+
ContextAwareBase(),
16+
SanityChecker {
17+
override fun check(model: Model?) {
18+
if (model == null) return
19+
20+
val secondsPhaseModels = mutableListOf<Model>()
21+
22+
SECOND_PHASE_TYPES.forEach {
23+
deepFindAllModelsOfType(it, secondsPhaseModels, model)
24+
}
25+
26+
deepFindNestedSubModelsOfType(LogbackAccessSpringProfileModel::class.java, secondsPhaseModels)
27+
?.takeIf { it.isNotEmpty() }
28+
?.also {
29+
addWarn("<springProfile> elements cannot be nested within an <appender> element")
30+
}?.forEach {
31+
val first = it.first
32+
val second = it.second
33+
addWarn(
34+
"Element <${first.tag}> at line ${first.lineNumber} contains a nested <${second.tag}> element at line ${second.lineNumber}",
35+
)
36+
}
37+
}
38+
39+
companion object {
40+
private val SECOND_PHASE_TYPES =
41+
listOf(
42+
AppenderModel::class.java,
43+
)
44+
}
45+
}

logback-access-reactor-netty-spring-boot-starter/src/test/kotlin/io/github/dmitrysulman/logback/access/reactor/netty/autoconfigure/LogbackAccessReactorNettyPropertiesTests.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@ import org.springframework.context.annotation.Configuration
1616
"logback.access.reactor.netty.debug=true",
1717
],
1818
)
19-
@EnableConfigurationProperties(LogbackAccessReactorNettyProperties::class)
19+
@EnableConfigurationProperties(ReactorNettyAccessLogProperties::class)
2020
@Configuration
2121
class LogbackAccessReactorNettyPropertiesTests(
22-
@Autowired private val properties: LogbackAccessReactorNettyProperties,
22+
@Autowired private val properties: ReactorNettyAccessLogProperties,
2323
) {
2424
@Test
2525
fun `smoke test`() {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package io.github.dmitrysulman.logback.access.reactor.netty.joran
2+
3+
import io.github.dmitrysulman.logback.access.reactor.netty.ReactorNettyAccessLogFactory
4+
import io.github.dmitrysulman.logback.access.reactor.netty.autoconfigure.ReactorNettyAccessLogFactoryAutoConfiguration
5+
import io.kotest.matchers.booleans.shouldBeTrue
6+
import org.junit.jupiter.api.Test
7+
import org.springframework.beans.factory.getBean
8+
import org.springframework.boot.autoconfigure.AutoConfigurations
9+
import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner
10+
11+
class LogbackAccessSpringProfileWithinSecondPhaseElementSanityCheckerTests {
12+
@Test
13+
fun `should add warning status on nested springProfile element within appender element`() {
14+
ReactiveWebApplicationContextRunner()
15+
.withConfiguration(AutoConfigurations.of(ReactorNettyAccessLogFactoryAutoConfiguration::class.java))
16+
.withPropertyValues("logback.access.reactor.netty.config=classpath:logback-access-springprofile-in-appender.xml")
17+
.run { context ->
18+
val factory = context.getBean<ReactorNettyAccessLogFactory>()
19+
factory.accessContext.statusManager.copyOfStatusList
20+
.any {
21+
it.message ==
22+
"<springProfile> elements cannot be nested within an <appender> element"
23+
}.shouldBeTrue()
24+
}
25+
}
26+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<configuration>
2+
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
3+
<springProfile name="profile">
4+
<encoder>
5+
<pattern>common</pattern>
6+
</encoder>
7+
</springProfile>
8+
</appender>
9+
10+
<appender-ref ref="STDOUT" />
11+
</configuration>

0 commit comments

Comments
 (0)