This is an automated email from the ASF dual-hosted git repository.

dongjoon pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/spark.git


The following commit(s) were added to refs/heads/master by this push:
     new 48cd8604f953 [SPARK-46868][CORE] Support Spark Worker Log UI
48cd8604f953 is described below

commit 48cd8604f953dc82cadb6c076914d4d5c69b8126
Author: Dongjoon Hyun <[email protected]>
AuthorDate: Thu Jan 25 19:31:26 2024 -0800

    [SPARK-46868][CORE] Support Spark Worker Log UI
    
    ### What changes were proposed in this pull request?
    
    This PR aims to support `Spark Worker Log UI` when `SPARK_LOG_DIR` is under 
work directory.
    
    ### Why are the changes needed?
    
    This is a new feature to allow the users to access the worker log like the 
following.
    
    **BEFORE**
    
    ![Screenshot 2024-01-25 at 3 04 20 
PM](https://github.com/apache/spark/assets/9700541/73ef33d5-9b56-4cca-83c2-9fd2e8ab5201)
    
    **AFTER**
    
    - Worker Page (Worker ID provides a new hyperlink for Log UI)
    ![Screenshot 2024-01-25 at 2 58 44 
PM](https://github.com/apache/spark/assets/9700541/1de66eee-7b73-4be3-a12c-e008442b7b6c)
    
    - Log UI
    ![Screenshot 2024-01-25 at 6 00 25 
PM](https://github.com/apache/spark/assets/9700541/e20fde05-ce5e-42cb-9112-4a8d2ec69418)
    
    ### Does this PR introduce _any_ user-facing change?
    
    To provide a better UX.
    
    ### How was this patch tested?
    
    Manually.
    
    ```
    $ sbin/start-master.sh
    $ SPARK_LOG_DIR=$PWD/work/logs sbin/start-worker.sh spark://$(hostname):7077
    ```
    
    ### Was this patch authored or co-authored using generative AI tooling?
    
    No.
    
    Closes #44888 from dongjoon-hyun/SPARK-46868.
    
    Authored-by: Dongjoon Hyun <[email protected]>
    Signed-off-by: Dongjoon Hyun <[email protected]>
---
 .../apache/spark/deploy/worker/ui/LogPage.scala    | 29 ++++++++++++++++------
 .../apache/spark/deploy/worker/ui/WorkerPage.scala |  6 ++++-
 2 files changed, 26 insertions(+), 9 deletions(-)

diff --git 
a/core/src/main/scala/org/apache/spark/deploy/worker/ui/LogPage.scala 
b/core/src/main/scala/org/apache/spark/deploy/worker/ui/LogPage.scala
index dd714cdc4437..991c791cc79e 100644
--- a/core/src/main/scala/org/apache/spark/deploy/worker/ui/LogPage.scala
+++ b/core/src/main/scala/org/apache/spark/deploy/worker/ui/LogPage.scala
@@ -30,23 +30,26 @@ import org.apache.spark.util.logging.RollingFileAppender
 private[ui] class LogPage(parent: WorkerWebUI) extends WebUIPage("logPage") 
with Logging {
   private val worker = parent.worker
   private val workDir = new File(parent.workDir.toURI.normalize().getPath)
-  private val supportedLogTypes = Set("stderr", "stdout")
+  private val supportedLogTypes = Set("stderr", "stdout", "out")
   private val defaultBytes = 100 * 1024
 
   def renderLog(request: HttpServletRequest): String = {
     val appId = Option(request.getParameter("appId"))
     val executorId = Option(request.getParameter("executorId"))
     val driverId = Option(request.getParameter("driverId"))
+    val self = Option(request.getParameter("self"))
     val logType = request.getParameter("logType")
     val offset = Option(request.getParameter("offset")).map(_.toLong)
     val byteLength = Option(request.getParameter("byteLength")).map(_.toInt)
       .getOrElse(defaultBytes)
 
-    val logDir = (appId, executorId, driverId) match {
-      case (Some(a), Some(e), None) =>
+    val logDir = (appId, executorId, driverId, self) match {
+      case (Some(a), Some(e), None, None) =>
         s"${workDir.getPath}/$a/$e/"
-      case (None, None, Some(d)) =>
+      case (None, None, Some(d), None) =>
         s"${workDir.getPath}/$d/"
+      case (None, None, None, Some(_)) =>
+        s"${sys.env.getOrElse("SPARK_LOG_DIR", workDir.getPath)}/"
       case _ =>
         throw new Exception("Request must specify either application or driver 
identifiers")
     }
@@ -60,16 +63,19 @@ private[ui] class LogPage(parent: WorkerWebUI) extends 
WebUIPage("logPage") with
     val appId = Option(request.getParameter("appId"))
     val executorId = Option(request.getParameter("executorId"))
     val driverId = Option(request.getParameter("driverId"))
+    val self = Option(request.getParameter("self"))
     val logType = request.getParameter("logType")
     val offset = Option(request.getParameter("offset")).map(_.toLong)
     val byteLength = Option(request.getParameter("byteLength")).map(_.toInt)
       .getOrElse(defaultBytes)
 
-    val (logDir, params, pageName) = (appId, executorId, driverId) match {
-      case (Some(a), Some(e), None) =>
+    val (logDir, params, pageName) = (appId, executorId, driverId, self) match 
{
+      case (Some(a), Some(e), None, None) =>
         (s"${workDir.getPath}/$a/$e/", s"appId=$a&executorId=$e", s"$a/$e")
-      case (None, None, Some(d)) =>
+      case (None, None, Some(d), None) =>
         (s"${workDir.getPath}/$d/", s"driverId=$d", d)
+      case (None, None, None, Some(_)) =>
+        (s"${sys.env.getOrElse("SPARK_LOG_DIR", workDir.getPath)}/", "self", 
"worker")
       case _ =>
         throw new Exception("Request must specify either application or driver 
identifiers")
     }
@@ -138,7 +144,14 @@ private[ui] class LogPage(parent: WorkerWebUI) extends 
WebUIPage("logPage") with
     }
 
     try {
-      val files = RollingFileAppender.getSortedRolledOverFiles(logDirectory, 
logType)
+      // Find a log file name
+      val fileName = if (logType.equals("out")) {
+        normalizedLogDir.listFiles.map(_.getName).filter(_.endsWith(".out"))
+          .headOption.getOrElse(logType)
+      } else {
+        logType
+      }
+      val files = RollingFileAppender.getSortedRolledOverFiles(logDirectory, 
fileName)
       logDebug(s"Sorted log files of type $logType in 
$logDirectory:\n${files.mkString("\n")}")
 
       val fileLengths: Seq[Long] = files.map(Utils.getFileLength(_, 
worker.conf))
diff --git 
a/core/src/main/scala/org/apache/spark/deploy/worker/ui/WorkerPage.scala 
b/core/src/main/scala/org/apache/spark/deploy/worker/ui/WorkerPage.scala
index e740b328dd7b..3dd5ebea471c 100644
--- a/core/src/main/scala/org/apache/spark/deploy/worker/ui/WorkerPage.scala
+++ b/core/src/main/scala/org/apache/spark/deploy/worker/ui/WorkerPage.scala
@@ -78,11 +78,15 @@ private[ui] class WorkerPage(parent: WorkerWebUI) extends 
WebUIPage("") {
     // For now we only show driver information if the user has submitted 
drivers to the cluster.
     // This is until we integrate the notion of drivers and applications in 
the UI.
 
+    val workerUrlRef = UIUtils.makeHref(parent.worker.reverseProxy, 
workerState.workerId,
+      parent.webUrl)
     val content =
       <div class="row"> <!-- Worker Details -->
         <div class="col-12">
           <ul class="list-unstyled">
-            <li><strong>ID:</strong> {workerState.workerId}</li>
+            <li><strong>ID:</strong>
+              <a 
href={s"$workerUrlRef/logPage/?self&logType=out"}>{workerState.workerId}</a>
+            </li>
             <li><strong>
               Master URL:</strong> {workerState.masterUrl}
             </li>


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to