DRILL-4571: Add link to local Drill logs from the web UI

This closes #472


Project: http://git-wip-us.apache.org/repos/asf/drill/repo
Commit: http://git-wip-us.apache.org/repos/asf/drill/commit/1a89a7fe
Tree: http://git-wip-us.apache.org/repos/asf/drill/tree/1a89a7fe
Diff: http://git-wip-us.apache.org/repos/asf/drill/diff/1a89a7fe

Branch: refs/heads/master
Commit: 1a89a7fe56533ecea8f7a7c9be6a3699925f3c96
Parents: 94b8aec
Author: Arina Ielchiieva <arina.yelchiy...@gmail.com>
Authored: Thu Mar 31 18:43:25 2016 +0300
Committer: Parth Chandra <par...@apache.org>
Committed: Tue May 3 10:50:09 2016 -0700

----------------------------------------------------------------------
 distribution/src/resources/logback.xml          |   2 +-
 .../org/apache/drill/exec/ExecConstants.java    |   5 +
 .../server/options/SystemOptionManager.java     |   3 +-
 .../drill/exec/server/rest/DrillRestServer.java |   1 +
 .../drill/exec/server/rest/LogsResources.java   | 211 +++++++++++++++++++
 .../server/rest/ViewableWithPermissions.java    |   1 +
 .../src/main/resources/rest/generic.ftl         |   3 +
 .../src/main/resources/rest/logs/list.ftl       |  55 +++++
 .../src/main/resources/rest/logs/log.ftl        |  37 ++++
 9 files changed, 316 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/drill/blob/1a89a7fe/distribution/src/resources/logback.xml
----------------------------------------------------------------------
diff --git a/distribution/src/resources/logback.xml 
b/distribution/src/resources/logback.xml
index 350383a..fb53dfc 100644
--- a/distribution/src/resources/logback.xml
+++ b/distribution/src/resources/logback.xml
@@ -44,7 +44,7 @@
         <maxFileSize>100MB</maxFileSize>
       </triggeringPolicy>
       <encoder>
-        <pattern>%msg</pattern>
+        <pattern>%msg%n</pattern>
       </encoder>
     </appender>
     

http://git-wip-us.apache.org/repos/asf/drill/blob/1a89a7fe/exec/java-exec/src/main/java/org/apache/drill/exec/ExecConstants.java
----------------------------------------------------------------------
diff --git 
a/exec/java-exec/src/main/java/org/apache/drill/exec/ExecConstants.java 
b/exec/java-exec/src/main/java/org/apache/drill/exec/ExecConstants.java
index 7f216f0..17fbb7b 100644
--- a/exec/java-exec/src/main/java/org/apache/drill/exec/ExecConstants.java
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/ExecConstants.java
@@ -303,4 +303,9 @@ public interface ExecConstants {
   StringValidator IMPERSONATION_POLICY_VALIDATOR =
       new 
InboundImpersonationManager.InboundImpersonationPolicyValidator(IMPERSONATION_POLICIES_KEY,
 "[]");
 
+  /**
+   * Web settings
+   */
+  String WEB_LOGS_MAX_LINES = "web.logs.max_lines";
+  OptionValidator WEB_LOGS_MAX_LINES_VALIDATOR = new 
PositiveLongValidator(WEB_LOGS_MAX_LINES, Integer.MAX_VALUE, 10000);
 }

http://git-wip-us.apache.org/repos/asf/drill/blob/1a89a7fe/exec/java-exec/src/main/java/org/apache/drill/exec/server/options/SystemOptionManager.java
----------------------------------------------------------------------
diff --git 
a/exec/java-exec/src/main/java/org/apache/drill/exec/server/options/SystemOptionManager.java
 
b/exec/java-exec/src/main/java/org/apache/drill/exec/server/options/SystemOptionManager.java
index db78108..c35ed0e 100644
--- 
a/exec/java-exec/src/main/java/org/apache/drill/exec/server/options/SystemOptionManager.java
+++ 
b/exec/java-exec/src/main/java/org/apache/drill/exec/server/options/SystemOptionManager.java
@@ -135,7 +135,8 @@ public class SystemOptionManager extends BaseOptionManager 
implements AutoClosea
       ExecConstants.ENABLE_WINDOW_FUNCTIONS_VALIDATOR,
       ClassTransformer.SCALAR_REPLACEMENT_VALIDATOR,
       ExecConstants.ENABLE_NEW_TEXT_READER,
