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