/*- | |
* ============LICENSE_START======================================================= | |
* OSAM | |
* ================================================================================ | |
* Copyright (C) 2018 AT&T | |
* ================================================================================ | |
* Licensed 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. | |
* ============LICENSE_END========================================================= | |
*/ | |
package org.onap.osam.controllers; | |
import ch.qos.logback.classic.LoggerContext; | |
import ch.qos.logback.core.Appender; | |
import ch.qos.logback.core.FileAppender; | |
import ch.qos.logback.core.spi.AppenderAttachable; | |
import org.apache.commons.io.input.ReversedLinesFileReader; | |
import org.apache.commons.lang3.StringUtils; | |
import org.onap.portalsdk.core.controller.RestrictedBaseController; | |
import org.onap.portalsdk.core.logging.logic.EELFLoggerDelegate; | |
import org.onap.osam.model.ExceptionResponse; | |
import org.onap.osam.roles.Role; | |
import org.onap.osam.roles.RoleProvider; | |
import org.onap.osam.utils.Streams; | |
import org.slf4j.LoggerFactory; | |
import org.springframework.beans.factory.annotation.Autowired; | |
import org.springframework.http.HttpStatus; | |
import org.springframework.web.bind.annotation.*; | |
import javax.servlet.http.HttpServletRequest; | |
import javax.ws.rs.InternalServerErrorException; | |
import javax.ws.rs.NotAuthorizedException; | |
import java.io.File; | |
import java.io.IOException; | |
import java.util.List; | |
import java.util.Objects; | |
import java.util.function.Supplier; | |
import java.util.stream.Collectors; | |
import java.util.stream.Stream; | |
import static com.att.eelf.configuration.Configuration.GENERAL_LOGGER_NAME; | |
@RestController | |
@RequestMapping("logger") | |
public class LoggerController extends RestrictedBaseController { | |
private static final EELFLoggerDelegate LOGGER = EELFLoggerDelegate.getLogger(LoggerController.class); | |
@Autowired | |
RoleProvider roleProvider; | |
@RequestMapping(value = "/{loggerName:audit|error|metrics}", method = RequestMethod.GET) | |
public String getLog(@PathVariable String loggerName, HttpServletRequest request, | |
@RequestParam(value="limit", defaultValue = "5000") Integer limit) throws IOException { | |
List<Role> roles = roleProvider.getUserRoles(request); | |
boolean userPermitted = roleProvider.userPermissionIsReadLogs(roles); | |
if (!userPermitted) { | |
throw new NotAuthorizedException("User not authorized to get logs"); | |
} | |
String logfilePath = getLogfilePath(loggerName); | |
try (final ReversedLinesFileReader reader = new ReversedLinesFileReader(new File(logfilePath))) { | |
Supplier<String> reverseLinesSupplier = () -> { | |
try { | |
return reader.readLine(); | |
} catch (NullPointerException e) { | |
// EOF Reached | |
return null; | |
} catch (IOException e) { | |
throw new InternalServerErrorException("error while reading " + logfilePath, e); | |
} | |
}; | |
return Streams.takeWhile( | |
Stream.generate(reverseLinesSupplier), | |
line -> !StringUtils.contains(line, "Logging is started")) | |
.limit(limit) | |
.limit(5_000) | |
.filter(Objects::nonNull) | |
.collect(Collectors.joining("\n")); | |
} | |
} | |
private String getLogfilePath(String loggerName) { | |
LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory(); | |
return context.getLoggerList().stream() | |
.filter(logger -> logger.getName().equals(GENERAL_LOGGER_NAME + "." + loggerName)) | |
.flatMap(this::pullSubAppenders) | |
.flatMap(appender -> { | |
// Appender might be "attachable", if so - roll-up its sub-appenders | |
return (appender instanceof AppenderAttachable) ? | |
pullSubAppenders((AppenderAttachable<?>) appender) : Stream.of(appender); | |
}) | |
.filter(appender -> appender instanceof FileAppender) | |
.map(appender -> (FileAppender<?>) appender) | |
.map(FileAppender::getFile) | |
.findFirst() | |
.orElseThrow(() -> new InternalServerErrorException("logfile for " + loggerName + " not found")); | |
} | |
private <T> Stream<Appender<T>> pullSubAppenders(AppenderAttachable<T> logger) { | |
return Streams.fromIterator(logger.iteratorForAppenders()); | |
} | |
@ExceptionHandler({ NotAuthorizedException.class }) | |
@ResponseStatus(HttpStatus.UNAUTHORIZED) | |
public String notAuthorizedHandler(NotAuthorizedException e) { | |
return "UNAUTHORIZED"; | |
} | |
@ExceptionHandler({ IOException.class, InternalServerErrorException.class }) | |
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) | |
public ExceptionResponse ioExceptionHandler(Exception e) { | |
return org.onap.osam.controllers.ControllersUtils.handleException(e, LOGGER); | |
} | |
} |