-      ExecConstants.ENABLE_BULK_LOAD_TABLE_LIST
+      ExecConstants.ENABLE_BULK_LOAD_TABLE_LIST,
+      ExecConstants.WEB_LOGS_MAX_LINES_VALIDATOR
     };
     final Map<String, OptionValidator> tmp = new HashMap<>();
     for (final OptionValidator validator : validators) {

http://git-wip-us.apache.org/repos/asf/drill/blob/1a89a7fe/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/DrillRestServer.java
----------------------------------------------------------------------
diff --git 
a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/DrillRestServer.java
 
b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/DrillRestServer.java
index ceecdb4..0401d58 100644
--- 
a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/DrillRestServer.java
+++ 
b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/DrillRestServer.java
@@ -55,6 +55,7 @@ public class DrillRestServer extends ResourceConfig {
     register(QueryResources.class);
     register(MetricsResources.class);
     register(ThreadsResources.class);
+    register(LogsResources.class);
     register(FreemarkerMvcFeature.class);
     register(MultiPartFeature.class);
     property(ServerProperties.METAINF_SERVICES_LOOKUP_DISABLE, true);

http://git-wip-us.apache.org/repos/asf/drill/blob/1a89a7fe/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/LogsResources.java
----------------------------------------------------------------------
diff --git 
a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/LogsResources.java
 
b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/LogsResources.java
new file mode 100644
index 0000000..8a89d41
--- /dev/null
+++ 
b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/LogsResources.java
@@ -0,0 +1,211 @@
+/**
+ * ****************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ****************************************************************************
+ */
+package org.apache.drill.exec.server.rest;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.common.collect.Sets;
+import org.apache.drill.common.exceptions.DrillRuntimeException;
+import org.apache.drill.exec.ExecConstants;
+import org.apache.drill.exec.work.WorkManager;
+import org.glassfish.jersey.server.mvc.Viewable;
+import org.joda.time.DateTime;
+import org.joda.time.format.DateTimeFormat;
+import org.joda.time.format.DateTimeFormatter;
+
+import javax.annotation.security.RolesAllowed;
+import javax.inject.Inject;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.SecurityContext;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileFilter;
+import java.io.FileReader;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Set;
+
+import static 
org.apache.drill.exec.server.rest.auth.DrillUserPrincipal.ADMIN_ROLE;
+
+@Path("/")
+@RolesAllowed(ADMIN_ROLE)
+public class LogsResources {
+
+  @Inject DrillRestServer.UserAuthEnabled authEnabled;
+  @Inject SecurityContext sc;
+  @Inject WorkManager work;
+
+  private static final FileFilter file_filter = new FileFilter() {
+    @Override
+    public boolean accept(File file) {
+      return file.isFile();
+    }
+  };
+  private static final DateTimeFormatter format = 
DateTimeFormat.forPattern("MM/dd/yyyy HH:mm:ss");
+
+
+  @GET
+  @Path("/logs")
+  @Produces(MediaType.TEXT_HTML)
+  public Viewable getLogs() {
+    Set<Log> logs = getLogsJSON();
+    return ViewableWithPermissions.create(authEnabled.get(), 
"/rest/logs/list.ftl", sc, logs);
+  }
+
+  @GET
+  @Path("/logs.json")
+  @Produces(MediaType.APPLICATION_JSON)
+  public Set<Log> getLogsJSON() {
+    Set<Log> logs = Sets.newTreeSet();
+    File[] files = getLogFolder().listFiles(file_filter);
+
+    for (File file : files) {
+      logs.add(new Log(file.getName(), file.length(), file.lastModified()));
+    }
+
+    return logs;
+  }
+
+  @GET
+  @Path("/log/{name}/content")
+  @Produces(MediaType.TEXT_HTML)
+  public Viewable getLog(@PathParam("name") String name) throws IOException {
+    LogContent content = getLogJSON(name);
+    return ViewableWithPermissions.create(authEnabled.get(), 
"/rest/logs/log.ftl", sc, content);
+  }
+
+  @GET
+  @Path("/log/{name}/content.json")
+  @Produces(MediaType.APPLICATION_JSON)
+  public LogContent getLogJSON(@PathParam("name") final String name) throws 
IOException {
+    File file = getFileByName(getLogFolder(), name);
+
+    final int maxLines = 
work.getContext().getOptionManager().getOption(ExecConstants.WEB_LOGS_MAX_LINES).num_val.intValue();
+
+    try (BufferedReader br = new BufferedReader(new FileReader(file))) {
+      Map<String, String> cache = new LinkedHashMap<String, String>(maxLines, 
.75f, true) {
+        @Override
+        protected boolean removeEldestEntry(Map.Entry<String, String> eldest) {
+          return size() > maxLines;
+        }
+      };
+
+      String line;
+      while ((line = br.readLine()) != null) {
+        cache.put(line, null);
+      }
+
+      return new LogContent(file.getName(), cache.keySet(), maxLines);
+    }
+  }
+
+  @GET
+  @Path("/log/{name}/download")
+  @Produces(MediaType.TEXT_PLAIN)
+  public Response getFullLog(@PathParam("name") final String name) {
+    File file = getFileByName(getLogFolder(), name);
+    Response.ResponseBuilder response = Response.ok(file);
+    response.header("Content-Disposition", 
String.format("attachment;filename\"%s\"", name));
+    return response.build();
+  }
+
+  private File getLogFolder() {
+    return new File(System.getenv("DRILL_LOG_DIR"));
+  }
+
+  private File getFileByName(File folder, final String name) {
+    File[] files = folder.listFiles(new FilenameFilter() {
+      @Override
+      public boolean accept(File dir, String fileName) {
+        return fileName.equals(name);
+      }
+    });
+    if (files.length == 0) {
+      throw new DrillRuntimeException (name + " doesn't exist");
+    }
+    return files[0];
+  }
+
+
+  @XmlRootElement
+  public class Log implements Comparable<Log> {
+
+    private String name;
+    private long size;
+    private DateTime lastModified;
+
+    @JsonCreator
+    public Log (@JsonProperty("name") String name, @JsonProperty("size") long 
size, @JsonProperty("lastModified") long lastModified) {
+      this.name = name;
+      this.size = size;
+      this.lastModified = new DateTime(lastModified);
+    }
+
+    public String getName() {
+      return name;
+    }
+
+    public String getSize() {
+      return Math.ceil(size / 1024d) + " KB";
+    }
+
+    public String getLastModified() {
+      return lastModified.toString(format);
+    }
+
+    @Override
+    public int compareTo(Log log) {
+      return this.getName().compareTo(log.getName());
+    }
+  }
+
+  @XmlRootElement
+  public class LogContent {
+    private String name;
+    private Collection<String> lines;
+    private int maxLines;
+
+    @JsonCreator
+    public LogContent (@JsonProperty("name") String name, 
@JsonProperty("lines") Collection<String> lines, @JsonProperty("maxLines") int 
maxLines) {
+      this.name = name;
+      this.lines = lines;
+      this.maxLines = maxLines;
+    }
+
+    public String getName() {
+      return name;
+    }
+
+    public Collection<String> getLines() { return lines; }
+
+    public int getMaxLines() { return maxLines; }
+  }
+}

http://git-wip-us.apache.org/repos/asf/drill/blob/1a89a7fe/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/ViewableWithPermissions.java
----------------------------------------------------------------------
diff --git 
a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/ViewableWithPermissions.java
 
b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/ViewableWithPermissions.java
index b2a0fae..73019aa 100644
--- 
a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/ViewableWithPermissions.java
+++ 
b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/ViewableWithPermissions.java
@@ -78,6 +78,7 @@ public class ViewableWithPermissions extends Viewable {
         .put("showStorage", isAdmin)
         .put("showOptions", isAdmin)
         .put("showThreads", isAdmin)
+        .put("showLogs", isAdmin)
         .put("showLogin", authEnabled && showControls && !isUserLoggedIn)
         .put("showLogout", authEnabled && showControls && isUserLoggedIn)
         .put("loggedInUserName", authEnabled && showControls &&

http://git-wip-us.apache.org/repos/asf/drill/blob/1a89a7fe/exec/java-exec/src/main/resources/rest/generic.ftl
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/resources/rest/generic.ftl 
b/exec/java-exec/src/main/resources/rest/generic.ftl
index b3e249e..60869e7 100644
--- a/exec/java-exec/src/main/resources/rest/generic.ftl
+++ b/exec/java-exec/src/main/resources/rest/generic.ftl
@@ -64,6 +64,9 @@
               <#if showThreads == true>
               <li><a href="/threads">Threads</a></li>
               </#if>
+              <#if showLogs == true>
+                  <li><a href="/logs">Logs</a></li>
+              </#if>
             </ul>
             </#if>
             <ul class="nav navbar-nav navbar-right">

http://git-wip-us.apache.org/repos/asf/drill/blob/1a89a7fe/exec/java-exec/src/main/resources/rest/logs/list.ftl
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/resources/rest/logs/list.ftl 
b/exec/java-exec/src/main/resources/rest/logs/list.ftl
new file mode 100644
index 0000000..3d836df
--- /dev/null
+++ b/exec/java-exec/src/main/resources/rest/logs/list.ftl
@@ -0,0 +1,55 @@
+<#-- Licensed to the Apache Software Foundation (ASF) under one or more 
contributor
+  license agreements. See the NOTICE file distributed with this work for 
additional
+  information regarding copyright ownership. The ASF licenses this file to
+  You under the Apache License, Version 2.0 (the "License"); you may not use
+  this file except in compliance with the License. You may obtain a copy of
+  the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required
+  by applicable law or agreed to in writing, software distributed under the
+  License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
+  OF ANY KIND, either express or implied. See the License for the specific
+  language governing permissions and limitations under the License. -->
+
+<#include "*/generic.ftl">
+<#macro page_head>
+</#macro>
+
+<#macro page_body>
+<a href="/queries">back</a><br/>
+<div class="page-header">
+</div>
+
+<#if (model?size > 0)>
+<div class="table-responsive">
+    <table class="table table-hover">
+            <thead>
+            <td>Name</td>
+            <td>Size</td>
+            <td>Last Modified</td>
+            </thead>
+        <tbody>
+            <#list model as log>
+            <tr>
+                <td>
+                    <a href="/log/${log.getName()}/content">
+                        <div 
style="height:100%;width:100%;white-space:pre-line">${log.getName()}</div>
+                    </a>
+                </td>
+                <td>
+                    <div 
style="height:100%;width:100%;white-space:pre-line">${log.getSize()}</div>
+                </td>
+                <td>
+                    <div 
style="height:100%;width:100%;white-space:pre-line">${log.getLastModified()}</div>
+                </td>
+            </tr>
+            </#list>
+        </tbody>
+    </table>
+</div>
+<#else>
+<div id="message" class="alert alert-info">
+    <strong>No logs are available.</strong>
+</div>
+</#if>
+</#macro>
+
+<@page_html/>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/drill/blob/1a89a7fe/exec/java-exec/src/main/resources/rest/logs/log.ftl
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/resources/rest/logs/log.ftl 
b/exec/java-exec/src/main/resources/rest/logs/log.ftl
new file mode 100644
index 0000000..b09b57a
--- /dev/null
+++ b/exec/java-exec/src/main/resources/rest/logs/log.ftl
@@ -0,0 +1,37 @@
+<#-- Licensed to the Apache Software Foundation (ASF) under one or more 
contributor
+  license agreements. See the NOTICE file distributed with this work for 
additional
+  information regarding copyright ownership. The ASF licenses this file to
+  You under the Apache License, Version 2.0 (the "License"); you may not use
+  this file except in compliance with the License. You may obtain a copy of
+  the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required
+  by applicable law or agreed to in writing, software distributed under the
+  License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
+  OF ANY KIND, either express or implied. See the License for the specific
+  language governing permissions and limitations under the License. -->
+
+<#include "*/generic.ftl">
+<#macro page_head>
+</#macro>
+
+<#macro page_body>
+<a href="/logs">back</a><br/>
+<div class="page-header">
+</div>
+<h3>${model.getName()} <span class="badge alert-info">(last 
${model.getMaxLines()} lines)</span></h3>
+<p>
+    <a href="/log/${model.getName()}/download">Download Full Log</a>
+</p>
+    <#if (model.getLines()?size > 0)>
+    <pre>
+        <#list model.getLines() as line>
+${line}
+            </#list>
+        </pre>
+    <#else>
+    <div id="message" class="alert alert-info">
+        <strong>Log is empty.</strong>
+    </div>
+    </#if>
+</#macro>
+
+<@page_html/>
\ No newline at end of file

Reply via email to