| /* |
| * 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.sling.engine.impl.log; |
| |
| import java.io.File; |
| import java.io.IOException; |
| |
| import org.apache.sling.engine.RequestLog; |
| import org.osgi.framework.BundleContext; |
| import org.osgi.service.component.annotations.Activate; |
| import org.osgi.service.component.annotations.Component; |
| import org.osgi.service.component.annotations.ConfigurationPolicy; |
| import org.osgi.service.component.annotations.Deactivate; |
| import org.osgi.service.component.propertytypes.ServiceDescription; |
| import org.osgi.service.component.propertytypes.ServiceVendor; |
| import org.osgi.service.metatype.annotations.AttributeDefinition; |
| import org.osgi.service.metatype.annotations.Designate; |
| import org.osgi.service.metatype.annotations.ObjectClassDefinition; |
| import org.osgi.service.metatype.annotations.Option; |
| |
| /** |
| * The <code>RequestLoggerService</code> is a factory component which gets |
| * configuration to register loggers for the {@link RequestLogger}. |
| */ |
| @Component(service = RequestLoggerService.class, configurationPolicy = ConfigurationPolicy.REQUIRE) |
| @ServiceDescription("Factory for configuration based request/access loggers") |
| @ServiceVendor("The Apache Software Foundation") |
| @Designate(ocd = RequestLoggerService.Config.class, factory = true) |
| public class RequestLoggerService { |
| |
| @ObjectClassDefinition( |
| name = "Apache Sling Customizable Request Data Logger", |
| description = "This configuration creates customizable " |
| + "loggers for request content. Each configuration results in a logger writing " |
| + "the requested data. Deleting an existing configuration removes the respective " |
| + "logger.") |
| public @interface Config { |
| |
| @AttributeDefinition( |
| name = "Log Format", |
| description = |
| "The format for log entries. This is " |
| + "a format string as defined at https://sling.apache.org/site/client-request-logging.html#ClientRequestLogging-LogFormatSpecification.") |
| String request_log_service_format(); |
| |
| @AttributeDefinition( |
| name = "Logger Name", |
| description = "Name of the destination for the log " |
| + "output. Depending on the output type this is a file name (absolute or " |
| + "relative), a SLF4J logger name or the name under which a RequestLog service " |
| + "has been registered.") |
| String request_log_service_output() default "request.log"; |
| |
| @AttributeDefinition( |
| name = "Logger Type", |
| description = "Type of log destination. Select " |
| + "\"Logger Name\" to write the access log to an SLF4J logger, \"File Name\" to " |
| + "write the access log to a file (relative paths resolved against sling.home) " |
| + "or \"RequestLog Service\" to use a named OSGi service registered with the " |
| + "service interface \"org.apache.sling.engine.RequestLog\" and a service property " |
| + "\"requestlog.name\" equal to the Logger Name setting.", |
| options = { |
| @Option(label = "Logger Name", value = "0"), |
| @Option(label = "File Name", value = "1"), |
| @Option(label = "RequestLog Service", value = "2") |
| }) |
| int request_log_service_outputtype() default 0; |
| |
| @AttributeDefinition( |
| name = "Request Entry", |
| description = "Check if the logger is called on " |
| + "request entry. Otherwise leave unchecked and the logger will be called on " |
| + "request exit (aka termination), which is the default for access logger type " |
| + "loggers.") |
| boolean request_log_service_onentry() default false; |
| } |
| |
| private static final int OUTPUT_TYPE_LOGGER = 0; |
| |
| private static final int OUTPUT_TYPE_FILE = 1; |
| |
| private static final int OUTPUT_TYPE_CLASS = 2; |
| |
| private boolean onEntry; |
| |
| private CustomLogFormat logFormat; |
| |
| private RequestLog log; |
| |
| /** |
| * Public default constructor for SCR integration |
| */ |
| public RequestLoggerService() {} |
| |
| RequestLoggerService(BundleContext bundleContext, Config configuration) { |
| this.setup(bundleContext, configuration); |
| } |
| |
| @Activate |
| void setup(BundleContext bundleContext, Config configuration) { |
| // whether to log on request entry or request exit |
| this.onEntry = configuration.request_log_service_onentry(); |
| |
| // shared or private CustomLogFormat |
| final String format = configuration.request_log_service_format(); |
| if (format != null) { |
| this.logFormat = new CustomLogFormat(format); |
| } |
| |
| // where to log to |
| final String output = configuration.request_log_service_output(); |
| if (output != null) { |
| this.log = this.getLog(bundleContext, output, configuration.request_log_service_outputtype()); |
| } |
| } |
| |
| @Deactivate |
| void shutdown() { |
| if (this.log != null) { |
| this.log.close(); |
| this.log = null; |
| } |
| |
| this.logFormat = null; |
| } |
| |
| void log(RequestLoggerRequest request, RequestLoggerResponse response) { |
| if (this.log != null && this.logFormat != null) { |
| this.log.write(this.logFormat.format(request, response)); |
| } |
| } |
| |
| boolean isOnEntry() { |
| return this.onEntry; |
| } |
| |
| private RequestLog getLog(BundleContext bundleContext, String output, int outputType) { |
| switch (outputType) { |
| case OUTPUT_TYPE_FILE: |
| // file logging |
| try { |
| // ensure the path is absolute |
| File file = new File(output); |
| if (!file.isAbsolute()) { |
| final String home = bundleContext.getProperty("sling.home"); |
| if (home != null) { |
| file = new File(home, output); |
| } |
| file = file.getAbsoluteFile(); |
| } |
| |
| return new FileRequestLog(file); |
| } catch (IOException ioe) { |
| // TODO: log |
| } |
| break; |
| |
| case OUTPUT_TYPE_CLASS: |
| // only try to use service if we have a bundle context |
| if (bundleContext != null) { |
| return new RequestLogServiceFacade(bundleContext, output); |
| } |
| break; |
| |
| case OUTPUT_TYPE_LOGGER: |
| default: |
| return new LoggerRequestLog(output); |
| } |
| |
| // fallback in case of issue or so... |
| return null; |
| } |
| } |