Skip to content

Commit

Permalink
Added ObservedValve for Tomcat
Browse files Browse the repository at this point in the history
to have that working we've added additional convention and contexts around javax and jakarta classes
  • Loading branch information
marcingrzejszczak committed Jul 20, 2023
1 parent 8a129bc commit a456e47
Show file tree
Hide file tree
Showing 15 changed files with 1,402 additions and 0 deletions.
1 change: 1 addition & 0 deletions config/checkstyle/checkstyle-suppressions.xml
Expand Up @@ -10,6 +10,7 @@
<suppress checks="IllegalImport" files="test[\\/]java[\\/]io[\\/]micrometer[\\/]core[\\/]instrument[\\/]binder[\\/]jersey[\\/]server.+" />
<suppress checks="IllegalImport" files="test[\\/]java[\\/]io[\\/]micrometer[\\/]jersey.+" />
<suppress checks="IllegalImport" files="test[\\/]java[\\/]io[\\/]micrometer[\\/]core[\\/]instrument.+Tests.java" />
<suppress checks="IllegalImport" files="io[\\/]micrometer[\\/]core[\\/]instrument[\\/]binder[\\/]http[\\/].+" />

<suppress checks="JavadocPackageCheck" files="benchmarks[\\/].+" />
<suppress checks="JavadocPackageCheck" files="samples[\\/].+" />
Expand Down
@@ -0,0 +1,147 @@
/*
* Copyright 2020 VMware, Inc.
*
* 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
*
* https://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.micrometer.core.instrument.binder.http;

import io.micrometer.common.KeyValue;
import io.micrometer.common.KeyValues;
import io.micrometer.common.lang.Nullable;
import io.micrometer.common.util.StringUtils;
import io.micrometer.core.instrument.binder.http.HttpObservationDocumentation.ClientLowCardinalityKeys;
import io.micrometer.core.instrument.binder.http.HttpObservationDocumentation.CommonHighCardinalityKeys;
import io.micrometer.core.instrument.binder.http.HttpObservationDocumentation.CommonLowCardinalityKeys;

import java.net.URI;
import java.util.regex.Pattern;

class AbstractDefaultHttpClientRequestObservationConvention {

private static final Pattern PATTERN_BEFORE_PATH = Pattern.compile("^https?://[^/]+/");

private static final KeyValue URI_NONE = KeyValue.of(CommonLowCardinalityKeys.URI, KeyValue.NONE_VALUE);

private static final KeyValue METHOD_NONE = KeyValue.of(CommonLowCardinalityKeys.METHOD, KeyValue.NONE_VALUE);

private static final KeyValue STATUS_CLIENT_ERROR = KeyValue.of(CommonLowCardinalityKeys.STATUS, "CLIENT_ERROR");

private static final KeyValue HTTP_OUTCOME_SUCCESS = KeyValue.of(CommonLowCardinalityKeys.OUTCOME, "SUCCESS");

private static final KeyValue HTTP_OUTCOME_UNKNOWN = KeyValue.of(CommonLowCardinalityKeys.OUTCOME, "UNKNOWN");

private static final KeyValue CLIENT_NAME_NONE = KeyValue.of(ClientLowCardinalityKeys.CLIENT_NAME,
KeyValue.NONE_VALUE);

private static final KeyValue EXCEPTION_NONE = KeyValue.of(CommonLowCardinalityKeys.EXCEPTION, KeyValue.NONE_VALUE);

private static final KeyValue HTTP_URL_NONE = KeyValue.of(CommonHighCardinalityKeys.HTTP_URL, KeyValue.NONE_VALUE);

private static final KeyValue USER_AGENT_NONE = KeyValue.of(CommonHighCardinalityKeys.USER_AGENT_ORIGINAL,
KeyValue.NONE_VALUE);

protected String getContextualName(String lowercaseMethod) {
return "http " + lowercaseMethod;
}

protected KeyValues getLowCardinalityKeyValues(@Nullable URI uri, @Nullable Throwable throwable,
@Nullable String methodName, @Nullable Integer statusCode, @Nullable String uriPathPattern) {
return KeyValues.of(clientName(uri), exception(throwable), method(methodName), outcome(statusCode),
status(statusCode), uri(uriPathPattern));
}

private KeyValue uri(@Nullable String uriTemplate) {
if (uriTemplate != null) {
return KeyValue.of(CommonLowCardinalityKeys.URI, extractPath(uriTemplate));
}
return URI_NONE;
}

private static String extractPath(String uriTemplate) {
String path = PATTERN_BEFORE_PATH.matcher(uriTemplate).replaceFirst("");
return (path.startsWith("/") ? path : "/" + path);
}

private KeyValue method(@Nullable String methodName) {
if (methodName != null) {
return KeyValue.of(CommonLowCardinalityKeys.METHOD, methodName);
}
else {
return METHOD_NONE;
}
}

private KeyValue status(@Nullable Integer statusCode) {
if (statusCode == null) {
return STATUS_CLIENT_ERROR;
}
return KeyValue.of(CommonLowCardinalityKeys.STATUS, String.valueOf(statusCode));
}

private KeyValue clientName(@Nullable URI uri) {
if (uri != null && uri.getHost() != null) {
return KeyValue.of(ClientLowCardinalityKeys.CLIENT_NAME, uri.getHost());
}
return CLIENT_NAME_NONE;
}

private KeyValue exception(@Nullable Throwable error) {
if (error != null) {
String simpleName = error.getClass().getSimpleName();
return KeyValue.of(CommonLowCardinalityKeys.EXCEPTION,
StringUtils.isNotBlank(simpleName) ? simpleName : error.getClass().getName());
}
return EXCEPTION_NONE;
}

private KeyValue outcome(@Nullable Integer statusCode) {
if (statusCode != null) {
return HttpOutcome.forStatus(statusCode);
}
return HTTP_OUTCOME_UNKNOWN;
}

protected KeyValues getHighCardinalityKeyValues(@Nullable URI uri, @Nullable String userAgent) {
// Make sure that KeyValues entries are already sorted by name for better
// performance
return KeyValues.of(requestUri(uri), userAgent(userAgent));
}

private KeyValue requestUri(@Nullable URI uri) {
if (uri != null) {
return KeyValue.of(CommonHighCardinalityKeys.HTTP_URL, uri.toASCIIString());
}
return HTTP_URL_NONE;
}

private KeyValue userAgent(@Nullable String userAgent) {
if (userAgent != null) {
return KeyValue.of(CommonHighCardinalityKeys.USER_AGENT_ORIGINAL, userAgent);
}
return USER_AGENT_NONE;
}

static class HttpOutcome {

static KeyValue forStatus(int statusCode) {
if (statusCode >= 200 && statusCode < 300) {
return HTTP_OUTCOME_SUCCESS;
}
else {
return HTTP_OUTCOME_UNKNOWN;
}
}

}

}
@@ -0,0 +1,157 @@
/*
* Copyright 2020 VMware, Inc.
*
* 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
*
* https://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.micrometer.core.instrument.binder.http;

import java.net.URI;

import io.micrometer.common.KeyValue;
import io.micrometer.common.KeyValues;
import io.micrometer.common.lang.Nullable;
import io.micrometer.common.util.StringUtils;
import io.micrometer.core.instrument.binder.http.HttpObservationDocumentation.CommonHighCardinalityKeys;
import io.micrometer.core.instrument.binder.http.HttpObservationDocumentation.CommonLowCardinalityKeys;

class AbstractDefaultHttpServerRequestObservationConvention {

protected static final String DEFAULT_NAME = "http.server.requests";

private static final KeyValue METHOD_UNKNOWN = KeyValue.of(CommonLowCardinalityKeys.METHOD, "UNKNOWN");

private static final KeyValue STATUS_UNKNOWN = KeyValue.of(CommonLowCardinalityKeys.STATUS, "UNKNOWN");

private static final KeyValue HTTP_OUTCOME_SUCCESS = KeyValue.of(CommonLowCardinalityKeys.OUTCOME, "SUCCESS");

private static final KeyValue HTTP_OUTCOME_UNKNOWN = KeyValue.of(CommonLowCardinalityKeys.OUTCOME, "UNKNOWN");

private static final KeyValue URI_UNKNOWN = KeyValue.of(CommonLowCardinalityKeys.URI, "UNKNOWN");

private static final KeyValue URI_ROOT = KeyValue.of(CommonLowCardinalityKeys.URI, "root");

private static final KeyValue URI_NOT_FOUND = KeyValue.of(CommonLowCardinalityKeys.URI, "NOT_FOUND");

private static final KeyValue URI_REDIRECTION = KeyValue.of(CommonLowCardinalityKeys.URI, "REDIRECTION");

private static final KeyValue EXCEPTION_NONE = KeyValue.of(CommonLowCardinalityKeys.EXCEPTION, KeyValue.NONE_VALUE);

private static final KeyValue HTTP_URL_UNKNOWN = KeyValue.of(CommonHighCardinalityKeys.HTTP_URL, "UNKNOWN");

protected String getContextualName(String lowercaseHttpMethod, @Nullable String pathPattern) {
if (pathPattern != null) {
return "http " + lowercaseHttpMethod + " " + pathPattern;
}
return "http " + lowercaseHttpMethod;
}

protected KeyValues getLowCardinalityKeyValues(@Nullable Throwable error, @Nullable String method,
@Nullable Integer status, @Nullable String pathPattern, @Nullable String requestUri) {
// Make sure that KeyValues entries are already sorted by name for better
// performance
KeyValues micrometerKeyValues = KeyValues.of(exception(error), method(method), outcome(status), status(status),
uri(pathPattern, status));
if (method == null) {
return micrometerKeyValues;
}
return micrometerKeyValues.and(lowCardinalityKeyValues(method, requestUri, status));
}

private KeyValues lowCardinalityKeyValues(String method, String requestUri, @Nullable Integer responseStatus) {
try {
URI uri = URI.create(requestUri);
KeyValue requestMethod = CommonLowCardinalityKeys.HTTP_REQUEST_METHOD.withValue(method);
KeyValue network = CommonLowCardinalityKeys.NETWORK_PROTOCOL_NAME.withValue("http");
KeyValue serverAddress = CommonLowCardinalityKeys.SERVER_ADDRESS.withValue(uri.getHost());
KeyValue serverPort = CommonLowCardinalityKeys.SERVER_PORT.withValue(String.valueOf(uri.getPort()));
KeyValue urlScheme = CommonLowCardinalityKeys.URL_SCHEME.withValue(String.valueOf(uri.getScheme()));
KeyValues keyValues = KeyValues.of(requestMethod, network, serverAddress, serverPort, urlScheme);
if (responseStatus != null) {
keyValues = keyValues
.and(CommonLowCardinalityKeys.HTTP_RESPONSE_STATUS_CODE.withValue(String.valueOf(responseStatus)));
}
return keyValues;
}
catch (Exception ex) {
return KeyValues.empty();
}
}

protected KeyValues getHighCardinalityKeyValues(String requestUri) {
return KeyValues.of(httpUrl(requestUri));
}

protected KeyValue method(@Nullable String method) {
return (method != null) ? KeyValue.of(CommonLowCardinalityKeys.METHOD, method) : METHOD_UNKNOWN;
}

private KeyValue status(@Nullable Integer status) {
return (status != null) ? KeyValue.of(CommonLowCardinalityKeys.STATUS, Integer.toString(status))
: STATUS_UNKNOWN;
}

private KeyValue uri(@Nullable String pattern, @Nullable Integer status) {
if (pattern != null) {
if (pattern.isEmpty()) {
return URI_ROOT;
}
return KeyValue.of(CommonLowCardinalityKeys.URI, pattern);
}
if (status != null) {
if (status >= 300 && status < 400) {
return URI_REDIRECTION;
}
if (status == 404) {
return URI_NOT_FOUND;
}
}
return URI_UNKNOWN;
}

private KeyValue exception(@Nullable Throwable error) {
if (error != null) {
String simpleName = error.getClass().getSimpleName();
return KeyValue.of(CommonLowCardinalityKeys.EXCEPTION,
StringUtils.isNotBlank(simpleName) ? simpleName : error.getClass().getName());
}
return EXCEPTION_NONE;
}

private KeyValue outcome(@Nullable Integer status) {
if (status != null) {
return HttpOutcome.forStatus(status);
}
return HTTP_OUTCOME_UNKNOWN;
}

private KeyValue httpUrl(@Nullable String requestUri) {
if (requestUri != null) {
return KeyValue.of(CommonHighCardinalityKeys.HTTP_URL, requestUri);
}
return HTTP_URL_UNKNOWN;
}

static class HttpOutcome {

static KeyValue forStatus(int statusCode) {
if (statusCode >= 200 && statusCode < 300) {
return HTTP_OUTCOME_SUCCESS;
}
else {
return HTTP_OUTCOME_UNKNOWN;
}
}

}

}
@@ -0,0 +1,80 @@
/*
* Copyright 2020 VMware, Inc.
*
* 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
*
* https://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.micrometer.core.instrument.binder.http;

import java.net.URI;

import io.micrometer.common.KeyValues;

/**
* Default {@link HttpJakartaServerRequestObservationConvention}.
*
* @author Brian Clozel
* @author Marcin Grzejszczak
* @since 1.12.0
*/
public class DefaultHttpJakartaClientRequestObservationConvention extends
AbstractDefaultHttpClientRequestObservationConvention implements HttpJakartaClientRequestObservationConvention {

private static final String DEFAULT_NAME = "http.client.requests";

private final String name;

/**
* Create a convention with the default name {@code "http.client.requests"}.
*/
public DefaultHttpJakartaClientRequestObservationConvention() {
this(DEFAULT_NAME);
}

/**
* Create a convention with a custom name.
* @param name the observation name
*/
public DefaultHttpJakartaClientRequestObservationConvention(String name) {
this.name = name;
}

@Override
public String getName() {
return this.name;
}

@Override
public String getContextualName(HttpJakartaClientRequestObservationContext context) {
String method = context.getCarrier() != null
? (context.getCarrier().getMethod() != null ? context.getCarrier().getMethod() : null) : null;
return getContextualName(method);
}

@Override
public KeyValues getLowCardinalityKeyValues(HttpJakartaClientRequestObservationContext context) {
URI uri = context.getCarrier() != null ? context.getCarrier().getUriInfo().getRequestUri() : null;
Throwable throwable = context.getError();
String methodName = context.getCarrier() != null ? context.getCarrier().getMethod() : null;
Integer statusCode = context.getResponse() != null ? context.getResponse().getStatus() : null;
String uriPathPattern = context.getUriTemplate();
return getLowCardinalityKeyValues(uri, throwable, methodName, statusCode, uriPathPattern);
}

@Override
public KeyValues getHighCardinalityKeyValues(HttpJakartaClientRequestObservationContext context) {
URI uri = context.getCarrier() != null ? context.getCarrier().getUriInfo().getRequestUri() : null;
String userAgent = context.getCarrier() != null ? context.getCarrier().getHeaderString("User-Agent") : null;
return getHighCardinalityKeyValues(uri, userAgent);
}

}

0 comments on commit a456e47

Please sign in to comment.