Skip to content

Commit 904c85b

Browse files
authored
Merge pull request #200 from atoulme/add_opentelemetry
Add opentelemetry
2 parents 09a791c + 14da325 commit 904c85b

19 files changed

+1067
-63
lines changed

fabric-chaincode-shim/build.gradle

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,22 @@ dependencies {
5050
implementation group: 'com.google.protobuf', name: 'protobuf-java-util', version: '3.11.1'
5151
// Required if using Java 11+ as no longer bundled in the core libraries
5252
testImplementation group: 'javax.xml.bind', name: 'jaxb-api', version: '2.3.1'
53-
implementation 'io.grpc:grpc-netty-shaded:1.31.1'
54-
implementation 'io.grpc:grpc-protobuf:1.31.1'
55-
implementation 'io.grpc:grpc-stub:1.31.1'
53+
implementation 'io.grpc:grpc-netty-shaded:1.34.1'
54+
implementation 'io.grpc:grpc-protobuf:1.34.1'
55+
implementation 'io.grpc:grpc-stub:1.34.1'
56+
testImplementation 'org.apache.logging.log4j:log4j-slf4j-impl:2.14.0'
57+
58+
59+
implementation platform("io.opentelemetry:opentelemetry-bom:1.6.0")
60+
61+
implementation "io.opentelemetry:opentelemetry-api"
62+
implementation "io.opentelemetry:opentelemetry-proto:1.6.0-alpha"
63+
implementation "io.opentelemetry:opentelemetry-sdk"
64+
implementation "io.opentelemetry:opentelemetry-sdk-extension-autoconfigure:1.6.0-alpha"
65+
implementation "io.opentelemetry:opentelemetry-sdk-trace"
66+
implementation 'io.opentelemetry:opentelemetry-exporter-otlp'
67+
implementation 'io.opentelemetry:opentelemetry-extension-trace-propagators:1.6.0'
68+
implementation "io.opentelemetry.instrumentation:opentelemetry-grpc-1.6:1.5.3-alpha"
5669
}
5770

