blob: 655eb84e31ab196133251a6481a71c84befa9a1e [file] [log] [blame]
/*
* 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;
}
}