Skip to content

Commit 30e57eb

Browse files
authored
Add hypertrace config (#82)
* Add hypertrace config Signed-off-by: Pavol Loffay <[email protected]> * Init submodules Signed-off-by: Pavol Loffay <[email protected]>
1 parent fd50a57 commit 30e57eb

File tree

21 files changed

+491
-28
lines changed

21 files changed

+491
-28
lines changed

.circleci/config.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ commands:
88
- run:
99
name: Generate cache key
1010
command: find . -type f -name "*.gradle*" -o -name "gradle-wrapper*" -exec shasum {} + | sort > /tmp/checksum.txt && cat /tmp/checksum.txt
11+
- run: make init-submodules
1112
- restore_cache:
1213
keys:
1314
- v1-dependencies-{{ checksum "/tmp/checksum.txt" }}

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[submodule "javaagent-core/src/main/proto"]
2+
path = javaagent-core/src/main/proto
3+
url = [email protected]:hypertrace/agent-config.git

Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,7 @@ format:
2020
.PHONY: clean
2121
clean:
2222
./gradlew clean
23+
24+
.PHONY: init-submodules
25+
init-submodules:
26+
git submodule update --init --recursive

README.md

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,25 +26,26 @@ make build
2626

2727
The final artifact is in `javaagent/build/libs/hypertrace-agent-<version>-all.jar`
2828

29-
## Run
29+
## Run & Configure
3030

3131
```bash
32-
OTEL_EXPORTER=zipkin java -javaagent:javaagent/build/libs/hypertrace-agent-0.0.1-all.jar -jar app.jar
32+
HT_EXPORTING_ADDRESS=http://localhost:9411/api/v2/spans java -javaagent:javaagent/build/libs/hypertrace-agent-<version>-all.jar -jar app.jar
3333
```
3434

35+
By default the agent uses Zipkin exporter.
36+
3537
The configuration precedence order
3638
1. OpenTelemetry Agent's trace config file `OTEL_TRACE_CONFIG`/`otel.trace.config`
37-
3. OpenTelemetry system properties and env variables
39+
2. OpenTelemetry system properties and env variables
40+
3. Hypertrace configuration with the following precedence order:
41+
1. system properties
42+
2. environment variables, TODO add link to agent-config repo
43+
3. [configuration file](./example-config.yaml), specified `HT_CONFIG_FILE=example-config.yaml`
3844

3945
Hypertrace body/headers capture can be disabled by:
40-
* `HYPERTRACE_INTEGRATION_ALL_ENABLED` - disables capture for all instrumentations
46+
* `HT_DATA_CAPTURE_HTTP_BODY_REQUEST` - disables additional data capture for all instrumentations
4147
* `HYPERTRACE_INTEGRATION_<integration>_ENABLED` - disables capture for a specified instrumentation e.g. `servlet`, `servlet-3`
4248

43-
### Disable request/response body capture
44-
45-
Request and response body capture can be disabled by `-Dotel.integration.body.enabled=false` or
46-
`-Dotel.integration.<instrumentation>-body.enabled=false`.
47-
4849
## Test
4950

5051
Tests use docker via Testcontainers.org.

example-config.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
serviceName: service_name
2+
reporting:
3+
address: http://localhost:9411/api/v2/spans

instrumentation/grpc-1.5/src/test/java/io/opentelemetry/instrumentation/hypertrace/grpc/v1_5/GrpcBodyTest.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@
3434
import java.io.IOException;
3535
import java.util.List;
3636
import java.util.concurrent.TimeoutException;
37-
import org.hypertrace.agent.core.DynamicConfig;
37+
import org.hypertrace.agent.core.EnvironmentConfig;
38+
import org.hypertrace.agent.core.HypertraceConfig;
3839
import org.hypertrace.agent.core.HypertraceSemanticAttributes;
3940
import org.hypertrace.agent.testing.AbstractInstrumenterTest;
4041
import org.hypertrace.example.GreeterGrpc;
@@ -107,7 +108,8 @@ public static void close() {
107108

108109
@AfterEach
109110
public void afterEach() {
110-
System.clearProperty(DynamicConfig.ENABLED_ALL_PROPERTY_NAME);
111+
System.clearProperty(EnvironmentConfig.CAPTURE_HTTP_BODY_PREFIX + "request");
112+
HypertraceConfig.reset();
111113
}
112114

113115
@Test
@@ -168,7 +170,7 @@ public void serverRequestBlocking() throws TimeoutException, InterruptedExceptio
168170
@Test
169171
public void disabledInstrumentation_dynamicConfig()
170172
throws TimeoutException, InterruptedException {
171-
System.setProperty(DynamicConfig.ENABLED_ALL_PROPERTY_NAME, "false");
173+
System.setProperty(EnvironmentConfig.CAPTURE_HTTP_BODY_PREFIX + "request", "false");
172174

173175
GreeterBlockingStub blockingStub = GreeterGrpc.newBlockingStub(CHANNEL);
174176
Response response = blockingStub.sayHello(REQUEST);

javaagent-core/build.gradle.kts

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,33 @@
1+
import com.google.protobuf.gradle.*
2+
13
plugins {
24
`java-library`
5+
idea
6+
id("com.google.protobuf") version "0.8.13"
7+
}
8+
9+
protobuf {
10+
protoc {
11+
// The artifact spec for the Protobuf Compiler
12+
artifact = "com.google.protobuf:protoc:3.13.0"
13+
}
14+
generateProtoTasks {
15+
}
16+
}
17+
18+
idea {
19+
module {
20+
sourceDirs.add(file("${projectDir}/build/generated/source/proto/main/proto"))
21+
}
322
}
423

524
dependencies {
625
implementation("io.opentelemetry:opentelemetry-api:0.9.1")
7-
}
826

27+
api("com.google.protobuf:protobuf-java:3.13.0")
28+
api("com.google.protobuf:protobuf-java-util:3.13.0")
29+
// convert yaml to json, since java protobuf impl supports only json
30+
implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.11.3")
31+
32+
testImplementation("org.junit-pioneer:junit-pioneer:1.0.0")
33+
}

javaagent-core/src/main/java/org/hypertrace/agent/core/DynamicConfig.java

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,25 +20,18 @@ public class DynamicConfig {
2020

2121
private DynamicConfig() {}
2222

23-
public static final String ENABLED_ALL_PROPERTY_NAME = "hypertrace.integration.all.enabled";
24-
2523
public static boolean isEnabled(String[] instrumentationNames) {
26-
String integrationEnabled = getProperty(ENABLED_ALL_PROPERTY_NAME);
27-
if (integrationEnabled != null && "false".equals(integrationEnabled.toLowerCase())) {
24+
if (!HypertraceConfig.get().getDataCapture().getHttpBody().getRequest().getValue()) {
2825
return false;
2926
}
3027

3128
for (String name : instrumentationNames) {
32-
integrationEnabled = getProperty("hypertrace.integration." + name + ".enabled");
29+
String integrationEnabled =
30+
EnvironmentConfig.getProperty("hypertrace.integration." + name + ".enabled");
3331
if (integrationEnabled != null && "false".equals(integrationEnabled.toLowerCase())) {
3432
return false;
3533
}
3634
}
3735
return true;
3836
}
39-
40-
public static String getProperty(String name) {
41-
/** * TODO this is just a mock impl */
42-
return System.getProperty(name, System.getenv(name.replaceAll("\\.", "_").toUpperCase()));
43-
}
4437
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/*
2+
* Copyright The Hypertrace Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.hypertrace.agent.core;
18+
19+
import com.google.protobuf.BoolValue;
20+
import com.google.protobuf.StringValue;
21+
import org.hypertrace.agent.config.Config.AgentConfig;
22+
import org.hypertrace.agent.config.Config.DataCapture;
23+
import org.hypertrace.agent.config.Config.Message;
24+
import org.hypertrace.agent.config.Config.Reporting;
25+
26+
public class EnvironmentConfig {
27+
28+
private EnvironmentConfig() {}
29+
30+
private static final String HT_PREFIX = "ht.";
31+
32+
static final String CONFIG_FILE_PROPERTY = HT_PREFIX + "config.file";
33+
static final String SERVICE_NAME = HT_PREFIX + "service.name";
34+
35+
private static final String REPORTING_PREFIX = HT_PREFIX + "reporting.";
36+
static final String REPORTING_ADDRESS = REPORTING_PREFIX + "address";
37+
static final String REPORTING_SECURE = REPORTING_PREFIX + "secure";
38+
39+
private static final String CAPTURE_PREFIX = HT_PREFIX + "data.capture.";
40+
public static final String CAPTURE_HTTP_HEADERS_PREFIX = CAPTURE_PREFIX + "http.headers.";
41+
public static final String CAPTURE_HTTP_BODY_PREFIX = CAPTURE_PREFIX + "http.body.";
42+
public static final String CAPTURE_RPC_METADATA_PREFIX = CAPTURE_PREFIX + "rpc.metadata.";
43+
public static final String CAPTURE_RPC_BODY_PREFIX = CAPTURE_PREFIX + "rpc.body.";
44+
45+
public static AgentConfig.Builder applyPropertiesAndEnvVars(AgentConfig.Builder builder) {
46+
String serviceName = getProperty(SERVICE_NAME);
47+
if (serviceName != null) {
48+
builder.setServiceName(serviceName);
49+
}
50+
51+
Reporting.Builder reportingBuilder = applyReporting(builder.getReporting().toBuilder());
52+
builder.setReporting(reportingBuilder);
53+
54+
DataCapture.Builder dataCaptureBuilder =
55+
setDefaultsToDataCapture(builder.getDataCapture().toBuilder());
56+
builder.setDataCapture(dataCaptureBuilder);
57+
return builder;
58+
}
59+
60+
private static Reporting.Builder applyReporting(Reporting.Builder builder) {
61+
String reporterAddress = getProperty(REPORTING_ADDRESS);
62+
if (reporterAddress != null) {
63+
builder.setAddress(StringValue.newBuilder().setValue(reporterAddress).build());
64+
}
65+
String secure = getProperty(REPORTING_SECURE);
66+
if (secure != null) {
67+
builder.setSecure(BoolValue.newBuilder().setValue(Boolean.valueOf(secure)).build());
68+
}
69+
70+
return builder;
71+
}
72+
73+
private static DataCapture.Builder setDefaultsToDataCapture(DataCapture.Builder builder) {
74+
builder.setHttpHeaders(
75+
applyMessageDefaults(builder.getHttpHeaders().toBuilder(), CAPTURE_HTTP_HEADERS_PREFIX));
76+
builder.setHttpBody(
77+
applyMessageDefaults(builder.getHttpBody().toBuilder(), CAPTURE_HTTP_BODY_PREFIX));
78+
builder.setRpcMetadata(
79+
applyMessageDefaults(builder.getRpcMetadata().toBuilder(), CAPTURE_RPC_METADATA_PREFIX));
80+
builder.setRpcBody(
81+
applyMessageDefaults(builder.getRpcBody().toBuilder(), CAPTURE_RPC_BODY_PREFIX));
82+
return builder;
83+
}
84+
85+
private static Message.Builder applyMessageDefaults(Message.Builder builder, String prefix) {
86+
String request = getProperty(prefix + "request");
87+
if (request != null) {
88+
builder.setRequest(BoolValue.newBuilder().setValue(Boolean.valueOf(request)).build());
89+
}
90+
String response = getProperty(prefix + "response");
91+
if (request != null) {
92+
builder.setResponse(BoolValue.newBuilder().setValue(Boolean.valueOf(response)).build());
93+
}
94+
return builder;
95+
}
96+
97+
public static String getProperty(String name) {
98+
return System.getProperty(name, System.getenv(name.replaceAll("\\.", "_").toUpperCase()));
99+
}
100+
}
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
/*
2+
* Copyright The Hypertrace Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.hypertrace.agent.core;
18+
19+
import com.fasterxml.jackson.databind.ObjectMapper;
20+
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
21+
import com.google.common.annotations.VisibleForTesting;
22+
import com.google.protobuf.BoolValue;
23+
import com.google.protobuf.StringValue;
24+
import com.google.protobuf.util.JsonFormat;
25+
import java.io.File;
26+
import java.io.FileInputStream;
27+
import java.io.IOException;
28+
import java.io.InputStream;
29+
import org.hypertrace.agent.config.Config.AgentConfig;
30+
import org.hypertrace.agent.config.Config.DataCapture;
31+
import org.hypertrace.agent.config.Config.Message;
32+
import org.hypertrace.agent.config.Config.Reporting;
33+
34+
/** {@link HypertraceConfig} loads a yaml config from file. */
35+
public class HypertraceConfig {
36+
37+
private HypertraceConfig() {}
38+
39+
private static AgentConfig agentConfig;
40+
41+
static final String DEFAULT_REPORTING_ADDRESS = "http://localhost:9411/api/v2/spans";
42+
static final String DEFAULT_SERVICE_NAME = "default_service_name";
43+
44+
public static AgentConfig get() {
45+
if (agentConfig == null) {
46+
synchronized (HypertraceConfig.class) {
47+
if (agentConfig == null) {
48+
try {
49+
agentConfig = load();
50+
} catch (IOException e) {
51+
throw new RuntimeException("Could not load config", e);
52+
}
53+
}
54+
}
55+
}
56+
return agentConfig;
57+
}
58+
59+
/** Reset the config, use only in tests. */
60+
public static void reset() {
61+
agentConfig = null;
62+
}
63+
64+
private static AgentConfig load() throws IOException {
65+
String configFile = EnvironmentConfig.getProperty(EnvironmentConfig.CONFIG_FILE_PROPERTY);
66+
if (configFile == null) {
67+
return EnvironmentConfig.applyPropertiesAndEnvVars(applyDefaults(AgentConfig.newBuilder()))
68+
.build();
69+
}
70+
return load(configFile);
71+
}
72+
73+
@VisibleForTesting
74+
static AgentConfig load(String filename) throws IOException {
75+
File configFile = new File(filename);
76+
if (!configFile.exists() || configFile.isDirectory() || !configFile.canRead()) {
77+
throw new IllegalArgumentException(
78+
String.format("Config file %s either does not exist or cannot be read", configFile));
79+
}
80+
81+
InputStream fileInputStream = new FileInputStream(configFile);
82+
String json = convertYamlToJson(fileInputStream);
83+
84+
AgentConfig.Builder configBuilder = AgentConfig.newBuilder();
85+
JsonFormat.parser().ignoringUnknownFields().merge(json, configBuilder);
86+
87+
return EnvironmentConfig.applyPropertiesAndEnvVars(applyDefaults(configBuilder)).build();
88+
}
89+
90+
private static AgentConfig.Builder applyDefaults(AgentConfig.Builder configBuilder) {
91+
if (configBuilder.getServiceName().isEmpty()) {
92+
configBuilder.setServiceName(DEFAULT_SERVICE_NAME);
93+
}
94+
95+
Reporting.Builder reportingBuilder =
96+
applyReportingDefaults(configBuilder.getReporting().toBuilder());
97+
configBuilder.setReporting(reportingBuilder);
98+
99+
DataCapture.Builder dataCaptureBuilder =
100+
setDefaultsToDataCapture(configBuilder.getDataCapture().toBuilder());
101+
configBuilder.setDataCapture(dataCaptureBuilder);
102+
return configBuilder;
103+
}
104+
105+
private static Reporting.Builder applyReportingDefaults(Reporting.Builder builder) {
106+
if (!builder.hasAddress()) {
107+
builder.setAddress(StringValue.newBuilder().setValue(DEFAULT_REPORTING_ADDRESS).build());
108+
}
109+
return builder;
110+
}
111+
112+
private static DataCapture.Builder setDefaultsToDataCapture(DataCapture.Builder builder) {
113+
builder.setHttpHeaders(applyMessageDefaults(builder.getHttpHeaders().toBuilder()));
114+
builder.setHttpBody(applyMessageDefaults(builder.getHttpBody().toBuilder()));
115+
builder.setRpcMetadata(applyMessageDefaults(builder.getRpcMetadata().toBuilder()));
116+
builder.setRpcBody(applyMessageDefaults(builder.getRpcBody().toBuilder()));
117+
return builder;
118+
}
119+
120+
private static Message.Builder applyMessageDefaults(Message.Builder builder) {
121+
if (!builder.hasRequest()) {
122+
builder.setRequest(BoolValue.newBuilder().setValue(true).build());
123+
}
124+
if (!builder.hasResponse()) {
125+
builder.setResponse(BoolValue.newBuilder().setValue(true).build());
126+
}
127+
return builder;
128+
}
129+
130+
private static String convertYamlToJson(InputStream yaml) throws IOException {
131+
ObjectMapper yamlReader = new ObjectMapper(new YAMLFactory());
132+
Object obj = yamlReader.readValue(yaml, Object.class);
133+
134+
ObjectMapper jsonWriter = new ObjectMapper();
135+
return jsonWriter.writeValueAsString(obj);
136+
}
137+
}

0 commit comments

Comments
 (0)