5871
dependencyCheck {

fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/ContractRouter.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.hyperledger.fabric.shim.ChaincodeServer;
2929
import org.hyperledger.fabric.shim.ChaincodeStub;
3030
import org.hyperledger.fabric.shim.ResponseUtils;
31+
import org.hyperledger.fabric.traces.Traces;
3132

3233
/**
3334
* Router class routes Init/Invoke requests to contracts. Implements
@@ -61,6 +62,7 @@ public ContractRouter(final String[] args) {
6162

6263
final Properties props = super.getChaincodeConfig();
6364
Metrics.initialize(props);
65+
Traces.initialize(props);
6466

6567
logger.fine("ContractRouter<init>");
6668
registry = new RoutingRegistryImpl();

fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/metrics/package-info.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,23 @@
2121
* to the <code>config.props</code> chaincode configuration file.
2222
* See the <a href="../../../../index.html">Overview</a> for details of how to
2323
* configure chaincode.
24+
*
25+
* <p>Open Telemetry</p>
26+
*
27+
* To use Open Telemetry, set the following properties:
28+
*
29+
* <pre>
30+
* CHAINCODE_METRICS_ENABLED=true
31+
* CHAINCODE_METRICS_PROVIDER=org.hyperledger.fabric.metrics.impl.OpenTelemetryMetricsProvider
32+
* </pre>
33+
*
34+
* Additionally, you can set properties after the specification:
35+
* https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/sdk-environment-variables.md
36+
*
37+
* Example:
38+
* <pre>
39+
* OTEL_EXPORTER_OTLP_ENDPOINT=otelcollector:4317
40+
* OTEL_EXPORTER_OTLP_INSECURE=true
41+
* </pre>
2442
*/
2543
package org.hyperledger.fabric.metrics;

fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/overview.html

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,25 @@ <h2>Configuration</h2>
2828
Setting the metrics enabled flag to <code>true</code> will turn on default metrics logging. The <code>TP_</code> values establish the core thread pool size, max thread poolsize, and the number of of tasks that will wait.
2929
</p>
3030

31+
32+
<h3>Open Telemetry</h3>
33+
<p>
34+
To use Open Telemetry, set the following properties:
35+
</p>
36+
<pre>
37+
CHAINCODE_METRICS_ENABLED=true
38+
CHAINCODE_METRICS_PROVIDER=org.hyperledger.fabric.metrics.impl.OpenTelemetryMetricsProvider
39+
</pre>
40+
<p>
41+
Additionally, you can set properties after the <a href="https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/sdk-environment-variables.md">OpenTelemetry specification</a>.<br>
42+
Example:
43+
</p>
44+
<pre>
45+
OTEL_EXPORTER_OTLP_ENDPOINT=otelcollector:4317
46+
OTEL_EXPORTER_OTLP_INSECURE=true
47+
</pre>
48+
49+
3150
<p>
3251
If you are building your chaincode using Gradle or Maven, create a <code>config.props</code> file in the <code>src/main/resources</code> directory and include the settings you want to override.
3352
</p>

fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/shim/ChaincodeBase.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
import io.grpc.netty.shaded.io.grpc.netty.NegotiationType;
4646
import io.grpc.netty.shaded.io.grpc.netty.NettyChannelBuilder;
4747
import io.grpc.netty.shaded.io.netty.handler.ssl.SslContext;
48+
import org.hyperledger.fabric.traces.Traces;
4849

4950
/**
5051
* Abstract implementation of {@link Chaincode}.
@@ -140,6 +141,7 @@ public void start(final String[] args) {
140141

141142
final Properties props = getChaincodeConfig();
142143
Metrics.initialize(props);
144+
Traces.initialize(props);
143145
connectToPeer();
144146
} catch (final Exception e) {
145147
LOGGER.severe(() -> "Chaincode could not start" + Logging.formatError(e));

fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/shim/impl/ChaincodeInvocationTask.java

Lines changed: 61 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
import java.util.function.Consumer;
1515
import java.util.logging.Logger;
1616

17+
import io.opentelemetry.api.trace.Span;
18+
import io.opentelemetry.api.trace.StatusCode;
1719
import org.hyperledger.fabric.Logging;
1820
import org.hyperledger.fabric.protos.peer.ChaincodeShim.ChaincodeMessage;
1921
import org.hyperledger.fabric.protos.peer.ChaincodeShim.ChaincodeMessage.Type;
@@ -22,11 +24,11 @@
2224

2325
import com.google.protobuf.ByteString;
2426
import com.google.protobuf.InvalidProtocolBufferException;
27+
import org.hyperledger.fabric.traces.Traces;
2528

2629
/**
2730
* A 'Callable' implementation the has the job of invoking the chaincode, and
2831
* matching the response and requests.
29-
*
3032
*/
3133
public class ChaincodeInvocationTask implements Callable<ChaincodeMessage> {
3234

@@ -76,55 +78,70 @@ public ChaincodeInvocationTask(final ChaincodeMessage message, final Type type,
7678
public ChaincodeMessage call() {
7779
ChaincodeMessage finalResponseMessage;
7880

81+
Span span = null;
7982
try {
80-
perfLogger.fine(() -> "> task:start TX::" + this.txId);
81-
82-
// A key interface for the chaincode's invoke() method implementation
83-
// is the 'ChaincodeStub' interface. An instance of this is created
84-
// per transaction invocation.
85-
//
86-
// This needs to be passed the message triggering the invoke, as well
87-
// as the interface to be used for sending any requests to the peer
88-
final ChaincodeStub stub = new InvocationStubImpl(message, this);
89-
90-
// result is what will be sent to the peer as a response to this invocation
91-
final Chaincode.Response result;
92-
93-
94-
perfLogger.fine(() -> "> task:invoke TX::" + this.txId);
95-
// Call chaincode's invoke
96-
// Note in Fabric v2, there won't be any INIT
97-
if (this.type.equals(Type.INIT)) {
98-
result = chaincode.init(stub);
99-
} else {
100-
result = chaincode.invoke(stub);
101-
}
102-
103-
perfLogger.fine(() -> "< task:invoke TX::" + this.txId);
104-
105-
if (result.getStatus().getCode() >= Chaincode.Response.Status.INTERNAL_SERVER_ERROR.getCode()) {
106-
// Send ERROR with entire result.Message as payload
107-
logger.severe(() -> String.format("[%-8.8s] Invoke failed with error code %d. Sending %s",
108-
message.getTxid(), result.getStatus().getCode(), ERROR));
83+
try {
84+
perfLogger.fine(() -> "> task:start TX::" + this.txId);
85+
86+
// A key interface for the chaincode's invoke() method implementation
87+
// is the 'ChaincodeStub' interface. An instance of this is created
88+
// per transaction invocation.
89+
//
90+
// This needs to be passed the message triggering the invoke, as well
91+
// as the interface to be used for sending any requests to the peer
92+
final ChaincodeStub stub = new InvocationStubImpl(message, this);
93+
94+
span = Traces.getProvider().createSpan(stub);
95+
// result is what will be sent to the peer as a response to this invocation
96+
final Chaincode.Response result;
97+
98+
99+
perfLogger.fine(() -> "> task:invoke TX::" + this.txId);
100+
101+
// Call chaincode's invoke
102+
// Note in Fabric v2, there won't be any INIT
103+
if (this.type.equals(Type.INIT)) {
104+
result = chaincode.init(stub);
105+
} else {
106+
result = chaincode.invoke(stub);
107+
}
108+
109+
perfLogger.fine(() -> "< task:invoke TX::" + this.txId);
110+
111+
if (result.getStatus().getCode() >= Chaincode.Response.Status.INTERNAL_SERVER_ERROR.getCode()) {
112+
// Send ERROR with entire result.Message as payload
113+
logger.severe(() -> String.format("[%-8.8s] Invoke failed with error code %d. Sending %s",
114+
message.getTxid(), result.getStatus().getCode(), ERROR));
115+
finalResponseMessage = ChaincodeMessageFactory.newErrorEventMessage(message.getChannelId(),
116+
message.getTxid(), result.getMessage(), stub.getEvent());
117+
if (span != null) {
118+
span.setStatus(StatusCode.ERROR, result.getMessage());
119+
}
120+
} else {
121+
// Send COMPLETED with entire result as payload
122+
logger.fine(() -> String.format("[%-8.8s] Invoke succeeded. Sending %s", message.getTxid(), COMPLETED));
123+
finalResponseMessage = ChaincodeMessageFactory.newCompletedEventMessage(message.getChannelId(),
124+
message.getTxid(), result, stub.getEvent());
125+
}
126+
127+
} catch (InvalidProtocolBufferException | RuntimeException e) {
128+
logger.severe(() -> String.format("[%-8.8s] Invoke failed. Sending %s: %s", message.getTxid(), ERROR, e));
109129
finalResponseMessage = ChaincodeMessageFactory.newErrorEventMessage(message.getChannelId(),
110-
message.getTxid(), result.getMessage(), stub.getEvent());
111-
} else {
112-
// Send COMPLETED with entire result as payload
113-
logger.fine(() -> String.format("[%-8.8s] Invoke succeeded. Sending %s", message.getTxid(), COMPLETED));
114-
finalResponseMessage = ChaincodeMessageFactory.newCompletedEventMessage(message.getChannelId(),
115-
message.getTxid(), result, stub.getEvent());
130+
message.getTxid(), e);
131+
if (span != null) {
132+
span.setStatus(StatusCode.ERROR, e.getMessage());
133+
}
116134
}
117135

118-
} catch (InvalidProtocolBufferException | RuntimeException e) {
119-
logger.severe(() -> String.format("[%-8.8s] Invoke failed. Sending %s: %s", message.getTxid(), ERROR, e));
120-
finalResponseMessage = ChaincodeMessageFactory.newErrorEventMessage(message.getChannelId(),
121-
message.getTxid(), e);
136+
// send the final response message to the peer
137+
outgoingMessageConsumer.accept(finalResponseMessage);
138+
perfLogger.fine(() -> "< task:end TX::" + this.txId);
139+
} finally {
140+
if (span != null) {
141+
span.end();
142+
}
122143
}
123144

124-
// send the final response message to the peer
125-
outgoingMessageConsumer.accept(finalResponseMessage);
126-
perfLogger.fine(() -> "< task:end TX::" + this.txId);
127-
128145
return null;
129146
}
130147

fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/shim/impl/ChaincodeSupportClient.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import java.util.function.Consumer;
1212
import java.util.logging.Logger;
1313

14+
import io.grpc.ClientInterceptor;
1415
import org.hyperledger.fabric.Logging;
1516
import org.hyperledger.fabric.protos.peer.ChaincodeShim.ChaincodeMessage;
1617
import org.hyperledger.fabric.protos.peer.ChaincodeSupportGrpc;
@@ -19,6 +20,7 @@
1920
import io.grpc.ManagedChannel;
2021
import io.grpc.ManagedChannelBuilder;
2122
import io.grpc.stub.StreamObserver;
23+
import org.hyperledger.fabric.traces.Traces;
2224

2325
public class ChaincodeSupportClient {
2426
private static final int DEFAULT_TIMEOUT = 5;
@@ -31,6 +33,10 @@ public class ChaincodeSupportClient {
3133
* @param channelBuilder
3234
*/
3335
public ChaincodeSupportClient(final ManagedChannelBuilder<?> channelBuilder) {
36+
ClientInterceptor interceptor = Traces.getProvider().createInterceptor();
37+
if (interceptor != null) {
38+
channelBuilder.intercept(interceptor);
39+
}
3440
this.channel = channelBuilder.build();
3541
this.stub = ChaincodeSupportGrpc.newStub(channel);
3642
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/*
2+
* Copyright 2019 IBM All Rights Reserved.
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
package org.hyperledger.fabric.traces;
7+
8+
import org.hyperledger.fabric.traces.impl.DefaultTracesProvider;
9+
import org.hyperledger.fabric.traces.impl.NullProvider;
10+
11+
import java.lang.reflect.InvocationTargetException;
12+
import java.util.Properties;
13+
import java.util.logging.Logger;
14+
15+
/**
16+
* Traces Interface.
17+
*
18+
* Traces setups up the provider in use from the configuration supplied
19+
*
20+
* If not enabled, nothing happens.
21+
*/
22+
public final class Traces {
23+
24+
private static final String CHAINCODE_TRACES_ENABLED = "CHAINCODE_TRACES_ENABLED";
25+
private static final String CHAINCODE_TRACES_PROVIDER = "CHAINCODE_TRACES_PROVIDER";
26+
27+
private static Logger logger = Logger.getLogger(Traces.class.getName());
28+
29+
private static TracesProvider provider;
30+
31+
32+
private Traces() {
33+
34+
}
35+
36+
/**
37+
*
38+
* @param props the configuration of the chaincode
39+
* @return The traces provider
40+
*/
41+
public static TracesProvider initialize(final Properties props) {
42+
if (Boolean.parseBoolean((String) props.get(CHAINCODE_TRACES_ENABLED))) {
43+
try {
44+
logger.info("Traces enabled");
45+
if (props.containsKey(CHAINCODE_TRACES_PROVIDER)) {
46+
final String providerClass = (String) props.get(CHAINCODE_TRACES_PROVIDER);
47+
48+
@SuppressWarnings("unchecked") // it must be this type otherwise an error
49+
final
50+
Class<TracesProvider> clazz = (Class<TracesProvider>) Class.forName(providerClass);
51+
provider = clazz.getConstructor().newInstance();
52+
} else {
53+
logger.info("Using default traces provider");
54+
provider = new DefaultTracesProvider();
55+
}
56+
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException
57+
| NoSuchMethodException | SecurityException e) {
58+
throw new RuntimeException("Unable to start traces", e);
59+
}
60+
} else {
61+
// return a 'null' provider
62+
logger.info("Traces disabled");
63+
provider = new NullProvider();
64+
65+
}
66+
67+
provider.initialize(props);
68+
return provider;
69+
}
70+
71+
/**
72+
*
73+
* @return TracesProvider
74+
*/
75+
public static TracesProvider getProvider() {
76+
if (provider == null) {
77+
throw new IllegalStateException("No provider set, this should have been set");
78+
}
79+
return provider;
80+
}
81+
}

0 commit comments

Comments
 (0)