Java tutorial
/* * JBoss, Home of Professional Open Source. * Copyright 2012, Red Hat, Inc., and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.as.logging; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SUBSYSTEM; import java.io.BufferedReader; import java.io.Closeable; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.nio.file.Path; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Set; import org.jboss.as.controller.AttributeDefinition; import org.jboss.as.controller.ModelVersion; import org.jboss.as.controller.OperationContext; import org.jboss.as.controller.OperationContext.ResultHandler; import org.jboss.as.controller.OperationFailedException; import org.jboss.as.controller.OperationStepHandler; import org.jboss.as.controller.PathAddress; import org.jboss.as.controller.PathElement; import org.jboss.as.controller.ReloadRequiredRemoveStepHandler; import org.jboss.as.controller.ReloadRequiredWriteAttributeHandler; import org.jboss.as.controller.SimpleAttributeDefinition; import org.jboss.as.controller.SimpleAttributeDefinitionBuilder; import org.jboss.as.controller.SimpleOperationDefinition; import org.jboss.as.controller.SimpleOperationDefinitionBuilder; import org.jboss.as.controller.operations.validation.IntRangeValidator; import org.jboss.as.controller.operations.validation.StringLengthValidator; import org.jboss.as.controller.registry.AttributeAccess.Flag; import org.jboss.as.controller.registry.ManagementResourceRegistration; import org.jboss.as.controller.registry.OperationEntry; import org.jboss.as.controller.registry.Resource; import org.jboss.as.controller.registry.RuntimePackageDependency; import org.jboss.as.controller.services.path.PathManager; import org.jboss.as.controller.transform.description.DiscardAttributeChecker; import org.jboss.as.controller.transform.description.RejectAttributeChecker; import org.jboss.as.controller.transform.description.ResourceTransformationDescriptionBuilder; import org.jboss.as.logging.logging.LoggingLogger; import org.jboss.as.logging.logmanager.WildFlyLogContextSelector; import org.jboss.as.server.ServerEnvironment; import org.jboss.dmr.ModelNode; import org.jboss.dmr.ModelType; import org.jboss.dmr.Property; /** * @author <a href="mailto:tomaz.cerar@redhat.com">Tomaz Cerar</a> * @author <a href="mailto:jperkins@redhat.com">James R. Perkins</a> */ public class LoggingResourceDefinition extends TransformerResourceDefinition { private static String APACHE_COMMONS_LOGGING = "org.apache.commons.logging"; private static String SLF4J = "org.slf4j"; private static String SLF4J_STUB = "org.jboss.logging.jul-to-slf4j-stub"; public static final String[] DUP_INJECTED_LOGGING_MODULES = new String[] { "org.jboss.logging", APACHE_COMMONS_LOGGING, "org.apache.log4j", SLF4J, SLF4J_STUB, }; static final PathElement SUBSYSTEM_PATH = PathElement.pathElement(SUBSYSTEM, LoggingExtension.SUBSYSTEM_NAME); static final SimpleAttributeDefinition ADD_LOGGING_API_DEPENDENCIES = SimpleAttributeDefinitionBuilder .create("add-logging-api-dependencies", ModelType.BOOLEAN, true).setAllowExpression(true) .setAttributeMarshaller(ElementAttributeMarshaller.VALUE_ATTRIBUTE_MARSHALLER) .setDefaultValue(new ModelNode(true)).setFlags(Flag.RESTART_ALL_SERVICES).build(); static final SimpleAttributeDefinition USE_DEPLOYMENT_LOGGING_CONFIG = SimpleAttributeDefinitionBuilder .create("use-deployment-logging-config", ModelType.BOOLEAN, true).setAllowExpression(true) .setAttributeMarshaller(ElementAttributeMarshaller.VALUE_ATTRIBUTE_MARSHALLER) .setDefaultValue(new ModelNode(true)).setFlags(Flag.RESTART_ALL_SERVICES).build(); private static final SimpleAttributeDefinition NAME = SimpleAttributeDefinitionBuilder .create("name", ModelType.STRING, false).setAllowExpression(true) .setValidator(new StringLengthValidator(1, false)).build(); private static final SimpleAttributeDefinition LINES = SimpleAttributeDefinitionBuilder .create("lines", ModelType.INT, true).setAllowExpression(true).setDefaultValue(new ModelNode(10)) .setValidator(new IntRangeValidator(-1, true)).build(); private static final SimpleAttributeDefinition SKIP = SimpleAttributeDefinitionBuilder .create("skip", ModelType.INT, true).setAllowExpression(true).setDefaultValue(new ModelNode(0)) .setValidator(new IntRangeValidator(0, true)).build(); private static final SimpleAttributeDefinition TAIL = SimpleAttributeDefinitionBuilder .create("tail", ModelType.BOOLEAN, true).setAllowExpression(true).setDefaultValue(new ModelNode(true)) .build(); private static final SimpleAttributeDefinition FILE_NAME = SimpleAttributeDefinitionBuilder .create("file-name", ModelType.STRING, false).setAllowExpression(false).build(); private static final SimpleAttributeDefinition FILE_SIZE = SimpleAttributeDefinitionBuilder .create("file-size", ModelType.LONG, false).setAllowExpression(false).build(); private static final SimpleAttributeDefinition LAST_MODIFIED_DATE = SimpleAttributeDefinitionBuilder .create("last-modified-date", ModelType.STRING, false).setAllowExpression(false).build(); private static final SimpleOperationDefinition READ_LOG_FILE = new SimpleOperationDefinitionBuilder( "read-log-file", LoggingExtension.getResourceDescriptionResolver()) .addAccessConstraint(LogFileResourceDefinition.VIEW_SERVER_LOGS) .setDeprecated(ModelVersion.create(3, 0, 0)) .setParameters(NAME, CommonAttributes.ENCODING, LINES, SKIP, TAIL).setReplyType(ModelType.LIST) .setReplyValueType(ModelType.STRING).setReadOnly().setRuntimeOnly().build(); private static final SimpleOperationDefinition LIST_LOG_FILES = new SimpleOperationDefinitionBuilder( "list-log-files", LoggingExtension.getResourceDescriptionResolver()) .addAccessConstraint(LogFileResourceDefinition.VIEW_SERVER_LOGS) .setDeprecated(ModelVersion.create(3, 0, 0)).setReplyType(ModelType.LIST) .setReplyParameters(FILE_NAME, FILE_SIZE, LAST_MODIFIED_DATE).setReadOnly().setRuntimeOnly() .build(); static final SimpleAttributeDefinition[] ATTRIBUTES = { ADD_LOGGING_API_DEPENDENCIES, USE_DEPLOYMENT_LOGGING_CONFIG, }; private final PathManager pathManager; protected LoggingResourceDefinition(final PathManager pathManager, final WildFlyLogContextSelector contextSelector) { super(new Parameters(SUBSYSTEM_PATH, LoggingExtension.getResourceDescriptionResolver()) .setAddHandler(new LoggingSubsystemAdd(pathManager, contextSelector)) .setRemoveHandler(ReloadRequiredRemoveStepHandler.INSTANCE) .setAddRestartLevel(OperationEntry.Flag.RESTART_ALL_SERVICES)); this.pathManager = pathManager; } @Override public void registerAttributes(final ManagementResourceRegistration resourceRegistration) { super.registerAttributes(resourceRegistration); final OperationStepHandler writeHandler = new ReloadRequiredWriteAttributeHandler(ATTRIBUTES); for (SimpleAttributeDefinition attribute : ATTRIBUTES) { resourceRegistration.registerReadWriteAttribute(attribute, null, writeHandler); } } @Override public void registerOperations(final ManagementResourceRegistration resourceRegistration) { super.registerOperations(resourceRegistration); // Only register on server if (pathManager != null) { resourceRegistration.registerOperationHandler(LIST_LOG_FILES, new ListLogFilesOperation()); resourceRegistration.registerOperationHandler(READ_LOG_FILE, new ReadLogFileOperation()); } } @Override public void registerTransformers(final KnownModelVersion modelVersion, final ResourceTransformationDescriptionBuilder rootResourceBuilder, final ResourceTransformationDescriptionBuilder loggingProfileBuilder) { switch (modelVersion) { case VERSION_1_5_0: { rootResourceBuilder.getAttributeBuilder() .setDiscard(new DiscardAttributeChecker.DiscardAttributeValueChecker(false, true, USE_DEPLOYMENT_LOGGING_CONFIG.getDefaultValue()), USE_DEPLOYMENT_LOGGING_CONFIG) .addRejectCheck(RejectAttributeChecker.DEFINED, USE_DEPLOYMENT_LOGGING_CONFIG).end(); break; } } } @Override public void registerAdditionalRuntimePackages(ManagementResourceRegistration resourceRegistration) { resourceRegistration.registerAdditionalRuntimePackages( RuntimePackageDependency.optional(APACHE_COMMONS_LOGGING), RuntimePackageDependency.optional(SLF4J), RuntimePackageDependency.optional(SLF4J_STUB), // The next ones are Loaded by LoggingExtension RuntimePackageDependency.optional("org.slf4j.ext"), RuntimePackageDependency.optional("org.slf4j.impl")); } private class ListLogFilesOperation implements OperationStepHandler { @Override public void execute(final OperationContext context, final ModelNode operation) { final ModelNode model = Resource.Tools.readModel(context.readResource(PathAddress.EMPTY_ADDRESS)); final String logDir = pathManager.getPathEntry(ServerEnvironment.SERVER_LOG_DIR).resolvePath(); List<File> logFiles; try { logFiles = findFiles(logDir, model); } catch (IOException e) { logFiles = Collections.emptyList(); LoggingLogger.ROOT_LOGGER.errorProcessingLogDirectory(logDir); } final SimpleDateFormat dateFormat = new SimpleDateFormat(LogFileResourceDefinition.ISO_8601_FORMAT); final ModelNode result = context.getResult().setEmptyList(); for (File logFile : logFiles) { final ModelNode fileInfo = new ModelNode(); fileInfo.get(FILE_NAME.getName()).set(logFile.getName()); fileInfo.get(FILE_SIZE.getName()).set(logFile.length()); fileInfo.get(LAST_MODIFIED_DATE.getName()).set(dateFormat.format(new Date(logFile.lastModified()))); result.add(fileInfo); } context.completeStep(ResultHandler.NOOP_RESULT_HANDLER); } } /** * Reads a log file and returns the results. * <p/> * <i>Note: </i> If this operation ends up being repeatedly invoked, from the web console for instance, there could * be a performance impact as the model is read and processed for file names during each invocation */ private class ReadLogFileOperation implements OperationStepHandler { @Override public void execute(final OperationContext context, final ModelNode operation) throws OperationFailedException { // Validate the operation for (AttributeDefinition attribute : READ_LOG_FILE.getParameters()) { attribute.validateOperation(operation); } final String fileName = NAME.resolveModelAttribute(context, operation).asString(); final int numberOfLines = LINES.resolveModelAttribute(context, operation).asInt(); final int skip = SKIP.resolveModelAttribute(context, operation).asInt(); final boolean tail = TAIL.resolveModelAttribute(context, operation).asBoolean(); final ModelNode encodingModel = CommonAttributes.ENCODING.resolveModelAttribute(context, operation); final String encoding = (encodingModel.isDefined() ? encodingModel.asString() : null); final File path = new File( pathManager.resolveRelativePathEntry(fileName, ServerEnvironment.SERVER_LOG_DIR)); // The file must exist if (!path.exists()) { throw LoggingLogger.ROOT_LOGGER.logFileNotFound(fileName, ServerEnvironment.SERVER_LOG_DIR); } final ModelNode model = Resource.Tools.readModel(context.readResource(PathAddress.EMPTY_ADDRESS)); final List<File> logFiles; try { logFiles = findFiles(pathManager.getPathEntry(ServerEnvironment.SERVER_LOG_DIR).resolvePath(), model); } catch (IOException e) { throw LoggingLogger.ROOT_LOGGER.failedToReadLogFile(e, fileName); } // User must have permissions to read the file if (!path.canRead() || !logFiles.contains(path)) { throw LoggingLogger.ROOT_LOGGER.readNotAllowed(fileName); } // Read the contents of the log file try { final List<String> lines; if (numberOfLines == 0) { lines = Collections.emptyList(); } else { lines = readLines(path, encoding, tail, skip, numberOfLines); } final ModelNode result = context.getResult().setEmptyList(); for (String line : lines) { result.add(line); } } catch (IOException e) { throw LoggingLogger.ROOT_LOGGER.failedToReadLogFile(e, fileName); } context.completeStep(ResultHandler.NOOP_RESULT_HANDLER); } private List<String> readLines(final File file, final String encoding, final boolean tail, final int skip, final int numberOfLines) throws IOException { final List<String> lines; if (numberOfLines < 0) { lines = new ArrayList<>(); } else { lines = new ArrayList<>(numberOfLines); } final InputStream in; BufferedReader reader = null; try { if (tail) { in = new LogFileResourceDefinition.LifoFileInputStream(file); } else { in = new FileInputStream(file); } if (encoding == null) { //system default used on purpose reader = new BufferedReader(new InputStreamReader(in)); } else { reader = new BufferedReader(new InputStreamReader(in, encoding)); } int lineCount = 0; String line; while ((line = reader.readLine()) != null) { if (++lineCount <= skip) continue; if (lines.size() == numberOfLines) break; lines.add(line); } if (tail) { Collections.reverse(lines); } return lines; } finally { safeClose(reader); } } } private static void safeClose(final Closeable closeable) { if (closeable != null) try { closeable.close(); } catch (Throwable t) { LoggingLogger.ROOT_LOGGER.failedToCloseResource(t, closeable); } } private static List<File> findFiles(final String defaultLogDir, final ModelNode model) throws IOException { final Set<Path> files = LoggingResource.findFiles(defaultLogDir, model, false); // Also need to include logging profile log files if (model.hasDefined(CommonAttributes.LOGGING_PROFILE)) { for (Property property : model.get(CommonAttributes.LOGGING_PROFILE).asPropertyList()) { files.addAll(LoggingResource.findFiles(defaultLogDir, property.getValue(), false)); } } ArrayList<File> result = new ArrayList<>(); for (Path file : files) { File toFile = file.toFile(); result.add(toFile); } return result; } }