Skip to content

Commit 406fbbc

Browse files
committed
use SLF4J with Logback for logging
Signed-off-by: Dejan Zele Pejchev <pejcev.dejan@gmail.com>
1 parent 8bf2af6 commit 406fbbc

9 files changed

Lines changed: 110 additions & 80 deletions

File tree

‎pom.xml‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
<kubernetes-client.version>6.7.2</kubernetes-client.version>
4343
<jackson.version>2.15.2</jackson.version>
4444
<scalatest-maven-plugin.version>2.2.0</scalatest-maven-plugin.version>
45+
<slf4j.version>2.0.7</slf4j.version>
4546
<!-- SPARK-36796 for JDK-17 test-->
4647
<extraJavaTestArgs>
4748
-XX:+IgnoreUnrecognizedVMOptions
@@ -190,6 +191,11 @@
190191
<artifactId>jackson-module-scala_${scala.binary.version}</artifactId>
191192
<version>${jackson.version}</version>
192193
</dependency>
194+
<dependency>
195+
<groupId>org.slf4j</groupId>
196+
<artifactId>slf4j-api</artifactId>
197+
<version>${slf4j.version}</version>
198+
</dependency>
193199
</dependencies>
194200

195201
<profiles>
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
rootLogger.level = info
2+
rootLogger.appenderRef.console.ref = console
3+
4+
appender.console.type = Console
5+
appender.console.name = console
6+
appender.console.layout.type = PatternLayout
7+
appender.console.layout.pattern = %d{HH:mm:ss.SSS} %-5level %logger{20} - %msg%n

