Skip to content

Commit

Permalink
Implement AccessLog support for HTTP/2.0
Browse files Browse the repository at this point in the history
  • Loading branch information
AzeemMuzammil committed Jun 5, 2024
1 parent c0c3cdf commit a76a2c5
Show file tree
Hide file tree
Showing 15 changed files with 699 additions and 17 deletions.
4 changes: 4 additions & 0 deletions ballerina/http_log_manager.bal
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,13 @@ public type TraceLogAdvancedConfiguration record {|
# Represents HTTP access log configuration.
#
# + console - Boolean value to enable or disable console access logs
# + format - The format of access logs to be printed (either `flat` or `json`)
# + attributes - The list of attributes of access logs to be printed
# + path - Optional file path to store access logs
public type AccessLogConfiguration record {|
boolean console = false;
string format = "flat";
string[] attributes?;
string path?;
|};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -323,14 +323,32 @@ public final class HttpConstants {
public static final String HTTP_TRACE_LOG_ENABLED = "http.tracelog.enabled";
public static final String HTTP_ACCESS_LOG = "http.accesslog";
public static final String HTTP_ACCESS_LOG_ENABLED = "http.accesslog.enabled";
public static final String HTTP_LOG_FORMAT_JSON = "json";

// TraceLog and AccessLog configs
public static final BString HTTP_LOG_CONSOLE = StringUtils.fromString("console");
public static final BString HTTP_LOG_FORMAT = StringUtils.fromString("format");
public static final BString HTTP_LOG_ATTRIBUTES = StringUtils.fromString("attributes");
public static final BString HTTP_LOG_FILE_PATH = StringUtils.fromString("path");
public static final BString HTTP_TRACE_LOG_HOST = StringUtils.fromString("host");
public static final BString HTTP_TRACE_LOG_PORT = StringUtils.fromString("port");
public static final BString HTTP_LOGGING_PROTOCOL = StringUtils.fromString("HTTP");

// AccessLog fiend names
public static final String ATTRIBUTE_IP = "ip";
public static final String ATTRIBUTE_DATE_TIME = "date_time";
public static final String ATTRIBUTE_REQUEST_METHOD = "request_method";
public static final String ATTRIBUTE_REQUEST_URI = "request_uri";
public static final String ATTRIBUTE_SCHEME = "scheme";
public static final String ATTRIBUTE_REQUEST = "request";
public static final String ATTRIBUTE_STATUS = "status";
public static final String ATTRIBUTE_REQUEST_BODY_SIZE = "request_body_size";
public static final String ATTRIBUTE_RESPONSE_BODY_SIZE = "response_body_size";
public static final String ATTRIBUTE_REQUEST_TIME = "request_time";
public static final String ATTRIBUTE_HTTP_REFERRER = "http_referrer";
public static final String ATTRIBUTE_HTTP_USER_AGENT = "http_user_agent";
public static final String ATTRIBUTE_HTTP_X_FORWARDED_FOR = "http_x_forwarded_for";

// ResponseCacheControl struct field names
public static final BString RES_CACHE_CONTROL_MUST_REVALIDATE_FIELD = StringUtils.fromString("mustRevalidate");
public static final BString RES_CACHE_CONTROL_NO_CACHE_FIELD = StringUtils.fromString("noCache");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

import io.ballerina.runtime.api.values.BMap;
import io.ballerina.runtime.api.values.BString;
import io.ballerina.stdlib.http.api.logging.accesslog.HttpAccessLogConfig;
import io.ballerina.stdlib.http.api.logging.formatters.HttpAccessLogFormatter;
import io.ballerina.stdlib.http.api.logging.formatters.HttpTraceLogFormatter;
import io.ballerina.stdlib.http.api.logging.formatters.JsonLogFormatter;
Expand Down Expand Up @@ -70,6 +71,7 @@ public HttpLogManager(boolean traceLogConsole, BMap traceLogAdvancedConfig, BMap
this.protocol = protocol.getValue();
this.setHttpTraceLogHandler(traceLogConsole, traceLogAdvancedConfig);
this.setHttpAccessLogHandler(accessLogConfig);
HttpAccessLogConfig.getInstance().initializeHttpAccessLogConfig(accessLogConfig);
}

/**
Expand Down Expand Up @@ -166,5 +168,4 @@ public void setHttpAccessLogHandler(BMap accessLogConfig) {
stdErr.println("ballerina: " + protocol + " access log enabled");
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package io.ballerina.stdlib.http.api.logging.accesslog;

import io.ballerina.runtime.api.values.BArray;
import io.ballerina.runtime.api.values.BMap;
import io.ballerina.runtime.api.values.BString;

import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import static io.ballerina.stdlib.http.api.HttpConstants.ATTRIBUTE_HTTP_REFERRER;
import static io.ballerina.stdlib.http.api.HttpConstants.ATTRIBUTE_HTTP_USER_AGENT;
import static io.ballerina.stdlib.http.api.HttpConstants.ATTRIBUTE_HTTP_X_FORWARDED_FOR;
import static io.ballerina.stdlib.http.api.HttpConstants.HTTP_LOG_ATTRIBUTES;
import static io.ballerina.stdlib.http.api.HttpConstants.HTTP_LOG_FORMAT;
import static io.ballerina.stdlib.http.api.HttpConstants.HTTP_LOG_FORMAT_JSON;

public class HttpAccessLogConfig {

private static final HttpAccessLogConfig instance = new HttpAccessLogConfig();
private static final Set<String> EXCLUDED_ATTRIBUTES = new HashSet<>(List.of(
ATTRIBUTE_HTTP_REFERRER, ATTRIBUTE_HTTP_USER_AGENT, ATTRIBUTE_HTTP_X_FORWARDED_FOR
));
private BMap accessLogConfig;

private HttpAccessLogConfig() {}

public static HttpAccessLogConfig getInstance() {
return instance;
}

public void initializeHttpAccessLogConfig(BMap accessLogConfig) {
this.accessLogConfig = accessLogConfig;
}

public List<String> getCustomHeaders() {
List<String> attributes = getAccessLogAttributes();
if (attributes == null) {
return Collections.emptyList();
}

return attributes.stream()
.filter(attr -> attr.startsWith("http_") && !EXCLUDED_ATTRIBUTES.contains(attr))
.map(attr -> attr.substring(5))
.collect(Collectors.toList());
}

public HttpAccessLogFormat getAccessLogFormat() {
if (accessLogConfig != null) {
BString logFormat = accessLogConfig.getStringValue(HTTP_LOG_FORMAT);
if (logFormat.getValue().equals(HTTP_LOG_FORMAT_JSON)) {
return HttpAccessLogFormat.JSON;
}
}
return HttpAccessLogFormat.FLAT;
}

public List<String> getAccessLogAttributes() {
if (accessLogConfig != null) {
BArray logAttributes = accessLogConfig.getArrayValue(HTTP_LOG_ATTRIBUTES);
if (logAttributes != null) {
return Arrays.stream(logAttributes.getStringArray())
.collect(Collectors.toList());
}
}
return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 LLC. 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 io.ballerina.stdlib.http.api.logging.accesslog;

public enum HttpAccessLogFormat {
FLAT, JSON
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
/*
* Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 LLC. 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 io.ballerina.stdlib.http.api.logging.accesslog;

import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;

public class HttpAccessLogMessage {
private String ip;
private Calendar dateTime;
private String requestMethod;
private String requestUri;
private String scheme;
private int status;
private long requestBodySize;
private long responseBodySize;
private long requestTime;
private String httpReferrer;
private String httpUserAgent;
private String httpXForwardedFor;
private String host;
private int port;
private Map<String, String> customHeaders;

public HttpAccessLogMessage() {
this.customHeaders = new HashMap<>();
}

public HttpAccessLogMessage(String ip, Calendar dateTime, String requestMethod, String requestUri, String scheme,
int status, long responseBodySize, String httpReferrer, String httpUserAgent) {
this.ip = ip;
this.dateTime = dateTime;
this.requestMethod = requestMethod;
this.requestUri = requestUri;
this.scheme = scheme;
this.status = status;
this.responseBodySize = responseBodySize;
this.httpReferrer = httpReferrer;
this.httpUserAgent = httpUserAgent;
this.customHeaders = new HashMap<>();
}

public Calendar getDateTime() {
return dateTime;
}

public void setDateTime(Calendar dateTime) {
this.dateTime = dateTime;
}

public String getIp() {
return ip;
}

public void setIp(String ip) {
this.ip = ip;
}

public String getRequestMethod() {
return requestMethod;
}

public void setRequestMethod(String requestMethod) {
this.requestMethod = requestMethod;
}

public String getRequestUri() {
return requestUri;
}

public void setRequestUri(String requestUri) {
this.requestUri = requestUri;
}

public String getScheme() {
return scheme;
}

public void setScheme(String scheme) {
this.scheme = scheme;
}

public int getStatus() {
return status;
}

public void setStatus(int status) {
this.status = status;
}

public long getRequestBodySize() {
return requestBodySize;
}

public void setRequestBodySize(Long requestBodySize) {
this.requestBodySize = requestBodySize;
}

public long getResponseBodySize() {
return responseBodySize;
}

public void setResponseBodySize(Long responseBodySize) {
this.responseBodySize = responseBodySize;
}

public long getRequestTime() {
return requestTime;
}

public void setRequestTime(Long requestTime) {
this.requestTime = requestTime;
}

public String getHttpUserAgent() {
return httpUserAgent;
}

public String getHttpReferrer() {
return httpReferrer;
}

public void setHttpReferrer(String httpReferrer) {
this.httpReferrer = httpReferrer;
}

public void setHttpUserAgent(String httpUserAgent) {
this.httpUserAgent = httpUserAgent;
}

public String getHttpXForwardedFor() {
return httpXForwardedFor;
}

public void setHttpXForwardedFor(String httpXForwardedFor) {
this.httpXForwardedFor = httpXForwardedFor;
}

public String getHost() {
return this.host;
}

public void setHost(String host) {
this.host = host;
}

public int getPort() {
return this.port;
}

public void setPort(int port) {
this.port = port;
}

public Map<String, String> getCustomHeaders() {
return customHeaders;
}

public void setCustomHeaders(Map<String, String> customHeaders) {
this.customHeaders = customHeaders;
}

public void putCustomHeader(String headerKey, String headerValue) {
this.customHeaders.put(headerKey, headerValue);
}
}
Loading

0 comments on commit a76a2c5

Please sign in to comment.