Skip to content

Instantly share code, notes, and snippets.

@nathanleclaire
Created March 10, 2020 21:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nathanleclaire/c2faa9c90a0a717fadbce0544ebba573 to your computer and use it in GitHub Desktop.
Save nathanleclaire/c2faa9c90a0a717fadbce0544ebba573 to your computer and use it in GitHub Desktop.
Generic filter with context injection - Honeycomb Java Beeline implementation
package com.mycompany.config.filters;
import io.honeycomb.libhoney.ResponseObserver;
import io.honeycomb.libhoney.responses.ClientRejected;
import io.honeycomb.libhoney.responses.ServerAccepted;
import io.honeycomb.libhoney.responses.ServerRejected;
import io.honeycomb.libhoney.responses.Unknown;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
public class DebugResponseObserver implements ResponseObserver {
private static final Logger LOG = LoggerFactory.getLogger(DebugResponseObserver.class);
protected static final String ERROR_TEMPLATE_401 = "Server responded with a 401 HTTP error code to a batch request." +
" This is likely caused by using an incorrect 'Team Write Key'. Check https://ui.honeycomb.io/account to verify your " +
"team write key. Rejected event: {}";
@Override
public void onServerAccepted(final ServerAccepted serverAccepted) {
LOG.trace("Event successfully sent to Honeycomb: {}", serverAccepted);
}
@Override
public void onServerRejected(final ServerRejected serverRejected) {
if (serverRejected.getBatchData().getBatchStatusCode() == HttpStatus.UNAUTHORIZED.value()) {
handle401(serverRejected);
} else {
LOG.debug("Event rejected by Honeycomb server: {}", serverRejected);
}
}
@Override
public void onClientRejected(final ClientRejected clientRejected) {
LOG.debug("Event rejected on the client side: {}", clientRejected);
}
@Override
public void onUnknown(final Unknown unknown) {
LOG.debug("Received an unknown error while trying to send Event to Honeycomb: {}", unknown);
}
protected void handle401(final ServerRejected serverRejected) {
LOG.debug(ERROR_TEMPLATE_401, serverRejected);
}
}
package com.mycompany.config.filters;
import io.honeycomb.beeline.DefaultBeeline;
import javax.servlet.http.HttpServletRequest;
import io.honeycomb.beeline.tracing.Beeline;
import io.honeycomb.beeline.tracing.Span;
import io.honeycomb.beeline.tracing.SpanBuilderFactory;
import io.honeycomb.beeline.tracing.SpanPostProcessor;
import io.honeycomb.beeline.tracing.Tracer;
import io.honeycomb.beeline.tracing.Tracing;
import io.honeycomb.beeline.tracing.sampling.Sampling;
import io.honeycomb.libhoney.HoneyClient;
import io.honeycomb.libhoney.LibHoney;
import io.honeycomb.libhoney.TransportOptions;
import io.honeycomb.libhoney.shaded.org.apache.http.HttpHeaders;
import io.honeycomb.libhoney.shaded.org.apache.http.HttpHost;
import io.honeycomb.libhoney.shaded.org.apache.http.auth.AuthScope;
import io.honeycomb.libhoney.shaded.org.apache.http.auth.Credentials;
import io.honeycomb.libhoney.shaded.org.apache.http.auth.UsernamePasswordCredentials;
import io.honeycomb.libhoney.shaded.org.apache.http.client.CredentialsProvider;
import io.honeycomb.libhoney.shaded.org.apache.http.impl.client.BasicCredentialsProvider;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
public class HoneycombFilter implements Filter {
protected ServletContext servletContext;
Beeline beeline;
final String DATASET = "traces";
final String WRITE_KEY = "$WRITEKEY";
@Override
public void init(FilterConfig filterConfig) {
servletContext = filterConfig.getServletContext();
HttpHost proxy = HttpHost.create("http://proxy.com:80");
BasicCredentialsProvider basicCredentialsProvider = new BasicCredentialsProvider();
AuthScope authScope = new AuthScope(proxy);
UsernamePasswordCredentials usernamePassword = new UsernamePasswordCredentials("proxy_username", "proxy_pass");
basicCredentialsProvider.setCredentials(authScope, usernamePassword);
TransportOptions transportOptions = LibHoney.transportOptions().setProxy(proxy).setCredentialsProvider(basicCredentialsProvider).build();
HoneyClient client = LibHoney.create(LibHoney.options().setDataset(DATASET).setWriteKey(WRITE_KEY).build(), transportOptions);
client.addResponseObserver(new DebugResponseObserver());
SpanPostProcessor postProcessor = Tracing.createSpanProcessor(client, Sampling.alwaysSampler());
SpanBuilderFactory factory = Tracing.createSpanBuilderFactory(postProcessor, Sampling.alwaysSampler());
Tracer tracer = Tracing.createTracer(factory);
beeline = Tracing.createBeeline(tracer, factory);
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
final HttpServletRequest httpServletRequest = (HttpServletRequest) request;
final HttpServletResponse httpServletResponse = (HttpServletResponse) response;
SpanBuilderFactory spanBuilderFactory = beeline.getSpanBuilderFactory();
Span rootSpan = spanBuilderFactory.createBuilder()
.setSpanName(httpServletRequest.getServletPath())
.setServiceName("serviceName")
.build();
Tracer tracer = beeline.getTracer();
tracer.startTrace(rootSpan);
rootSpan.addField("request.host", request.getServerName());
rootSpan.addField("request.http_version", request.getProtocol());
rootSpan.addField("request.method", httpServletRequest.getMethod());
rootSpan.addField("request.path", httpServletRequest.getServletPath());
rootSpan.addField("request.query", httpServletRequest.getQueryString());
rootSpan.addField("request.header.accept", httpServletRequest.getHeader(HttpHeaders.ACCEPT));
rootSpan.addField("request.header.user_agent", httpServletRequest.getHeader(HttpHeaders.USER_AGENT));
rootSpan.addField("request.content_length", request.getContentLength()); // FIXME: gets set to -1
try {
request.setAttribute("beeline", beeline);
chain.doFilter(request, response);
rootSpan.addField("response.header_content_type", httpServletResponse.getHeader(HttpHeaders.CONTENT_TYPE));
rootSpan.addField("response.status_code", httpServletResponse.getStatus());
} catch (Exception e) {
e.printStackTrace();
} finally {
rootSpan.close();
beeline.getTracer().endTrace();
}
}
@Override
public void destroy() {
servletContext = null;
}
}
package com.mycompany.config.filters;
import io.honeycomb.beeline.DefaultBeeline;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class HoneycombFilterRegistration {
@Bean
public FilterRegistrationBean honeycomb() {
return new FilterRegistrationBean(new HoneycombFilter());
}
}
@nathanleclaire
Copy link
Author

