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

[#9892] Update agent proxy-user plugin for apache format #9894

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 70 additions & 1 deletion agent-plugins/proxy-user/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,79 @@ pinpoint.config
#### HTTP headers option.
~~~
# User-specified HTTP headers
# e.g. profiler.proxy.http.headers=X-Trace, X-Request
# Supports apache and nginx formats.
# e.g. profiler.proxy.http.headers=X-Trace, X-Request, X-ApacheLB, X-SSL-nginx
profiler.proxy.http.headers=
~~~

### Apache HTTP Server

* [http://httpd.apache.org/docs/2.4/en/mod/mod\_headers.html](http://httpd.apache.org/docs/2.4/en/mod/mod_headers.html)

Add HTTP header.

```text
X-ApacheLB: t=991424704447256 D=3775428
```

e.g.

httpd.conf

```text
<IfModule mod_jk.c>
...
RequestHeader set X-ApacheLB "%t %D"
...
</IfModule>
```

**%t is required value.**

### Nginx

* [http://nginx.org/en/docs/http/ngx\_http\_core\_module.html](http://nginx.org/en/docs/http/ngx_http_core_module.html)
* [http://nginx.org/en/docs/http/ngx\_http\_proxy\_module.html\#proxy\_set\_header](http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_set_header)

Add HTTP header.

```text
X-SSL-nginx: t=1504248328.423 D=0.123
```

e.g.

nginx.conf

```text
...
server {
listen 9080;
server_name localhost;

location / {
...
set $pinpoint_proxy_header "t=$msec D=$request_time";
proxy_set_header X-SSL-nginx $pinpoint_proxy_header;
}
}
...
```

or

```text
http {
...

proxy_set_header X-SSL-nginx t=$msec;

...
}
```

**t=$msec is required value.**

### Mobile app
Add HTTP header.
~~~
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@
import java.util.List;

public class UserRequestParser implements ProxyRequestParser {
static final String PREFIX_RECEIVED = "t=";
static final String PREFIX_DURATION = "D=";

private List<String> headerNameList = Collections.emptyList();

@Override
Expand All @@ -44,61 +47,100 @@ public void init(ProfilerConfig profilerConfig) {
if (profilerConfig == null) {
return;
}
this.headerNameList = profilerConfig.readList(UserRequestConstants.USER_PROXY_HEADER_NAME_LIST);
headerNameList = profilerConfig.readList(UserRequestConstants.USER_PROXY_HEADER_NAME_LIST);
}

@Override
public ProxyRequestHeader parseHeader(String name, String value) {
final ProxyRequestHeaderBuilder header = new ProxyRequestHeaderBuilder();
header.setApp(name);
for (String token : StringUtils.tokenizeToStringList(value, " ")) {
if (token.startsWith("t=")) {
final long receivedTimeMillis = toReceivedTimeMillis(token.substring(2));
if (receivedTimeMillis > 0) {
header.setReceivedTimeMillis(receivedTimeMillis);
header.setValid(true);
} else {
header.setValid(false);
header.setCause("invalid received time");
return header.build();
}
} else if (token.startsWith("D=")) {
final long durationTimeMicroseconds = toDurationTimeMicros(token.substring(2));
if (durationTimeMicroseconds > 0) {
header.setDurationTimeMicroseconds((int) durationTimeMicroseconds);
}

final List<String> tokenList = StringUtils.tokenizeToStringList(value, " ");
final String receivedTimeValue = findValue(tokenList, PREFIX_RECEIVED);
if (receivedTimeValue != null) {
final long receivedTimeMillis = toReceivedTimeMillis(receivedTimeValue);
if (receivedTimeMillis > 0) {
header.setReceivedTimeMillis(receivedTimeMillis);
header.setValid(true);
} else {
header.setValid(false);
header.setCause("invalid received time");
return header.build();
}
} else {
header.setValid(false);
header.setCause("not found received time");
return header.build();
}
final String durationTimeValue = findValue(tokenList, PREFIX_DURATION);
if (durationTimeValue != null) {
final long durationTimeMicroseconds = toDurationTimeMicros(durationTimeValue);
if (durationTimeMicroseconds > 0) {
header.setDurationTimeMicroseconds((int) durationTimeMicroseconds);
}
}

return header.build();
}

public long toReceivedTimeMillis(final String value) {
String findValue(List<String> tokenList, String prefix) {
for (String token : tokenList) {
if (token.startsWith(prefix)) {
return token.substring(prefix.length());
}
}
return null;
}

long toReceivedTimeMillis(final String value) {
if (value == null) {
return 0;
}

final int length = value.length();
if (length < 13) {
return 0;
}

if (length >= 16) {
// apache - microseconds
return NumberUtils.parseLong(value.substring(0, length - 3), 0);
}
final int millisPosition = value.lastIndexOf('.');
if (millisPosition != -1) {
if (length - millisPosition != 4) {
// nginx - seconds.milliseconds
// e.g. 1504230492.763
if (millisPosition < 10 || length - millisPosition != 4) {
// invalid format.
return 0;
}
String strValue = value.substring(0, millisPosition) + value.substring(millisPosition + 1);
final String strValue = value.substring(0, millisPosition) + value.substring(millisPosition + 1);
return NumberUtils.parseLong(strValue, 0);
}else {
return NumberUtils.parseLong(value, 0);
}
// app - milliseconds
return NumberUtils.parseLong(value, 0);
}

public int toDurationTimeMicros(final String value) {
int toDurationTimeMicros(final String value) {
if (value == null) {
return 0;
}

// to microseconds.
return NumberUtils.parseInteger(value, 0);
final int length = value.length();
final int millisPosition = value.lastIndexOf('.');
if (millisPosition != -1) {
// nginx
// e.g. 0.000
if (length - millisPosition != 4) {
// invalid format.
return 0;
}
final String strValue = value.substring(0, millisPosition) + value.substring(millisPosition + 1);
return NumberUtils.parseInteger(strValue, 0) * 1000;
} else {
// apache, app
// to microseconds.
return NumberUtils.parseInteger(value, 0);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,38 @@
import static org.assertj.core.api.Assertions.assertThat;

public class UserRequestParserTest {
static final String HEADER_NAME = "X-LB-SSL";

@Test
public void parse() {
UserRequestParser parser = new UserRequestParser();
String value = "t=1625212448369 D=123";
ProxyRequestHeader proxyHttpHeader = parser.parseHeader("HEADER_NAME", value);
ProxyRequestHeader proxyHttpHeader = parser.parseHeader(HEADER_NAME, value);
assertThat(proxyHttpHeader.isValid()).isTrue();
assertThat(1625212448369L).isEqualTo(proxyHttpHeader.getReceivedTimeMillis());
assertThat(123L).isEqualTo(proxyHttpHeader.getDurationTimeMicroseconds());
assertThat("HEADER_NAME").isEqualTo(proxyHttpHeader.getApp());
assertThat(HEADER_NAME).isEqualTo(proxyHttpHeader.getApp());
assertThat(-1).isEqualTo(proxyHttpHeader.getIdlePercent());
assertThat(-1).isEqualTo(proxyHttpHeader.getBusyPercent());

// apache
final long currentTimeMillis = System.currentTimeMillis();
value = "t=" + currentTimeMillis + "999" + " D=12345";
proxyHttpHeader = parser.parseHeader(HEADER_NAME, value);
assertThat(proxyHttpHeader.isValid()).isTrue();
assertThat(currentTimeMillis).isEqualTo(proxyHttpHeader.getReceivedTimeMillis());
assertThat(12345).isEqualTo(proxyHttpHeader.getDurationTimeMicroseconds());
assertThat(HEADER_NAME).isEqualTo(proxyHttpHeader.getApp());
assertThat(-1).isEqualTo(proxyHttpHeader.getIdlePercent());
assertThat(-1).isEqualTo(proxyHttpHeader.getBusyPercent());

// nginx
value = "t=1504248328.423 D=0.123";
proxyHttpHeader = parser.parseHeader(HEADER_NAME, value);
assertThat(proxyHttpHeader.isValid()).isTrue();
assertThat(1504248328423L).isEqualTo(proxyHttpHeader.getReceivedTimeMillis());
assertThat(123000L).isEqualTo(proxyHttpHeader.getDurationTimeMicroseconds());
assertThat(HEADER_NAME).isEqualTo(proxyHttpHeader.getApp());
assertThat(-1).isEqualTo(proxyHttpHeader.getIdlePercent());
assertThat(-1).isEqualTo(proxyHttpHeader.getBusyPercent());
}
Expand All @@ -40,10 +62,31 @@ public void parse() {
public void parseOnlyReceivedTime() {
UserRequestParser parser = new UserRequestParser();
String value = "t=1625212448369";
ProxyRequestHeader proxyHttpHeader = parser.parseHeader("HEADER_NAME", value);
ProxyRequestHeader proxyHttpHeader = parser.parseHeader(HEADER_NAME, value);
assertThat(proxyHttpHeader.isValid()).isTrue();
assertThat(1625212448369L).isEqualTo(proxyHttpHeader.getReceivedTimeMillis());
assertThat(-1).isEqualTo(proxyHttpHeader.getDurationTimeMicroseconds());
assertThat(HEADER_NAME).isEqualTo(proxyHttpHeader.getApp());
assertThat(-1).isEqualTo(proxyHttpHeader.getIdlePercent());
assertThat(-1).isEqualTo(proxyHttpHeader.getBusyPercent());

// apache
final long currentTimeMillis = System.currentTimeMillis();
value = "t=" + currentTimeMillis + "999";
proxyHttpHeader = parser.parseHeader(HEADER_NAME, value);
assertThat(currentTimeMillis).isEqualTo(proxyHttpHeader.getReceivedTimeMillis());
assertThat(-1).isEqualTo(proxyHttpHeader.getDurationTimeMicroseconds());
assertThat(HEADER_NAME).isEqualTo(proxyHttpHeader.getApp());
assertThat(-1).isEqualTo(proxyHttpHeader.getIdlePercent());
assertThat(-1).isEqualTo(proxyHttpHeader.getBusyPercent());

// nginx
value = "t=1504248328.423";
proxyHttpHeader = parser.parseHeader(HEADER_NAME, value);
assertThat(proxyHttpHeader.isValid()).isTrue();
assertThat(1504248328423L).isEqualTo(proxyHttpHeader.getReceivedTimeMillis());
assertThat(-1).isEqualTo(proxyHttpHeader.getDurationTimeMicroseconds());
assertThat(HEADER_NAME).isEqualTo(proxyHttpHeader.getApp());
assertThat(-1).isEqualTo(proxyHttpHeader.getIdlePercent());
assertThat(-1).isEqualTo(proxyHttpHeader.getBusyPercent());
}
Expand All @@ -52,26 +95,35 @@ public void parseOnlyReceivedTime() {
public void parseNotFoundReceived() {
UserRequestParser parser = new UserRequestParser();
String value = "D=123";
ProxyRequestHeader proxyHttpHeader = parser.parseHeader("HEADER_NAME", value);
ProxyRequestHeader proxyHttpHeader = parser.parseHeader(HEADER_NAME, value);
assertThat(proxyHttpHeader.isValid()).isFalse();
}

@Test
public void parseReceivedSeconds() {
UserRequestParser parser = new UserRequestParser();
String value = "t=1625212448.369";
ProxyRequestHeader proxyHttpHeader = parser.parseHeader("HEADER_NAME", value);
assertThat(1625212448369L).isEqualTo(proxyHttpHeader.getReceivedTimeMillis());
assertThat(-1).isEqualTo(proxyHttpHeader.getDurationTimeMicroseconds());
assertThat(-1).isEqualTo(proxyHttpHeader.getIdlePercent());
assertThat(-1).isEqualTo(proxyHttpHeader.getBusyPercent());
}

@Test
public void parseInvalidReceived() {
UserRequestParser parser = new UserRequestParser();
String value = "t=1625212448:369";
ProxyRequestHeader proxyHttpHeader = parser.parseHeader("HEADER_NAME", value);
ProxyRequestHeader proxyHttpHeader = parser.parseHeader(HEADER_NAME, value);
assertThat(proxyHttpHeader.isValid()).isFalse();

value = "t=alpha-1625212448369";
proxyHttpHeader = parser.parseHeader(HEADER_NAME, value);
assertThat(proxyHttpHeader.isValid()).isFalse();

value = "t=-1625212448369";
proxyHttpHeader = parser.parseHeader(HEADER_NAME, value);
assertThat(proxyHttpHeader.isValid()).isFalse();

value = "t=1000";
proxyHttpHeader = parser.parseHeader(HEADER_NAME, value);
assertThat(proxyHttpHeader.isValid()).isFalse();

value = "t=-16252124483691784578975972957897594795479379";
proxyHttpHeader = parser.parseHeader(HEADER_NAME, value);
assertThat(proxyHttpHeader.isValid()).isFalse();

value = "t=123.456";
proxyHttpHeader = parser.parseHeader(HEADER_NAME, value);
assertThat(proxyHttpHeader.isValid()).isFalse();
}
}
38 changes: 38 additions & 0 deletions agent-testweb/agent-proxy-plugin-testweb/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.navercorp.pinpoint</groupId>
<artifactId>pinpoint-agent-testweb</artifactId>
<version>2.6.0-SNAPSHOT</version>
</parent>

<artifactId>pinpoint-agent-proxy-plugin-testweb</artifactId>

<packaging>jar</packaging>

<properties>
<pinpoint.agent.jvmargument>
${pinpoint.agent.default.jvmargument}
</pinpoint.agent.jvmargument>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>com.navercorp.pinpoint</groupId>
<artifactId>pinpoint-agent-testweb-commons</artifactId>
</dependency>
</dependencies>

</project>
Loading