| /* |
| * 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. |
| */ |
| package org.apache.drill.exec.server.rest; |
| |
| import com.fasterxml.jackson.annotation.JsonCreator; |
| import com.fasterxml.jackson.annotation.JsonProperty; |
| import com.google.common.base.Preconditions; |
| 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 org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| 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.HttpHeaders; |
| 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.IOException; |
| import java.util.Collection; |
| 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 { |
| private static final Logger logger = LoggerFactory.getLogger(LogsResources.class); |
| |
| @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 { |
| try { |
| LogContent content = getLogJSON(name); |
| return ViewableWithPermissions.create(authEnabled.get(), "/rest/logs/log.ftl", sc, content); |
| } catch (Exception | Error e) { |
| logger.error("Exception was thrown when fetching log {} :\n{}", name, e); |
| return ViewableWithPermissions.create(authEnabled.get(), "/rest/errorMessage.ftl", sc, e); |
| } |
| } |
| |
| @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))) { |
| @SuppressWarnings("serial") |
| Map<Integer, String> cache = new LinkedHashMap<Integer, String>(maxLines, .75f, true) { |
| @Override |
| protected boolean removeEldestEntry(Map.Entry<Integer, String> eldest) { |
| return size() > maxLines; |
| } |
| }; |
| |
| String line; |
| int i = 0; |
| while ((line = br.readLine()) != null) { |
| cache.put(i++, line); |
| } |
| |
| return new LogContent(file.getName(), cache.values(), maxLines); |
| } |
| } |
| |
| @GET |
| @Path("/log/{name}/download") |
| @Produces(MediaType.TEXT_PLAIN) |
| public Response getFullLog(@PathParam("name") final String name) { |
| File file = getFileByName(getLogFolder(), name); |
| return Response.ok(file) |
| .header(HttpHeaders.CONTENT_DISPOSITION, String.format("attachment;filename=\"%s\"", name)) |
| .build(); |
| } |
| |
| private File getLogFolder() { |
| return new File(Preconditions.checkNotNull(System.getenv("DRILL_LOG_DIR"), "DRILL_LOG_DIR variable is not set")); |
| } |
| |
| private File getFileByName(File folder, final String name) { |
| File[] files = folder.listFiles((dir, fileName) -> 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 final String name; |
| private final long size; |
| private final 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 final String name; |
| private final Collection<String> lines; |
| private final 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; } |
| } |
| } |