diff --git a/config/dev/log4j2.xml b/config/dev/log4j2.xml index 3ed3eea003..bb2bdbeb70 100644 --- a/config/dev/log4j2.xml +++ b/config/dev/log4j2.xml @@ -1,13 +1,12 @@ - + ${sys:log-path:-logs} node-${hostName} ${log-path}/archive - ${sys:log4j2.level:-info} + ${sys:defaultLogLevel:-info} ${sys:consoleLogLevel:-error} - ${sys:fileLogLevel:-${defaultLogLevel}} @@ -78,7 +77,7 @@ - + diff --git a/node/src/integration-test/kotlin/net/corda/node/logging/ErrorCodeLoggingTests.kt b/node/src/integration-test/kotlin/net/corda/node/logging/ErrorCodeLoggingTests.kt new file mode 100644 index 0000000000..3b07bcbb49 --- /dev/null +++ b/node/src/integration-test/kotlin/net/corda/node/logging/ErrorCodeLoggingTests.kt @@ -0,0 +1,48 @@ +package net.corda.node.logging + +import net.corda.core.flows.FlowLogic +import net.corda.core.flows.InitiatingFlow +import net.corda.core.flows.StartableByRPC +import net.corda.core.internal.div +import net.corda.core.messaging.FlowHandle +import net.corda.core.messaging.startFlow +import net.corda.core.utilities.getOrThrow +import net.corda.testing.driver.DriverParameters +import net.corda.testing.driver.NodeHandle +import net.corda.testing.driver.driver +import org.assertj.core.api.Assertions.assertThat +import org.junit.Test +import java.io.File + +class ErrorCodeLoggingTests { + @Test + fun `log entries with a throwable and ERROR or WARN get an error code appended`() { + driver(DriverParameters(notarySpecs = emptyList())) { + val node = startNode(startInSameProcess = false).getOrThrow() + node.rpc.startFlow(::MyFlow).waitForCompletion() + val logFile = node.logFile() + + val linesWithErrorCode = logFile.useLines { lines -> lines.filter { line -> line.contains("[errorCode=") }.toList() } + + assertThat(linesWithErrorCode).isNotEmpty + } + } + + @StartableByRPC + @InitiatingFlow + class MyFlow : FlowLogic() { + override fun call(): String { + throw IllegalArgumentException("Mwahahahah") + } + } +} + +private fun FlowHandle<*>.waitForCompletion() { + try { + returnValue.getOrThrow() + } catch (e: Exception) { + // This is expected to throw an exception, using getOrThrow() just to wait until done. + } +} + +private fun NodeHandle.logFile(): File = (baseDirectory / "logs").toFile().walk().filter { it.name.startsWith("node-") && it.extension == "log" }.single() \ No newline at end of file diff --git a/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt b/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt index 047898c1da..628a4e2508 100644 --- a/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt +++ b/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt @@ -426,10 +426,9 @@ interface NodeStartupLogging { } fun CliWrapperBase.initLogging(baseDirectory: Path) { - val loggingLevel = loggingLevel.name.toLowerCase(Locale.ENGLISH) - System.setProperty("defaultLogLevel", loggingLevel) // These properties are referenced from the XML config file. + System.setProperty("defaultLogLevel", specifiedLogLevel) // These properties are referenced from the XML config file. if (verbose) { - System.setProperty("consoleLogLevel", loggingLevel) + System.setProperty("consoleLogLevel", specifiedLogLevel) Node.renderBasicInfoToConsole = false } System.setProperty("log-path", (baseDirectory / NodeCliCommand.LOGS_DIRECTORY_NAME).toString()) diff --git a/node/src/main/kotlin/net/corda/node/internal/subcommands/ValidateConfigurationCli.kt b/node/src/main/kotlin/net/corda/node/internal/subcommands/ValidateConfigurationCli.kt index 8c93c64cad..2cf2544fa8 100644 --- a/node/src/main/kotlin/net/corda/node/internal/subcommands/ValidateConfigurationCli.kt +++ b/node/src/main/kotlin/net/corda/node/internal/subcommands/ValidateConfigurationCli.kt @@ -17,7 +17,7 @@ import java.nio.file.Path internal class ValidateConfigurationCli : CliWrapperBase("validate-configuration", "Validate the configuration without starting the node.") { internal companion object { - private val logger = loggerFor() + private val logger by lazy { loggerFor() } internal fun logConfigurationErrors(errors: Iterable, configFile: Path) { errors.forEach { error -> @@ -56,7 +56,7 @@ internal class ValidateConfigurationCli : CliWrapperBase("validate-configuration internal fun SharedNodeCmdLineOptions.nodeConfiguration(): Valid = NodeConfigurationParser.invoke(this) private object NodeConfigurationParser : (SharedNodeCmdLineOptions) -> Valid { - private val logger = loggerFor() + private val logger by lazy { loggerFor() } private val configRenderingOptions = ConfigRenderOptions.defaults().setComments(false).setOriginComments(false).setFormatted(true) diff --git a/node/src/main/resources/log4j2.component.properties b/node/src/main/resources/log4j2.component.properties index 1b55982139..40a6560d2f 100644 --- a/node/src/main/resources/log4j2.component.properties +++ b/node/src/main/resources/log4j2.component.properties @@ -1,2 +1,2 @@ -Log4jContextSelector=net.corda.node.utilities.logging.AsyncLoggerContextSelectorNoThreadLocal +log4jContextSelector=net.corda.node.utilities.logging.AsyncLoggerContextSelectorNoThreadLocal AsyncLogger.RingBufferSize=262144 \ No newline at end of file diff --git a/tools/cliutils/src/main/kotlin/net/corda/cliutils/CordaCliWrapper.kt b/tools/cliutils/src/main/kotlin/net/corda/cliutils/CordaCliWrapper.kt index 21c8fb5507..fd7fa5fd11 100644 --- a/tools/cliutils/src/main/kotlin/net/corda/cliutils/CordaCliWrapper.kt +++ b/tools/cliutils/src/main/kotlin/net/corda/cliutils/CordaCliWrapper.kt @@ -137,10 +137,9 @@ abstract class CliWrapperBase(val alias: String, val description: String) : Call // This needs to be called before loggers (See: NodeStartup.kt:51 logger called by lazy, initLogging happens before). // Node's logging is more rich. In corda configurations two properties, defaultLoggingLevel and consoleLogLevel, are usually used. open fun initLogging() { - val loggingLevel = loggingLevel.name.toLowerCase(Locale.ENGLISH) - System.setProperty("defaultLogLevel", loggingLevel) // These properties are referenced from the XML config file. + System.setProperty("defaultLogLevel", specifiedLogLevel) // These properties are referenced from the XML config file. if (verbose) { - System.setProperty("consoleLogLevel", loggingLevel) + System.setProperty("consoleLogLevel", specifiedLogLevel) } System.setProperty("log-path", Paths.get(".").toString()) } @@ -154,6 +153,8 @@ abstract class CliWrapperBase(val alias: String, val description: String) : Call logger.info("Application Args: ${args.joinToString(" ")}") return runProgram() } + + val specifiedLogLevel: String by lazy { System.getProperty("log4j2.level")?.toLowerCase(Locale.ENGLISH) ?: loggingLevel.name.toLowerCase(Locale.ENGLISH) } } /** diff --git a/tools/cliutils/src/main/kotlin/net/corda/cliutils/CordaLog4j2ConfigFactory.kt b/tools/cliutils/src/main/kotlin/net/corda/cliutils/CordaLog4j2ConfigFactory.kt index 1dc245b26a..2f0931d9ba 100644 --- a/tools/cliutils/src/main/kotlin/net/corda/cliutils/CordaLog4j2ConfigFactory.kt +++ b/tools/cliutils/src/main/kotlin/net/corda/cliutils/CordaLog4j2ConfigFactory.kt @@ -10,9 +10,8 @@ import org.apache.logging.log4j.core.config.xml.XmlConfiguration import org.apache.logging.log4j.core.impl.LogEventFactory @Plugin(name = "CordaLog4j2ConfigFactory", category = "ConfigurationFactory") -@Order(10) +@Order(Integer.MAX_VALUE) class CordaLog4j2ConfigFactory : ConfigurationFactory() { - private companion object { private val SUPPORTED_TYPES = arrayOf(".xml", "*") } @@ -22,13 +21,13 @@ class CordaLog4j2ConfigFactory : ConfigurationFactory() { override fun getSupportedTypes() = SUPPORTED_TYPES private class ErrorCodeAppendingConfiguration(loggerContext: LoggerContext, source: ConfigurationSource) : XmlConfiguration(loggerContext, source) { - override fun doConfigure() { - super.doConfigure() loggers.values.forEach { val existingFactory = it.logEventFactory - it.logEventFactory = LogEventFactory { loggerName, marker, fqcn, level, message, properties, error -> existingFactory.createEvent(loggerName, marker, fqcn, level, message?.withErrorCodeFor(error, level), properties, error) } + it.logEventFactory = LogEventFactory { loggerName, marker, fqcn, level, message, properties, error -> + existingFactory.createEvent(loggerName, marker, fqcn, level, message?.withErrorCodeFor(error, level), properties, error) + } } } }