Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Azure SDK target mapping #2787

Merged
merged 2 commits into from
Dec 13, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ private static String getDependencyName(SpanData span) {
return name;
}

String path = UrlParser.getPathFromUrl(url);
String path = UrlParser.getPath(url);
if (path == null) {
return name;
}
Expand Down Expand Up @@ -289,7 +289,8 @@ private static void setOperationName(
private static void applyHttpClientSpan(
RemoteDependencyTelemetryBuilder telemetryBuilder, Attributes attributes) {

int defaultPort = getDefaultPortForHttpUrl(attributes.get(SemanticAttributes.HTTP_URL));
String httpUrl = attributes.get(SemanticAttributes.HTTP_URL);
int defaultPort = getDefaultPortForHttpUrl(httpUrl);
String target = getTargetOrDefault(attributes, defaultPort, "Http");

telemetryBuilder.setType("Http");
Expand All @@ -300,8 +301,7 @@ private static void applyHttpClientSpan(
telemetryBuilder.setResultCode(Long.toString(httpStatusCode));
}

String url = attributes.get(SemanticAttributes.HTTP_URL);
telemetryBuilder.setData(url);
telemetryBuilder.setData(httpUrl);
}

private static void applyRpcClientSpan(
Expand Down Expand Up @@ -350,6 +350,12 @@ private static String getTargetOrNull(Attributes attributes, int defaultPort) {
Long port = attributes.get(AiSemanticAttributes.NET_SOCK_PEER_PORT);
return getTarget(host, port, defaultPort);
}
String httpUrl = attributes.get(SemanticAttributes.HTTP_URL);
if (httpUrl != null) {
// this is needed for instrumentations which don't yet follow the latest OpenTelemetry
// semantic attributes (in particular Azure SDK instrumentation)
return UrlParser.getTarget(httpUrl);
}
return null;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,107 +1,212 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

// Includes work from:
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package com.azure.monitor.opentelemetry.exporter.implementation.utils;

import reactor.util.annotation.Nullable;

public class UrlParser {

/**
* Returns the "target" (host:port) portion of the url.
*
* <p>Returns {@code null} if the target cannot be extracted from url for any reason.
*/
// TODO remove this unused?
jeanbisutti marked this conversation as resolved.
Show resolved Hide resolved
@Nullable
public static String getTargetFromUrl(String url) {
public static String getAuthority(String url) {

int schemeEndIndex = url.indexOf(':');
if (schemeEndIndex == -1) {
// not a valid url
int schemeEndIndexExclusive = getSchemeEndIndexExclusive(url);
if (schemeEndIndexExclusive == -1) {
// invalid url
return null;
}

int len = url.length();
if (schemeEndIndex + 2 < len
&& url.charAt(schemeEndIndex + 1) == '/'
&& url.charAt(schemeEndIndex + 2) == '/') {
// has authority component
// look for
// '/' - start of path
// '?' or end of string - empty path
int index;
for (index = schemeEndIndex + 3; index < len; index++) {
char c = url.charAt(index);
if (c == '/' || c == '?' || c == '#') {
break;
}
}
String target = url.substring(schemeEndIndex + 3, index);
return target.isEmpty() ? null : target;
} else {
// has no authority
int hostEndIndexExclusive = getHostEndIndexExclusive(url, schemeEndIndexExclusive);
if (hostEndIndexExclusive == schemeEndIndexExclusive) {
// no host (or port)
return null;
}

int portEndIndexExclusive = getPortEndIndexExclusive(url, hostEndIndexExclusive);

return url.substring(schemeEndIndexExclusive, portEndIndexExclusive);
}

@Nullable
public static String getTarget(String url) {

int schemeEndIndexExclusive = getSchemeEndIndexExclusive(url);
if (schemeEndIndexExclusive == -1) {
// invalid url
return null;
}

int hostEndIndexExclusive = getHostEndIndexExclusive(url, schemeEndIndexExclusive);
if (hostEndIndexExclusive == schemeEndIndexExclusive) {
// no host (or port)
return null;
}

if (hostEndIndexExclusive < url.length() && url.charAt(hostEndIndexExclusive) != ':') {
// no port
return url.substring(schemeEndIndexExclusive, hostEndIndexExclusive);
}

int portStartIndex = hostEndIndexExclusive + 1;

int portEndIndexExclusive = getPortEndIndexExclusive(url, portStartIndex);
if (portEndIndexExclusive == portStartIndex) {
// no port
return url.substring(schemeEndIndexExclusive, hostEndIndexExclusive);
}

String port = url.substring(portStartIndex, portEndIndexExclusive);

if ((port.equals("80") && url.startsWith("http://"))
|| (port.equals("443") && url.startsWith("https://"))) {
return url.substring(schemeEndIndexExclusive, hostEndIndexExclusive);
}

return url.substring(schemeEndIndexExclusive, portEndIndexExclusive);
}

@Nullable
public static String getPath(String url) {

int schemeEndIndexExclusive = getSchemeEndIndexExclusive(url);
if (schemeEndIndexExclusive == -1) {
// invalid url
return null;
}

int hostEndIndexExclusive = getHostEndIndexExclusive(url, schemeEndIndexExclusive);
int portEndIndexExclusive = getPortEndIndexExclusive(url, hostEndIndexExclusive);
int pathEndIndexExclusive = getPathEndIndexExclusive(url, portEndIndexExclusive);

return url.substring(portEndIndexExclusive, pathEndIndexExclusive);
}

@Nullable
public static String getHost(String url) {

int hostStartIndex = getSchemeEndIndexExclusive(url);
if (hostStartIndex == -1) {
// invalid url
return null;
}

int hostEndIndexExclusive = getHostEndIndexExclusive(url, hostStartIndex);
if (hostEndIndexExclusive == hostStartIndex) {
// no host
return null;
}

return url.substring(hostStartIndex, hostEndIndexExclusive);
}

/**
* Returns the path portion of the url.
*
* <p>Returns {@code null} if the path cannot be extracted from url for any reason.
*/
@Nullable
public static String getPathFromUrl(String url) {
public static Integer getPort(String url) {

int schemeEndIndexExclusive = getSchemeEndIndexExclusive(url);
if (schemeEndIndexExclusive == -1) {
// invalid url
return null;
}

int hostEndIndexExclusive = getHostEndIndexExclusive(url, schemeEndIndexExclusive);
if (hostEndIndexExclusive == schemeEndIndexExclusive) {
// no host (or port)
return null;
}

if (hostEndIndexExclusive < url.length() && url.charAt(hostEndIndexExclusive) != ':') {
// no port
return null;
}

int portStartIndex = hostEndIndexExclusive + 1;

int portEndIndexExclusive = getPortEndIndexExclusive(url, portStartIndex);
if (portEndIndexExclusive == portStartIndex) {
// no port
return null;
}

return safeParse(url.substring(portStartIndex, portEndIndexExclusive));
}

public static int getSchemeEndIndexExclusive(String url) {

int schemeEndIndex = url.indexOf(':');
if (schemeEndIndex == -1) {
// not a valid url
return null;
// invalid url
return -1;
}

int len = url.length();
if (schemeEndIndex + 2 < len
&& url.charAt(schemeEndIndex + 1) == '/'
&& url.charAt(schemeEndIndex + 2) == '/') {
// has authority component
// look for
// '/' - start of path
// '?' or end of string - empty path
int pathStartIndex = -1;
for (int i = schemeEndIndex + 3; i < len; i++) {
char c = url.charAt(i);
if (c == '/') {
pathStartIndex = i;
break;
} else if (c == '?' || c == '#') {
// empty path
return "";
}
if (len <= schemeEndIndex + 2
|| url.charAt(schemeEndIndex + 1) != '/'
|| url.charAt(schemeEndIndex + 2) != '/') {
// has no authority component
return -1;
}

return schemeEndIndex + 3;
}

public static int getHostEndIndexExclusive(String url, int startIndex) {
// look for the end of the host:
// ':' ==> start of port, or
// '/', '?', '#' ==> start of path
int index;
int len = url.length();
for (index = startIndex; index < len; index++) {
char c = url.charAt(index);
if (c == ':' || c == '/' || c == '?' || c == '#') {
break;
}
if (pathStartIndex == -1) {
// end of the url was reached while scanning for the beginning of the path
// which means the path is empty
return "";
}
return index;
}

public static int getPortEndIndexExclusive(String url, int startIndex) {
// look for the end of the port:
// '/', '?', '#' ==> start of path
int index;
int len = url.length();
for (index = startIndex; index < len; index++) {
char c = url.charAt(index);
if (c == '/' || c == '?' || c == '#') {
break;
}
int pathEndIndex = getPathEndIndex(url, pathStartIndex + 1);
return url.substring(pathStartIndex, pathEndIndex);
} else {
// has no authority, path starts right away
int pathStartIndex = schemeEndIndex + 1;
int pathEndIndex = getPathEndIndex(url, pathStartIndex);
return url.substring(pathStartIndex, pathEndIndex);
}
return index;
}

@Nullable
private static Integer safeParse(String port) {
try {
return Integer.valueOf(port);
} catch (NumberFormatException e) {
return null;
}
}

// returns the ending index of the path component (exclusive)
private static int getPathEndIndex(String url, int startIndex) {
private static int getPathEndIndexExclusive(String url, int startIndex) {
// look for the end of the port:
// '/', '?', '#' ==> start of path
int index;
int len = url.length();
for (int i = startIndex; i < len; i++) {
char c = url.charAt(i);
for (index = startIndex; index < len; index++) {
char c = url.charAt(index);
if (c == '?' || c == '#') {
return i;
break;
}
}
return len;
return index;
}

private UrlParser() {}
Expand Down