From 6daf5cfda17d0b57af55bd87e8106390e8d3439b Mon Sep 17 00:00:00 2001 From: Filip Nowak Date: Tue, 18 Oct 2022 16:03:21 +0200 Subject: [PATCH] JPERF-838: Add Log4j2SplunkForwarder SplunkForwarder decorator to handle log4j2 config files. --- .gitignore | 2 +- CHANGELOG.md | 3 ++ .../tools/infrastructure/api/Sed.kt | 26 +++++++++- .../infrastructure/api/jira/JiraNodeConfig.kt | 5 +- .../api/splunk/AtlassianSplunkForwarder.kt | 11 ++--- .../api/splunk/DisabledSplunkForwarder.kt | 2 +- .../api/splunk/Log4j2SplunkForwarder.kt | 16 +++++++ .../api/splunk/SplunkForwarder.kt | 2 +- .../api/splunk/UniversalSplunkForwarder.kt | 9 +--- .../infrastructure/splunk/Log4jJsonifier.kt | 48 +++++++++++++++++++ 10 files changed, 103 insertions(+), 21 deletions(-) create mode 100644 src/main/kotlin/com/atlassian/performance/tools/infrastructure/api/splunk/Log4j2SplunkForwarder.kt create mode 100644 src/main/kotlin/com/atlassian/performance/tools/infrastructure/splunk/Log4jJsonifier.kt diff --git a/.gitignore b/.gitignore index 3060674f..d49a8b53 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ .gradle build -.idea \ No newline at end of file +.idea diff --git a/CHANGELOG.md b/CHANGELOG.md index e67754ce..e3095bee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,9 @@ Dropping a requirement of a major version of a dependency is a new contract. ## [Unreleased] [Unreleased]: https://github.com/atlassian/infrastructure/compare/release-4.19.3...master +### Added +- Enable Jsonifying log4j2 configurations in `SplunkForwarder`. Resolve [JPERF-838]. + ## [4.19.3] - 2022-06-23 [4.19.3]: https://github.com/atlassian/infrastructure/compare/release-4.19.2...release-4.19.3 diff --git a/src/main/kotlin/com/atlassian/performance/tools/infrastructure/api/Sed.kt b/src/main/kotlin/com/atlassian/performance/tools/infrastructure/api/Sed.kt index 8627472d..c819899a 100644 --- a/src/main/kotlin/com/atlassian/performance/tools/infrastructure/api/Sed.kt +++ b/src/main/kotlin/com/atlassian/performance/tools/infrastructure/api/Sed.kt @@ -13,4 +13,28 @@ class Sed { val escapedOutput = output.replace("/", "\\/") connection.execute("sed -i -r 's/$escapedExpression/$escapedOutput/g' $file") } -} \ No newline at end of file + + internal fun safeReplace( + connection: SshConnection, + expression: String, + output: String, + file: String + ): SshConnection.SshResult { + val escapedExpression = expression.replace("/", "\\/") + val escapedOutput = output.replace("/", "\\/") + return connection.safeExecute("sed -i -r 's/$escapedExpression/$escapedOutput/g' $file") + } + + internal fun safeReplaceXmlTag( + connection: SshConnection, + sourceTagName: String, + replacementString: String, + file: String + ): SshConnection.SshResult { + val escapedSource= sourceTagName.replace("/", "\\/") + val escapedReplacement= replacementString.replace("/", "\\/") + return connection.safeExecute("sed -i '/\\/$escapedSource/a\\\n" + + " $escapedReplacement\n" + + "/$escapedSource/, /\\/$escapedSource/d' $file") + } +} diff --git a/src/main/kotlin/com/atlassian/performance/tools/infrastructure/api/jira/JiraNodeConfig.kt b/src/main/kotlin/com/atlassian/performance/tools/infrastructure/api/jira/JiraNodeConfig.kt index a759d080..9d813489 100644 --- a/src/main/kotlin/com/atlassian/performance/tools/infrastructure/api/jira/JiraNodeConfig.kt +++ b/src/main/kotlin/com/atlassian/performance/tools/infrastructure/api/jira/JiraNodeConfig.kt @@ -9,6 +9,7 @@ import com.atlassian.performance.tools.infrastructure.api.jvm.jmx.DisabledRemote import com.atlassian.performance.tools.infrastructure.api.jvm.jmx.RemoteJmx import com.atlassian.performance.tools.infrastructure.api.profiler.Profiler import com.atlassian.performance.tools.infrastructure.api.splunk.DisabledSplunkForwarder +import com.atlassian.performance.tools.infrastructure.api.splunk.Log4j2SplunkForwarder import com.atlassian.performance.tools.infrastructure.api.splunk.SplunkForwarder import com.atlassian.performance.tools.infrastructure.profiler.DisabledProfiler import java.net.URI @@ -143,7 +144,7 @@ class JiraNodeConfig private constructor( debug = debug, remoteJmx = remoteJmx, jvmArgs = jvmArgs, - splunkForwarder = splunkForwarder, + splunkForwarder = Log4j2SplunkForwarder("log4j2.xml", splunkForwarder), collectdConfigs = collectdConfigs, launchTimeouts = launchTimeouts, jdk = if (versionedJdk != null) versionedJdk as JavaDevelopmentKit else jdk, @@ -157,4 +158,4 @@ class JiraNodeConfig private constructor( JiraNodeConfig::class.java.getResource("/collectd/conf/jira-default.conf").toURI() ) } -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/atlassian/performance/tools/infrastructure/api/splunk/AtlassianSplunkForwarder.kt b/src/main/kotlin/com/atlassian/performance/tools/infrastructure/api/splunk/AtlassianSplunkForwarder.kt index 60b3425a..8b945f92 100644 --- a/src/main/kotlin/com/atlassian/performance/tools/infrastructure/api/splunk/AtlassianSplunkForwarder.kt +++ b/src/main/kotlin/com/atlassian/performance/tools/infrastructure/api/splunk/AtlassianSplunkForwarder.kt @@ -1,7 +1,7 @@ package com.atlassian.performance.tools.infrastructure.api.splunk import com.atlassian.performance.tools.infrastructure.DockerImage -import com.atlassian.performance.tools.infrastructure.api.Sed +import com.atlassian.performance.tools.infrastructure.splunk.Log4jJsonifier import com.atlassian.performance.tools.ssh.api.SshConnection import java.time.Duration @@ -34,12 +34,7 @@ class AtlassianSplunkForwarder( } override fun jsonifyLog4j(sshConnection: SshConnection, log4jPropertiesPath: String) { - Sed().replace( - connection = sshConnection, - expression = "NewLineIndentingFilteringPatternLayout", - output = "layout.JsonLayout", - file = log4jPropertiesPath - ) + Log4jJsonifier().jsonifyLog4j1(sshConnection, log4jPropertiesPath) } override fun getRequiredPorts(): List { @@ -92,4 +87,4 @@ internal class LogStashConfigBuilder(private val additionalEventFields: Map { return emptyList() } -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/atlassian/performance/tools/infrastructure/api/splunk/Log4j2SplunkForwarder.kt b/src/main/kotlin/com/atlassian/performance/tools/infrastructure/api/splunk/Log4j2SplunkForwarder.kt new file mode 100644 index 00000000..8ef712f1 --- /dev/null +++ b/src/main/kotlin/com/atlassian/performance/tools/infrastructure/api/splunk/Log4j2SplunkForwarder.kt @@ -0,0 +1,16 @@ +package com.atlassian.performance.tools.infrastructure.api.splunk + +import com.atlassian.performance.tools.infrastructure.splunk.Log4jJsonifier +import com.atlassian.performance.tools.ssh.api.SshConnection +import java.nio.file.Paths + +class Log4j2SplunkForwarder(log4j2ConfigFileName: String, splunkForwarder: SplunkForwarder) : + SplunkForwarder by splunkForwarder { + + private val log4j2ConfigFileName = log4j2ConfigFileName + + override fun jsonifyLog4j(sshConnection: SshConnection, log4jPropertiesPath: String) { + val log4j2ConfigPath = Paths.get(log4jPropertiesPath).resolveSibling(log4j2ConfigFileName).toString() + Log4jJsonifier().jsonifyLog4j1AndLog4j2(sshConnection, log4jPropertiesPath, log4j2ConfigPath) + } +} diff --git a/src/main/kotlin/com/atlassian/performance/tools/infrastructure/api/splunk/SplunkForwarder.kt b/src/main/kotlin/com/atlassian/performance/tools/infrastructure/api/splunk/SplunkForwarder.kt index 22a8bfb2..3aa8a87c 100644 --- a/src/main/kotlin/com/atlassian/performance/tools/infrastructure/api/splunk/SplunkForwarder.kt +++ b/src/main/kotlin/com/atlassian/performance/tools/infrastructure/api/splunk/SplunkForwarder.kt @@ -6,4 +6,4 @@ interface SplunkForwarder { fun run(sshConnection: SshConnection, name: String, logsPath: String) fun jsonifyLog4j(sshConnection: SshConnection, log4jPropertiesPath: String) fun getRequiredPorts(): List -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/atlassian/performance/tools/infrastructure/api/splunk/UniversalSplunkForwarder.kt b/src/main/kotlin/com/atlassian/performance/tools/infrastructure/api/splunk/UniversalSplunkForwarder.kt index b2d35130..e2c79709 100644 --- a/src/main/kotlin/com/atlassian/performance/tools/infrastructure/api/splunk/UniversalSplunkForwarder.kt +++ b/src/main/kotlin/com/atlassian/performance/tools/infrastructure/api/splunk/UniversalSplunkForwarder.kt @@ -1,7 +1,7 @@ package com.atlassian.performance.tools.infrastructure.api.splunk import com.atlassian.performance.tools.infrastructure.DockerImage -import com.atlassian.performance.tools.infrastructure.api.Sed +import com.atlassian.performance.tools.infrastructure.splunk.Log4jJsonifier import com.atlassian.performance.tools.ssh.api.SshConnection @@ -33,12 +33,7 @@ class UniversalSplunkForwarder( } override fun jsonifyLog4j(sshConnection: SshConnection, log4jPropertiesPath: String) { - Sed().replace( - connection = sshConnection, - expression = "NewLineIndentingFilteringPatternLayout", - output = "layout.JsonLayout", - file = log4jPropertiesPath - ) + Log4jJsonifier().jsonifyLog4j1(sshConnection, log4jPropertiesPath) } override fun getRequiredPorts(): List { diff --git a/src/main/kotlin/com/atlassian/performance/tools/infrastructure/splunk/Log4jJsonifier.kt b/src/main/kotlin/com/atlassian/performance/tools/infrastructure/splunk/Log4jJsonifier.kt new file mode 100644 index 00000000..1e487f63 --- /dev/null +++ b/src/main/kotlin/com/atlassian/performance/tools/infrastructure/splunk/Log4jJsonifier.kt @@ -0,0 +1,48 @@ +package com.atlassian.performance.tools.infrastructure.splunk + +import com.atlassian.performance.tools.infrastructure.api.Sed +import com.atlassian.performance.tools.ssh.api.SshConnection +import org.apache.logging.log4j.LogManager +import org.apache.logging.log4j.Logger + + +class Log4jJsonifier { + + private val logger: Logger = LogManager.getLogger(Log4jJsonifier::class.java) + + fun jsonifyLog4j1(sshConnection: SshConnection, log4jPropertiesPath: String) { + Sed().replace( + connection = sshConnection, + expression = "NewLineIndentingFilteringPatternLayout", + output = "layout.JsonLayout", + file = log4jPropertiesPath + ) + } + + fun jsonifyLog4j1AndLog4j2(sshConnection: SshConnection, log4jPropertiesPath: String, log4j2ConfigPath: String) { + val log4j1Result = Sed().safeReplace( + connection = sshConnection, + expression = "NewLineIndentingFilteringPatternLayout", + output = "layout.JsonLayout", + file = log4jPropertiesPath + ) + if (!log4j1Result.isSuccessful()) { + logger.debug("Attempt to jsonify $log4jPropertiesPath was unsuccessful.") + } + val log4j2Result = Sed().safeReplaceXmlTag( + connection = sshConnection, + sourceTagName = "PatternLayout", + replacementString = "", + file = log4j2ConfigPath + ) + if (!log4j1Result.isSuccessful()) { + logger.debug("Attempt to jsonify $log4j2ConfigPath was unsuccessful.") + } + if (!log4j1Result.isSuccessful() && !log4j2Result.isSuccessful()) { + throw Exception("Failed to jsonify any log4j configuration.") + } + if (log4j1Result.isSuccessful() && log4j2Result.isSuccessful()) { + logger.debug("Migrated both log4j1 and log4j2 config files, ensure that's correct.") + } + } +}