‎src/main/scala/org/apache/spark/deploy/armada/submit/ArmadaClientApplication.scala‎

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ import org.apache.spark.deploy.k8s.Config.{CONTAINER_IMAGE => KUBERNETES_CONTAIN
7676
import org.apache.spark.resource.ResourceProfile
7777
import org.apache.spark.scheduler.cluster.SchedulerBackendUtils
7878
import org.apache.spark.scheduler.cluster.k8s.KubernetesExecutorBuilder
79+
import org.slf4j.{Logger, LoggerFactory}
7980

8081
import scala.collection.mutable
8182
import scala.concurrent.Await
@@ -143,18 +144,12 @@ private[spark] object ArmadaClientApplication {
143144
private val DEFAULT_NAMESPACE = "default"
144145
private val DEFAULT_ARMADA_APP_ID = "armada-spark-app-id"
145146
private val DEFAULT_RUN_AS_USER = 185
146-
147147
}
148148

149149
/** Main class and entry point of application submission in KUBERNETES mode.
150150
*/
151151
private[spark] class ArmadaClientApplication extends SparkApplication {
152-
// FIXME: Find the real way to log properly.
153-
private def log(msg: String): Unit = {
154-
// scalastyle:off println
155-
System.err.println(msg)
156-
// scalastyle:on println
157-
}
152+
private val logger = LoggerFactory.getLogger(getClass)
158153

159154
override def start(args: Array[String], conf: SparkConf): Unit = {
160155
val parsedArguments = ClientArguments.fromCommandLineArgs(args)
@@ -168,17 +163,17 @@ private[spark] class ArmadaClientApplication extends SparkApplication {
168163
val armadaJobConfig = validateArmadaJobConfig(sparkConf, clientArguments)
169164

170165
val (host, port) = ArmadaUtils.parseMasterUrl(sparkConf.get("spark.master"))
171-
log(s"Connecting to Armada Server - host: $host, port: $port")
166+
logger.info(s"Connecting to Armada Server - host: $host, port: $port")
172167

173168
val armadaClient = ArmadaClient(host, port, useSsl = false, sparkConf.get(ARMADA_AUTH_TOKEN))
174169
val healthTimeout =
175170
Duration(sparkConf.get(ARMADA_HEALTH_CHECK_TIMEOUT), SECONDS)
176171

177-
log(s"Checking Armada Server health (timeout: $healthTimeout)")
172+
logger.info(s"Checking Armada Server health (timeout: $healthTimeout)")
178173
val healthResp = Await.result(armadaClient.submitHealth(), healthTimeout)
179174

180175
if (healthResp.status.isServing) {
181-
log("Armada Server is serving requests!")
176+
logger.info("Armada Server is serving requests!")
182177
} else {
183178
throw new RuntimeException(
184179
"Armada health check failed - Armada Server is not serving requests!"
@@ -194,7 +189,7 @@ private[spark] class ArmadaClientApplication extends SparkApplication {
194189
val lookoutURL =
195190
s"$lookoutBaseURL/?page=0&sort[id]=jobId&sort[desc]=true&" +
196191
s"ps=50&sb=$driverJobId&active=false&refresh=true"
197-
log(s"Lookout URL for the driver job is $lookoutURL")
192+
logger.info(s"Lookout URL for the driver job is $lookoutURL")
198193

199194
()
200195
}
@@ -836,7 +831,7 @@ private[spark] class ArmadaClientApplication extends SparkApplication {
836831
val error = Some(driverResponse.jobResponseItems.head.error)
837832
.filter(_.nonEmpty)
838833
.getOrElse("none")
839-
log(
834+
logger.info(
840835
s"Submitted driver job with ID: $driverJobId, Error: $error"
841836
)
842837
driverJobId
@@ -851,7 +846,7 @@ private[spark] class ArmadaClientApplication extends SparkApplication {
851846
val executorsResponse = armadaClient.submitJobs(queue, jobSetId, executors)
852847
executorsResponse.jobResponseItems.map { item =>
853848
val error = Some(item.error).filter(_.nonEmpty).getOrElse("none")
854-
log(s"Submitted executor job with ID: ${item.jobId}, Error: $error")
849+
logger.info(s"Submitted executor job with ID: ${item.jobId}, Error: $error")
855850
item.jobId
856851
}
857852
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
rootLogger.level = info
2+
rootLogger.appenderRef.console.ref = console
3+
4+
appender.console.type = Console
5+
appender.console.name = console
6+
appender.console.layout.type = PatternLayout
7+
appender.console.layout.pattern = %d{HH:mm:ss.SSS} %-5level %logger{20} - %msg%n

‎src/test/scala/org/apache/spark/deploy/armada/e2e/ArmadaOperations.scala‎

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import api.submit.Queue
2121
import com.fasterxml.jackson.databind.{DeserializationFeature, ObjectMapper}
2222
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory
2323
import com.fasterxml.jackson.module.scala.DefaultScalaModule
24+
import org.slf4j.{Logger, LoggerFactory}
2425

2526
import scala.concurrent.{ExecutionContext, Future}
2627
import scala.concurrent.duration._
@@ -46,6 +47,7 @@ object JobSetStatus {
4647
* Armada server URL (default: "localhost:30002")
4748
*/
4849
class ArmadaClient(armadaUrl: String = "localhost:30002") {
50+
private val logger = LoggerFactory.getLogger(getClass)
4951
private val processTimeout = DefaultProcessTimeout
5052

5153
private val yamlMapper = {
@@ -94,12 +96,12 @@ class ArmadaClient(armadaUrl: String = "localhost:30002") {
9496
def ensureQueueExists(name: String)(implicit ec: ExecutionContext): Future[Unit] = {
9597
getQueue(name).flatMap {
9698
case Some(_) =>
97-
println(s"[QUEUE] Queue $name already exists")
99+
logger.info(s"[QUEUE] Queue $name already exists")
98100
Future.successful(())
99101
case None =>
100-
println(s"[QUEUE] Creating queue $name...")
102+
logger.info(s"[QUEUE] Creating queue $name...")
101103
createQueue(name).flatMap { _ =>
102-
println(s"[QUEUE] Waiting for queue $name to become available...")
104+
logger.info(s"[QUEUE] Waiting for queue $name to become available...")
103105
Future {
104106
var attempts = 0
105107
val maxAttempts = 30
@@ -110,12 +112,12 @@ class ArmadaClient(armadaUrl: String = "localhost:30002") {
110112
queueFound = getQueueSync(name).isDefined
111113
attempts += 1
112114
if (!queueFound && attempts % 5 == 0) {
113-
println(s"[QUEUE] Still waiting for queue $name... (${attempts}s)")
115+
logger.info(s"[QUEUE] Still waiting for queue $name... (${attempts}s)")
114116
}
115117
}
116118

117119
if (queueFound) {
118-
println(s"[QUEUE] Queue $name is ready")
120+
logger.info(s"[QUEUE] Queue $name is ready")
119121
} else {
120122
throw new RuntimeException(s"Queue $name not available after $attempts seconds")
121123
}
@@ -164,7 +166,7 @@ class ArmadaClient(armadaUrl: String = "localhost:30002") {
164166
val elapsed = (System.currentTimeMillis() - startTime) / 1000
165167
// Log progress periodically for long-running jobs
166168
if (elapsed % ProgressLogInterval.toSeconds == 0 && elapsed > 0) {
167-
println(s"Still monitoring job - elapsed: ${elapsed}s")
169+
logger.info(s"Still monitoring job - elapsed: ${elapsed}s")
168170
}
169171
}
170172
}

‎src/test/scala/org/apache/spark/deploy/armada/e2e/ArmadaSparkE2E.scala‎

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import org.scalatest.funsuite.AnyFunSuite
2222
import org.scalatest.matchers.should.Matchers
2323
import org.scalatest.concurrent.{Eventually, ScalaFutures}
2424
import org.scalatest.time.{Seconds, Span}
25+
import org.slf4j.LoggerFactory
2526

2627
import java.util.Properties
2728
import scala.concurrent.ExecutionContext.Implicits.global
@@ -34,6 +35,8 @@ class ArmadaSparkE2E
3435
with ScalaFutures
3536
with Eventually {
3637

38+
private val logger = LoggerFactory.getLogger(getClass)
39+
3740
implicit override val patienceConfig: PatienceConfig = PatienceConfig(
3841
timeout = Span(300, Seconds),
3942
interval = Span(2, Seconds)
@@ -72,14 +75,14 @@ class ArmadaSparkE2E
7275
sparkVersion = finalSparkVersion
7376
)
7477

75-
println(s"Test configuration loaded: $baseConfig")
78+
logger.info(s"Test configuration loaded: $baseConfig")
7679

7780
// Verify Armada cluster is ready before running tests
7881
val clusterReadyTimeout = ClusterReadyTimeout.toSeconds.toInt
7982
val testQueueName = s"${baseConfig.baseQueueName}-cluster-check-${System.currentTimeMillis()}"
8083

81-
println(s"[CLUSTER-CHECK] Verifying Armada cluster readiness...")
82-
println(s"[CLUSTER-CHECK] Will retry for up to $clusterReadyTimeout seconds")
84+
logger.info(s"[CLUSTER-CHECK] Verifying Armada cluster readiness...")
85+
logger.info(s"[CLUSTER-CHECK] Will retry for up to $clusterReadyTimeout seconds")
8386

8487
val startTime = System.currentTimeMillis()
8588
var clusterReady = false
@@ -88,29 +91,33 @@ class ArmadaSparkE2E
8891

8992
while (!clusterReady && (System.currentTimeMillis() - startTime) < clusterReadyTimeout * 1000) {
9093
attempts += 1
91-
println(
94+
logger.info(
9295
s"[CLUSTER-CHECK] Attempt #$attempts - Creating and verifying test queue: $testQueueName"
9396
)
9497

9598
try {
9699
armadaClient.ensureQueueExists(testQueueName).futureValue
97-
println(s"[CLUSTER-CHECK] Queue creation and verification succeeded - cluster is ready!")
100+
logger.info(
101+
s"[CLUSTER-CHECK] Queue creation and verification succeeded - cluster is ready!"
102+
)
98103
clusterReady = true
99104

100105
try {
101106
armadaClient.deleteQueue(testQueueName).futureValue
102-
println(s"[CLUSTER-CHECK] Test queue cleaned up")
107+
logger.info(s"[CLUSTER-CHECK] Test queue cleaned up")
103108
} catch {
104109
case _: Exception => // Ignore cleanup failures
105110
}
106111
} catch {
107112
case ex: Exception =>
108113
lastError = Some(ex)
109114
val elapsed = (System.currentTimeMillis() - startTime) / 1000
110-
println(s"[CLUSTER-CHECK] Attempt #$attempts failed after ${elapsed}s: ${ex.getMessage}")
115+
logger.info(
116+
s"[CLUSTER-CHECK] Attempt #$attempts failed after ${elapsed}s: ${ex.getMessage}"
117+
)
111118

112119
if ((System.currentTimeMillis() - startTime) < clusterReadyTimeout * 1000) {
113-
println(
120+
logger.info(
114121
s"[CLUSTER-CHECK] Waiting ${ClusterCheckRetryDelay.toSeconds} seconds before retry..."
115122
)
116123
Thread.sleep(ClusterCheckRetryDelay.toMillis)
@@ -126,7 +133,7 @@ class ArmadaSparkE2E
126133
)
127134
}
128135

129-
println(
136+
logger.info(
130137
s"[CLUSTER-CHECK] Cluster verified ready after $totalTime seconds ($attempts attempts)"
131138
)
132139
}

‎src/test/scala/org/apache/spark/deploy/armada/e2e/ProcessExecutor.scala‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import scala.concurrent.{Future, ExecutionContext, blocking}
2222
import scala.concurrent.duration._
2323
import scala.sys.process._
2424
import scala.util.{Failure, Success, Try}
25+
import org.slf4j.{Logger, LoggerFactory}
2526

2627
case class ProcessResult(
2728
exitCode: Int,
@@ -31,6 +32,7 @@ case class ProcessResult(
3132
)
3233

3334
object ProcessExecutor {
35+
private val logger = LoggerFactory.getLogger(getClass)
3436

3537
/** Execute command and always return ProcessResult, even on failure */
3638
def executeWithResult(command: Seq[String], timeout: Duration): ProcessResult = {
@@ -42,14 +44,14 @@ object ProcessExecutor {
4244
stdout.append(line).append("\n")
4345
// Print docker/spark-submit output in real-time for debugging
4446
if (command.headOption.contains("docker") && line.nonEmpty) {
45-
println(s"[SPARK-SUBMIT] $line")
47+
logger.info(s"[SPARK-SUBMIT] $line")
4648
}
4749
},
4850
line => {
4951
stderr.append(line).append("\n")
5052
// Print docker/spark-submit errors in real-time for debugging
5153
if (command.headOption.contains("docker") && line.nonEmpty) {
52-
println(s"[SPARK-SUBMIT] $line")
54+
logger.info(s"[SPARK-SUBMIT] $line")
5355
}
5456
}
5557
)

‎src/test/scala/org/apache/spark/deploy/armada/e2e/SimplePodMonitor.scala‎

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,13 @@
1717

1818
package org.apache.spark.deploy.armada.e2e
1919

20+
import org.slf4j.{Logger, LoggerFactory}
2021
import scala.collection.mutable
2122
import scala.concurrent.duration._
2223

2324
/** Simple pod monitoring that captures logs and events on failure */
2425
class SimplePodMonitor(namespace: String) {
26+
private val logger = LoggerFactory.getLogger(getClass)
2527
private val capturedLogs = mutable.ArrayBuffer[String]()
2628

2729
/** Check if any pods have failed and capture their logs if so */
@@ -45,7 +47,7 @@ class SimplePodMonitor(namespace: String) {
4547
val failedPodName = failedPods.head.split("\\s+").head
4648

4749
// Immediately capture logs for the failed pod
48-
println(s"[MONITOR] Pod $failedPodName failed, capturing logs...")
50+
logger.info(s"[MONITOR] Pod $failedPodName failed, capturing logs...")
4951
try {
5052
val logsCmd = Seq(
5153
"kubectl",
@@ -58,8 +60,8 @@ class SimplePodMonitor(namespace: String) {
5860
)
5961
val logsResult = ProcessExecutor.executeWithResult(logsCmd, 10.seconds)
6062
if (logsResult.exitCode == 0 && logsResult.stdout.nonEmpty) {
61-
println(s"[MONITOR] Pod $failedPodName logs:")
62-
println(logsResult.stdout)
63+
logger.info(s"[MONITOR] Pod $failedPodName logs:")
64+
logger.info(logsResult.stdout)
6365
}
6466

6567
// Also try to describe the pod
@@ -69,33 +71,33 @@ class SimplePodMonitor(namespace: String) {
6971
val lines = describeResult.stdout.split("\n")
7072
val eventsIndex = lines.indexWhere(_.contains("Events:"))
7173
if (eventsIndex >= 0) {
72-
println(s"[MONITOR] Pod $failedPodName events:")
73-
println(lines.slice(eventsIndex, eventsIndex + 20).mkString("\n"))
74+
logger.info(s"[MONITOR] Pod $failedPodName events:")
75+
logger.info(lines.slice(eventsIndex, eventsIndex + 20).mkString("\n"))
7476
}
7577
}
7678
} catch {
7779
case e: Exception =>
78-
println(s"[MONITOR] Failed to capture logs for $failedPodName: ${e.getMessage}")
80+
logger.info(s"[MONITOR] Failed to capture logs for $failedPodName: ${e.getMessage}")
7981
}
8082

8183
Some(s"Pod $failedPodName failed in namespace $namespace")
8284
} else {
8385
None
8486
}
8587
} else {
86-
println(s"[MONITOR] Failed to get pods: ${podsResult.stderr}")
88+
logger.info(s"[MONITOR] Failed to get pods: ${podsResult.stderr}")
8789
None
8890
}
8991
} catch {
9092
case e: Exception =>
91-
println(s"[MONITOR] Error checking pods: ${e.getMessage}")
93+
logger.info(s"[MONITOR] Error checking pods: ${e.getMessage}")
9294
None
9395
}
9496
}
9597

9698
/** Capture all logs and events for debugging */
9799
def captureDebugInfo(): Unit = {
98-
println(s"\n[DEBUG] Capturing debug info for namespace $namespace")
100+
logger.info(s"[DEBUG] Capturing debug info for namespace $namespace")
99101

100102
try {
101103
val podsCmd = Seq("kubectl", "get", "pods", "-n", namespace, "-o", "name")
@@ -107,7 +109,7 @@ class SimplePodMonitor(namespace: String) {
107109

108110
podNames.foreach { podName =>
109111
try {
110-
println(s"[DEBUG] Capturing logs for pod $podName")
112+
logger.info(s"[DEBUG] Capturing logs for pod $podName")
111113

112114
val logsCmd = Seq(
113115
"kubectl",
@@ -150,7 +152,7 @@ class SimplePodMonitor(namespace: String) {
150152
}
151153
} catch {
152154
case e: Exception =>
153-
println(s"[DEBUG] Failed to capture info for pod $podName: ${e.getMessage}")
155+
logger.info(s"[DEBUG] Failed to capture info for pod $podName: ${e.getMessage}")
154156
}
155157
}
156158
}
@@ -163,16 +165,16 @@ class SimplePodMonitor(namespace: String) {
163165

164166
} catch {
165167
case e: Exception =>
166-
println(s"[DEBUG] Failed to capture debug info: ${e.getMessage}")
168+
logger.info(s"[DEBUG] Failed to capture debug info: ${e.getMessage}")
167169
}
168170
}
169171

170172
/** Print all captured logs */
171173
def printCapturedLogs(): Unit = {
172174
if (capturedLogs.nonEmpty) {
173-
println(s"\n========== DEBUG INFO FOR NAMESPACE: $namespace ==========")
174-
capturedLogs.foreach(println)
175-
println(s"========== END DEBUG INFO ==========\n")
175+
logger.info(s"\n========== DEBUG INFO FOR NAMESPACE: $namespace ==========")
176+
capturedLogs.foreach(logger.info)
177+
logger.info(s"========== END DEBUG INFO ==========\n")
176178
}
177179
}
178180
}

0 commit comments

Comments
 (0)