Skip to content

Commit 5a6b8cf

Browse files
committed
TS-44797 Added more debug logs in impacted test engine
1 parent f4c8b54 commit 5a6b8cf

File tree

19 files changed

+143
-30
lines changed

19 files changed

+143
-30
lines changed

.github/pull_request_template.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Addresses issue [TS-XXXXX](https://cqse.atlassian.net/browse/TS-XXXXX)
22

33
- [ ] Changes are tested adequately
4-
- [ ] Agent's README.md updated in case of user-visible changes
4+
- [ ] Teamscale documentation updated in case of user-visible changes
55
- [ ] CHANGELOG.md updated
66
- [ ] Present new features in [N&N](https://cqse.atlassian.net/l/cp/KHSr81m1)
77
- [ ] [TGA Tutorial](https://docs.teamscale.com/tutorial/setting-up-tga-java) updated

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ We use [semantic versioning](http://semver.org/):
55
- PATCH version when you make backwards compatible bug fixes.
66

77
# Next version
8-
98
- _agent_: improved logging when multiple agents are attached
9+
- [feature] _teamscale-gradle-plugin_: Added `debugLogging` option to enable debug logging for the java profiler and the impacted test engine
10+
- [feature] _teamscale-maven-plugin_: Enhanced `debugLogging` option to also enable debug logging for the impacted test engine
1011

1112
# 36.1.0
1213
- [feature] _impacted-test-engine_: Added support for JUnit 5.13 and 6.0

agent/src/main/java/com/teamscale/jacoco/agent/testimpact/TestwiseCoverageResource.java

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
package com.teamscale.jacoco.agent.testimpact;
22

3-
import java.io.IOException;
4-
import java.util.List;
3+
import com.teamscale.client.ClusteredTestDetails;
4+
import com.teamscale.client.PrioritizableTestCluster;
5+
import com.teamscale.jacoco.agent.JacocoRuntimeController;
6+
import com.teamscale.jacoco.agent.ResourceBase;
7+
import com.teamscale.report.testwise.jacoco.cache.CoverageGenerationException;
8+
import com.teamscale.report.testwise.model.TestExecution;
9+
import com.teamscale.report.testwise.model.TestInfo;
510

611
import javax.ws.rs.DefaultValue;
712
import javax.ws.rs.GET;
@@ -12,14 +17,8 @@
1217
import javax.ws.rs.QueryParam;
1318
import javax.ws.rs.core.MediaType;
1419
import javax.ws.rs.core.Response;
15-
16-
import com.teamscale.client.ClusteredTestDetails;
17-
import com.teamscale.client.PrioritizableTestCluster;
18-
import com.teamscale.jacoco.agent.JacocoRuntimeController;
19-
import com.teamscale.jacoco.agent.ResourceBase;
20-
import com.teamscale.report.testwise.jacoco.cache.CoverageGenerationException;
21-
import com.teamscale.report.testwise.model.TestExecution;
22-
import com.teamscale.report.testwise.model.TestInfo;
20+
import java.io.IOException;
21+
import java.util.List;
2322

2423
/**
2524
* The resource of the Jersey + Jetty http server holding all the endpoints specific for the
@@ -57,7 +56,7 @@ public Response handleTestStart(@PathParam(TEST_ID_PARAMETER) String testId) {
5756
handleBadRequest("Test name is missing!");
5857
}
5958

60-
logger.debug("Start test " + testId);
59+
logger.debug("Start test {}", testId);
6160

6261
testwiseCoverageAgent.testEventHandler.testStart(testId);
6362
return Response.noContent().build();
@@ -68,12 +67,12 @@ public Response handleTestStart(@PathParam(TEST_ID_PARAMETER) String testId) {
6867
@Produces(MediaType.APPLICATION_JSON)
6968
@Path("/test/end/{" + TEST_ID_PARAMETER + "}")
7069
public TestInfo handleTestEnd(@PathParam(TEST_ID_PARAMETER) String testId,
71-
TestExecution testExecution) throws JacocoRuntimeController.DumpException, CoverageGenerationException {
70+
TestExecution testExecution) throws JacocoRuntimeController.DumpException, CoverageGenerationException {
7271
if (testId == null || testId.isEmpty()) {
7372
handleBadRequest("Test name is missing!");
7473
}
7574

76-
logger.debug("End test " + testId);
75+
logger.debug("End test {}", testId);
7776

7877
return testwiseCoverageAgent.testEventHandler.testEnd(testId,
7978
testExecution);

impacted-test-engine/src/main/kotlin/com/teamscale/test_impacted/commons/LoggerUtils.kt

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,42 @@ object LoggerUtils {
2424
override fun format(lr: LogRecord) =
2525
String.format("[%1\$s] %2\$s%n", lr.level.localizedName, lr.message)
2626
}
27+
handler.level = Level.WARNING
2728
MAIN_LOGGER.addHandler(handler)
2829
}
2930

31+
/**
32+
* Configures file-based logging for the impacted test engine with the specified log level.
33+
*
34+
* @param logLevel The minimum log level that will be written to the log file.
35+
* @param logFilePath The filesystem path where the log file will be written. If null, no file logging is configured.
36+
*/
37+
fun configureFileLogging(logLevel: Level, logFilePath: String?) {
38+
if (logFilePath == null) return
39+
try {
40+
val fileHandler = FileHandler(logFilePath, true)
41+
fileHandler.level = logLevel
42+
fileHandler.formatter = object : SimpleFormatter() {
43+
@Synchronized
44+
override fun format(lr: LogRecord) =
45+
String.format(
46+
"%1\$tF %1\$tT [%2\$s] %3\$s: %4\$s%n",
47+
lr.millis,
48+
lr.level.localizedName,
49+
lr.loggerName,
50+
lr.message
51+
)
52+
}
53+
MAIN_LOGGER.addHandler(fileHandler)
54+
MAIN_LOGGER.level = logLevel
55+
} catch (e: IOException) {
56+
val logger = createLogger()
57+
logger.warning(
58+
"Cannot create log file at $logFilePath specified via teamscale.test.impacted.logFilePath: ${e.message}"
59+
)
60+
}
61+
}
62+
3063
/**
3164
* Normally, the java util logging framework picks up the config file specified via the system property
3265
* {@value #JAVA_UTIL_LOGGING_CONFIG_FILE_SYSTEM_PROPERTY}. For some reason, this does not work here, so we need to

impacted-test-engine/src/main/kotlin/com/teamscale/test_impacted/engine/ImpactedTestEngine.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.teamscale.test_impacted.engine
22

3+
import com.teamscale.test_impacted.commons.LoggerUtils
34
import com.teamscale.test_impacted.commons.LoggerUtils.createLogger
45
import com.teamscale.test_impacted.engine.options.TestEngineOptionUtils
56
import org.junit.platform.engine.*
@@ -18,11 +19,13 @@ class ImpactedTestEngine : TestEngine {
1819
): TestDescriptor {
1920
val engineOptions = TestEngineOptionUtils
2021
.getEngineOptions(discoveryRequest.configurationParameters)
22+
LoggerUtils.configureFileLogging(engineOptions.logLevel, engineOptions.logFilePath)
2123
if (!engineOptions.enabled) {
24+
LOG.fine { "Impacted test engine is disabled." }
2225
return EngineDescriptor(uniqueId, ENGINE_NAME)
2326
}
2427
val configuration = engineOptions.testEngineConfiguration
25-
val engine = InternalImpactedTestEngine(configuration, engineOptions.partition)
28+
val engine = InternalImpactedTestEngine(configuration)
2629

2730
// Re-initialize the configuration for this discovery (and optional following execution).
2831
internalImpactedTestEngine = engine

impacted-test-engine/src/main/kotlin/com/teamscale/test_impacted/engine/InternalImpactedTestEngine.kt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,9 @@ import org.junit.platform.engine.support.descriptor.EngineDescriptor
2121
* @constructor Initializes the test engine with the given configuration and partition.
2222
* @param configuration The configuration object that provides dependencies such as test engine registry,
2323
* test sorter, and test data writer.
24-
* @param partition The partition identifier used to divide tests and manage their execution.
2524
*/
2625
internal class InternalImpactedTestEngine(
2726
configuration: ImpactedTestEngineConfiguration,
28-
private val partition: String?
2927
) {
3028
private val testEngineRegistry = configuration.testEngineRegistry
3129
private val testSorter = configuration.testSorter

impacted-test-engine/src/main/kotlin/com/teamscale/test_impacted/engine/TestEngineRegistry.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.teamscale.test_impacted.engine
22

3+
import com.teamscale.test_impacted.commons.LoggerUtils.createLogger
34
import org.junit.platform.commons.util.ClassLoaderUtils
45
import org.junit.platform.engine.TestEngine
56
import java.util.*
@@ -17,18 +18,26 @@ open class TestEngineRegistry(
1718
excludedTestEngineIds: Set<String>
1819
) : Iterable<TestEngine> {
1920
private val testEnginesById: Map<String, TestEngine>
21+
private val logger = createLogger()
2022

2123
init {
2224
var otherTestEngines = loadOtherTestEngines(excludedTestEngineIds)
2325

26+
logger.fine("Loaded ${otherTestEngines.size} test engines after excluding: $excludedTestEngineIds")
27+
logger.fine("Found test engines: ${otherTestEngines.map { it.id }}")
28+
2429
// If there are no test engines set we don't need to filter but simply use all other test engines.
2530
if (includedTestEngineIds.isNotEmpty()) {
31+
logger.fine("Filtering by included test engine IDs: $includedTestEngineIds")
32+
val beforeFilterCount = otherTestEngines.size
2633
otherTestEngines = otherTestEngines.filter { testEngine ->
2734
includedTestEngineIds.contains(testEngine.id)
2835
}
36+
logger.fine("Filtered from $beforeFilterCount to ${otherTestEngines.size} test engines")
2937
}
3038

3139
testEnginesById = otherTestEngines.associateBy { it.id }
40+
logger.fine("Final test engines to be used: ${testEnginesById.keys}")
3241
}
3342

3443
/**

impacted-test-engine/src/main/kotlin/com/teamscale/test_impacted/engine/executor/TeamscaleAgentNotifier.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,12 @@ open class TeamscaleAgentNotifier(
2424

2525
/** Reports the start of a test to the Teamscale JaCoCo agent. */
2626
open fun startTest(testUniformPath: String) {
27+
LOG.fine { "Starting test notification for: $testUniformPath" }
2728
try {
2829
testwiseCoverageAgentApis.forEach { apiService ->
2930
apiService.testStarted(testUniformPath.encodeUrl()).execute()
3031
}
32+
LOG.fine { "Successfully notified test start for: $testUniformPath" }
3133
} catch (e: IOException) {
3234
LOG.log(
3335
Level.SEVERE, e
@@ -37,6 +39,7 @@ open class TeamscaleAgentNotifier(
3739

3840
/** Reports the end of a test to the Teamscale JaCoCo agent. */
3941
open fun endTest(testUniformPath: String, testExecution: TestExecution?) {
42+
LOG.fine { "Ending test notification for: $testUniformPath (execution: ${testExecution != null})" }
4043
try {
4144
testwiseCoverageAgentApis.forEach { apiService ->
4245
val url = testUniformPath.encodeUrl()
@@ -46,6 +49,7 @@ open class TeamscaleAgentNotifier(
4649
apiService.testFinished(url, testExecution).execute()
4750
}
4851
}
52+
LOG.fine { "Successfully notified test end for: $testUniformPath" }
4953
} catch (e: IOException) {
5054
LOG.log(
5155
Level.SEVERE, e
@@ -55,10 +59,12 @@ open class TeamscaleAgentNotifier(
5559

5660
/** Reports the end of the test run to the Teamscale JaCoCo agent. */
5761
open fun testRunEnded() {
62+
LOG.fine { "Notifying test run ended (partial: $partial)" }
5863
try {
5964
testwiseCoverageAgentApis.forEach { apiService ->
6065
apiService.testRunFinished(partial).execute()
6166
}
67+
LOG.fine { "Successfully notified test run ended" }
6268
} catch (e: IOException) {
6369
LOG.log(
6470
Level.SEVERE, e

impacted-test-engine/src/main/kotlin/com/teamscale/test_impacted/engine/options/TestEngineOptionUtils.kt

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package com.teamscale.test_impacted.engine.options
33
import com.teamscale.client.CommitDescriptor
44
import org.junit.platform.engine.ConfigurationParameters
55
import java.util.*
6+
import java.util.logging.Level
67

78
/**
89
* Utility object for creating and retrieving `TestEngineOptions` based on
@@ -40,10 +41,22 @@ object TestEngineOptionUtils {
4041
testCoverageAgentUrls = propertyReader.getStringList("agentsUrls"),
4142
includedTestEngineIds = propertyReader.getStringList("includedEngines").toSet(),
4243
excludedTestEngineIds = propertyReader.getStringList("excludedEngines").toSet(),
43-
reportDirectoryPath = propertyReader.getString("reportDirectory")
44+
reportDirectoryPath = propertyReader.getString("reportDirectory"),
45+
logLevel = determineLogLevel(propertyReader.getString("logLevel")),
46+
logFilePath = propertyReader.getString("logFilePath"),
4447
)
4548
}
4649

50+
private fun determineLogLevel(logLevelString: String?): Level {
51+
return when (logLevelString?.uppercase()) {
52+
"DEBUG" -> Level.FINE
53+
"INFO" -> Level.INFO
54+
"WARN" -> Level.WARNING
55+
"ERROR" -> Level.SEVERE
56+
else -> Level.INFO
57+
}
58+
}
59+
4760
private fun PrefixingPropertyReader.createServerOptions() =
4861
ServerOptions(
4962
getString("server.url") ?: throw AssertionError("server url is required"),

impacted-test-engine/src/main/kotlin/com/teamscale/test_impacted/engine/options/TestEngineOptions.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import com.teamscale.test_impacted.engine.executor.TeamscaleAgentNotifier
1212
import com.teamscale.tia.client.ITestwiseCoverageAgentApi
1313
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
1414
import java.io.File
15+
import java.util.logging.Level
1516
import kotlin.io.path.createDirectories
1617

1718
/**
@@ -37,6 +38,9 @@ import kotlin.io.path.createDirectories
3738
* @property excludedTestEngineIds A set of test engine IDs to explicitly exclude from the test run.
3839
* @param reportDirectoryPath The filesystem path where test reports will be saved. Must be writable during initialization.
3940
* @param testCoverageAgentUrls A list of URLs pointing to test-wise coverage agents used during test execution.
41+
* @property logLevel The log level for the impacted test engine. Defaults to INFO.
42+
* @property logFilePath The filesystem path where the impacted test engine log file will be written.
43+
* If null, no file logging is configured.
4044
*/
4145
class TestEngineOptions(
4246
val enabled: Boolean,
@@ -54,7 +58,9 @@ class TestEngineOptions(
5458
private val includedTestEngineIds: Set<String> = emptySet(),
5559
private val excludedTestEngineIds: Set<String> = emptySet(),
5660
reportDirectoryPath: String? = null,
57-
testCoverageAgentUrls: List<String> = emptyList()
61+
testCoverageAgentUrls: List<String> = emptyList(),
62+
val logLevel: Level = Level.INFO,
63+
val logFilePath: String? = null,
5864
) {
5965

6066
private var reportDirectory = reportDirectoryPath?.let { File(it) }

0 commit comments

Comments
 (0)