nathanleclaire commented Mar 11, 2020

code example of custom field/span:

	@GetMapping("/owners")
	public String processFindForm(Owner owner, BindingResult result, Map<String, Object> model,
			HttpServletRequest context) {

		// allow parameterless GET request for /owners to return all records
		if (owner.getLastName() == null) {
			owner.setLastName(""); // empty string signifies broadest possible search
		}

		Beeline beeline = (Beeline) context.getAttribute("beeline");
		Span findOwnersByNameSpan = beeline.startChildSpan("findOwnersByNameSpan");

		final int MYTHREADS = 8;
		ExecutorService executor = Executors.newFixedThreadPool(MYTHREADS);
		executor.execute(beeline.getTracer().traceRunnable("foo", () -> {
			try {
				Thread.sleep(300);
			}
			catch (InterruptedException e) {
				e.printStackTrace();
			}
		}));
		executor.execute(beeline.getTracer().traceRunnable("bar", () -> {
			try {
				Thread.sleep(400);
			}
			catch (InterruptedException e) {
				e.printStackTrace();
			}
		}));

		executor.shutdown();

		try {
			executor.awaitTermination(10, TimeUnit.SECONDS);
		}
		catch (InterruptedException e) {
			e.printStackTrace();
		}

		// find owners by last name
		Collection<Owner> results = this.owners.findByLastName(owner.getLastName());

		findOwnersByNameSpan.close();

		if (results.isEmpty()) {
			// no owners found
			result.rejectValue("lastName", "notFound", "not found");
			return "owners/findOwners";
		}
		else if (results.size() == 1) {
			// 1 owner found
			owner = results.iterator().next();
			return "redirect:/owners/" + owner.getId();
		}
		else {
			// multiple owners found
			model.put("selections", results);
			return "owners/ownersList";
		}
	}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment