xref: /aosp_15_r20/external/opencensus-java/contrib/http_util/README.md (revision a24ffb47c3166327784aa05b149974e82e8f71b8)
1*a24ffb47SSadaf Ebrahimi# OpenCensus HTTP Util
2*a24ffb47SSadaf Ebrahimi[![Build Status][travis-image]][travis-url]
3*a24ffb47SSadaf Ebrahimi[![Windows Build Status][appveyor-image]][appveyor-url]
4*a24ffb47SSadaf Ebrahimi[![Maven Central][maven-image]][maven-url]
5*a24ffb47SSadaf Ebrahimi
6*a24ffb47SSadaf EbrahimiThe *OpenCensus HTTP Util for Java* is a collection of utilities for trace instrumentation when
7*a24ffb47SSadaf Ebrahimiworking with HTTP.
8*a24ffb47SSadaf Ebrahimi
9*a24ffb47SSadaf Ebrahimi## Quickstart
10*a24ffb47SSadaf Ebrahimi
11*a24ffb47SSadaf Ebrahimi### Add the dependencies to your project
12*a24ffb47SSadaf Ebrahimi
13*a24ffb47SSadaf EbrahimiFor Maven add to your `pom.xml`:
14*a24ffb47SSadaf Ebrahimi```xml
15*a24ffb47SSadaf Ebrahimi<dependencies>
16*a24ffb47SSadaf Ebrahimi  <dependency>
17*a24ffb47SSadaf Ebrahimi    <groupId>io.opencensus</groupId>
18*a24ffb47SSadaf Ebrahimi    <artifactId>opencensus-api</artifactId>
19*a24ffb47SSadaf Ebrahimi    <version>0.28.3</version>
20*a24ffb47SSadaf Ebrahimi  </dependency>
21*a24ffb47SSadaf Ebrahimi  <dependency>
22*a24ffb47SSadaf Ebrahimi    <groupId>io.opencensus</groupId>
23*a24ffb47SSadaf Ebrahimi    <artifactId>opencensus-contrib-http-util</artifactId>
24*a24ffb47SSadaf Ebrahimi    <version>0.28.3</version>
25*a24ffb47SSadaf Ebrahimi  </dependency>
26*a24ffb47SSadaf Ebrahimi</dependencies>
27*a24ffb47SSadaf Ebrahimi```
28*a24ffb47SSadaf Ebrahimi
29*a24ffb47SSadaf EbrahimiFor Gradle add to your dependencies:
30*a24ffb47SSadaf Ebrahimi```groovy
31*a24ffb47SSadaf Ebrahimicompile 'io.opencensus:opencensus-api:0.28.3'
32*a24ffb47SSadaf Ebrahimicompile 'io.opencensus:opencensus-contrib-http-util:0.28.3'
33*a24ffb47SSadaf Ebrahimi```
34*a24ffb47SSadaf Ebrahimi
35*a24ffb47SSadaf Ebrahimi## Instrumenting HTTP libraries/frameworks
36*a24ffb47SSadaf Ebrahimi
37*a24ffb47SSadaf Ebrahimi### customization for libraries/frameworks
38*a24ffb47SSadaf Ebrahimi
39*a24ffb47SSadaf EbrahimiUsers can implement `HttpExtractor` to customize what information are extracted from the HTTP
40*a24ffb47SSadaf Ebrahimirequest/response entity.
41*a24ffb47SSadaf Ebrahimi
42*a24ffb47SSadaf EbrahimiIf context propagation is enabled, users need to provide framework specific `TextFormat.Setter`
43*a24ffb47SSadaf Ebrahimiand `TextFormat.Getter`. They are used to inject/extract information into/from the `Carrier` of
44*a24ffb47SSadaf Ebrahimithe request. The `Carrier` can be the request itself or other objects, as long as it provides
45*a24ffb47SSadaf Ebrahimifunctionalities of setting/getting HTTP attributes.
46*a24ffb47SSadaf Ebrahimi
47*a24ffb47SSadaf EbrahimiBelow is an example of how the customization for libraries/frameworks should be done:
48*a24ffb47SSadaf Ebrahimi
49*a24ffb47SSadaf Ebrahimi```java
50*a24ffb47SSadaf Ebrahimi// // Http request entity in the library/framework.
51*a24ffb47SSadaf Ebrahimi// public class HttpRequest {
52*a24ffb47SSadaf Ebrahimi//   ...
53*a24ffb47SSadaf Ebrahimi// }
54*a24ffb47SSadaf Ebrahimi//
55*a24ffb47SSadaf Ebrahimi// // Http response entity in the library/framework.
56*a24ffb47SSadaf Ebrahimi// public class HttpResponse {
57*a24ffb47SSadaf Ebrahimi//   ...
58*a24ffb47SSadaf Ebrahimi// }
59*a24ffb47SSadaf Ebrahimi
60*a24ffb47SSadaf Ebrahimi// use the HttpRequest itself as Carrier.
61*a24ffb47SSadaf EbrahimiTextFormat.Setter<HttpRequest> myTextFormatSetter =
62*a24ffb47SSadaf Ebrahimi    new TextFormat.Setter<HttpRequest>() {
63*a24ffb47SSadaf Ebrahimi      @Override
64*a24ffb47SSadaf Ebrahimi      public void put(HttpRequest carrier, String key, String value) {
65*a24ffb47SSadaf Ebrahimi        carrier.setHeader(key, value);
66*a24ffb47SSadaf Ebrahimi      }
67*a24ffb47SSadaf Ebrahimi    };
68*a24ffb47SSadaf EbrahimiTextFormat.Getter<HttpRequest> myTextFormatGetter =
69*a24ffb47SSadaf Ebrahimi    new TextFormat.Getter<HttpRequest>() {
70*a24ffb47SSadaf Ebrahimi      @Override
71*a24ffb47SSadaf Ebrahimi      public String get(HttpRequest carrier, String key) {
72*a24ffb47SSadaf Ebrahimi        return carrier.getHeader(key);
73*a24ffb47SSadaf Ebrahimi      }
74*a24ffb47SSadaf Ebrahimi    };
75*a24ffb47SSadaf EbrahimiHttpExtractor<HttpRequest, HttpResponse> extractor =
76*a24ffb47SSadaf Ebrahimi    new HttpExtractor<HttpRequest, HttpResponse>() {
77*a24ffb47SSadaf Ebrahimi      @Override
78*a24ffb47SSadaf Ebrahimi      public Integer getStatusCode(HttpResponse response) {
79*a24ffb47SSadaf Ebrahimi        return response.getStatusCode();
80*a24ffb47SSadaf Ebrahimi      }
81*a24ffb47SSadaf Ebrahimi
82*a24ffb47SSadaf Ebrahimi      // other methods that need to be overridden
83*a24ffb47SSadaf Ebrahimi      // ...
84*a24ffb47SSadaf Ebrahimi    };
85*a24ffb47SSadaf Ebrahimi```
86*a24ffb47SSadaf Ebrahimi
87*a24ffb47SSadaf Ebrahimi### Client
88*a24ffb47SSadaf Ebrahimi
89*a24ffb47SSadaf EbrahimiUsers can create a `HttpClientHandler` to help instrument client-side HTTP request/response.
90*a24ffb47SSadaf Ebrahimi
91*a24ffb47SSadaf EbrahimiAn example usage of the handler would be:
92*a24ffb47SSadaf Ebrahimi
93*a24ffb47SSadaf Ebrahimi```java
94*a24ffb47SSadaf EbrahimiHttpClientHandler<HttpRequest, HttpResponse, HttpRequest> handler =
95*a24ffb47SSadaf Ebrahimi    new HttpClientHandler<HttpRequest, HttpResponse>(
96*a24ffb47SSadaf Ebrahimi        tracer, extractor, myTextFormat, myTextFormatSetter);
97*a24ffb47SSadaf Ebrahimi
98*a24ffb47SSadaf Ebrahimi// Use #handleStart in client to start a new span.
99*a24ffb47SSadaf Ebrahimi// Use `null` if you want to use current Span as the parent Span.
100*a24ffb47SSadaf EbrahimiHttpRequestContext context = handler.handleStart(null, request, request);
101*a24ffb47SSadaf EbrahimiHttpResponse response = null;
102*a24ffb47SSadaf EbrahimiThrowable error = null;
103*a24ffb47SSadaf Ebrahimitry {
104*a24ffb47SSadaf Ebrahimi  // Do something to send the request, and get response code from the server
105*a24ffb47SSadaf Ebrahimi  response = getResponse(request);
106*a24ffb47SSadaf Ebrahimi
107*a24ffb47SSadaf Ebrahimi  // Optionally, use #handleMessageSent in client to log a SENT event and its size.
108*a24ffb47SSadaf Ebrahimi  handler.handleMessageSent(context, request.getContentLength());
109*a24ffb47SSadaf Ebrahimi
110*a24ffb47SSadaf Ebrahimi  // Optionally, use #handleMessageReceived in client to log a RECEIVED event and message size.
111*a24ffb47SSadaf Ebrahimi  handler.handleMessageReceived(context, response.getContentLength());
112*a24ffb47SSadaf Ebrahimi} catch (Throwable e) {
113*a24ffb47SSadaf Ebrahimi  error = e;
114*a24ffb47SSadaf Ebrahimi} finally {
115*a24ffb47SSadaf Ebrahimi  // Use #handleEnd in client to close the span.
116*a24ffb47SSadaf Ebrahimi  handler.handleEnd(context, request, response, error);
117*a24ffb47SSadaf Ebrahimi}
118*a24ffb47SSadaf Ebrahimi```
119*a24ffb47SSadaf Ebrahimi
120*a24ffb47SSadaf Ebrahimi### Server
121*a24ffb47SSadaf Ebrahimi
122*a24ffb47SSadaf EbrahimiUsers can create a `HttpServerHandler` to help instrument server-side HTTP request/response.
123*a24ffb47SSadaf Ebrahimi
124*a24ffb47SSadaf EbrahimiAn example usage of the handler would be:
125*a24ffb47SSadaf Ebrahimi
126*a24ffb47SSadaf Ebrahimi```java
127*a24ffb47SSadaf EbrahimiHttpServerHandler<HttpRequest, HttpResponse> handler =
128*a24ffb47SSadaf Ebrahimi    new HttpServerHandler<HttpRequest, HttpResponse, HttpRequest>(
129*a24ffb47SSadaf Ebrahimi        tracer, extractor, myTextFormat, myTextFormatGetter,
130*a24ffb47SSadaf Ebrahimi        false /* true if it is public endpoint */);
131*a24ffb47SSadaf Ebrahimi
132*a24ffb47SSadaf Ebrahimi// Use #handleStart in server to start a new span.
133*a24ffb47SSadaf EbrahimiHttpRequestContext context = handler.handleStart(request, request);
134*a24ffb47SSadaf EbrahimiHttpResponse response = constructResponse();
135*a24ffb47SSadaf EbrahimiThrowable error = null;
136*a24ffb47SSadaf Ebrahimitry (Scope scope = tracer.withSpan(handler.getSpanFromContext(context))) {
137*a24ffb47SSadaf Ebrahimi  // Do something to decide whether to serve the request or early exit.
138*a24ffb47SSadaf Ebrahimi  // For example, client may expect a 100 Continue before sending the message body.
139*a24ffb47SSadaf Ebrahimi  if (extractor.getRequestSize(request) > REQUEST_LIMIT) {
140*a24ffb47SSadaf Ebrahimi    response.setStatus(413);
141*a24ffb47SSadaf Ebrahimi  } else {
142*a24ffb47SSadaf Ebrahimi    response.setStatus(100);
143*a24ffb47SSadaf Ebrahimi    String content = request.getContent();
144*a24ffb47SSadaf Ebrahimi
145*a24ffb47SSadaf Ebrahimi    // Optionally, use #handleMessageReceived in server to log a RECEIVED event and its size.
146*a24ffb47SSadaf Ebrahimi    handler.handleMessageReceived(context, request.getContentLength());
147*a24ffb47SSadaf Ebrahimi
148*a24ffb47SSadaf Ebrahimi    // Do something to prepare the response or exception.
149*a24ffb47SSadaf Ebrahimi    response.setStatus(201);
150*a24ffb47SSadaf Ebrahimi    response.write("OK");
151*a24ffb47SSadaf Ebrahimi    response.flush();
152*a24ffb47SSadaf Ebrahimi
153*a24ffb47SSadaf Ebrahimi    // Optionally, use #handleMessageSent in server to log a SENT message event and its message size.
154*a24ffb47SSadaf Ebrahimi    handler.handleMessageSent(context, response.getContentLength());
155*a24ffb47SSadaf Ebrahimi  } catch (Throwable e) {
156*a24ffb47SSadaf Ebrahimi    error = e;
157*a24ffb47SSadaf Ebrahimi  } finally {
158*a24ffb47SSadaf Ebrahimi    // Use #handleEnd in server to close the span.
159*a24ffb47SSadaf Ebrahimi    handler.handleEnd(context, request, response, error);
160*a24ffb47SSadaf Ebrahimi  }
161*a24ffb47SSadaf Ebrahimi}
162*a24ffb47SSadaf Ebrahimi```
163*a24ffb47SSadaf Ebrahimi
164*a24ffb47SSadaf Ebrahimi### handling async calls
165*a24ffb47SSadaf Ebrahimi
166*a24ffb47SSadaf EbrahimiIn asynchronous HTTP calls, message receiving and sending may happen in different
167*a24ffb47SSadaf Ebrahimithreads. Users need to ensure the started span (as well as scope, if any) is
168*a24ffb47SSadaf Ebrahimiclosed or ended no matter the call is successful or not.
169*a24ffb47SSadaf Ebrahimi
170*a24ffb47SSadaf EbrahimiTo do that, store current scope and span somewhere, e.g. the context of the channel,
171*a24ffb47SSadaf Ebrahimiand close them before the channel exits.
172*a24ffb47SSadaf Ebrahimi
173*a24ffb47SSadaf Ebrahimi[travis-image]: https://travis-ci.org/census-instrumentation/opencensus-java.svg?branch=master
174*a24ffb47SSadaf Ebrahimi[travis-url]: https://travis-ci.org/census-instrumentation/opencensus-java
175*a24ffb47SSadaf Ebrahimi[appveyor-image]: https://ci.appveyor.com/api/projects/status/hxthmpkxar4jq4be/branch/master?svg=true
176*a24ffb47SSadaf Ebrahimi[appveyor-url]: https://ci.appveyor.com/project/opencensusjavateam/opencensus-java/branch/master
177*a24ffb47SSadaf Ebrahimi[maven-image]: https://maven-badges.herokuapp.com/maven-central/io.opencensus/opencensus-contrib-grpc-util/badge.svg
178*a24ffb47SSadaf Ebrahimi[maven-url]: https://maven-badges.herokuapp.com/maven-central/io.opencensus/opencensus-contrib-grpc-util
179*a24ffb47SSadaf Ebrahimi[grpc-url]: https://github.com/grpc/grpc-java
180