Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*-
* Copyright (C) 2022 Vaadin Ltd
*
* This program is available under Vaadin Commercial License and Service Terms.
*
*
* See for the full
* license.
*/
package com.vaadin.extension;
import static com.vaadin.extension.Constants.FLOW_VERSION;
import static com.vaadin.extension.Constants.REQUEST_TYPE;
import static com.vaadin.extension.Constants.SESSION_ID;
import static com.vaadin.flow.server.Constants.VAADIN_MAPPING;
import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.HTTP_HOST;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.HTTP_METHOD;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.HTTP_ROUTE;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.HTTP_SCHEME;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.HTTP_STATUS_CODE;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.HTTP_TARGET;
import io.opentelemetry.context.propagation.TextMapGetter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.HasElement;
import com.vaadin.flow.component.UI;
import com.vaadin.flow.router.RouteConfiguration;
import com.vaadin.flow.server.Version;
import com.vaadin.flow.shared.ApplicationConstants;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.SpanBuilder;
import io.opentelemetry.api.trace.SpanKind;
import io.opentelemetry.api.trace.StatusCode;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import io.opentelemetry.instrumentation.api.instrumenter.LocalRootSpan;
import io.opentelemetry.instrumentation.api.instrumenter.http.HttpRouteHolder;
import io.opentelemetry.instrumentation.api.instrumenter.http.HttpRouteSource;
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
import java.time.Instant;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
public class InstrumentationHelper {
public static final String INSTRUMENTATION_NAME = "com.vaadin.observability.instrumentation";
public static final String INSTRUMENTATION_VERSION = "2.1";
private static final SpanNameGenerator generator = new SpanNameGenerator();
private static final SpanAttributeGenerator attrGet = new SpanAttributeGenerator();
public static final Instrumenter INSTRUMENTER = Instrumenter
. builder(GlobalOpenTelemetry.get(),
INSTRUMENTATION_NAME, generator)
.setInstrumentationVersion(INSTRUMENTATION_VERSION)
.addAttributesExtractor(attrGet)
.buildInstrumenter(InstrumentationRequest::getSpanKind);
private static final TextMapGetter REQUEST_GETTER =
new TextMapGetter<>() {
@Override
public String get(HttpServletRequest carrier,
String key) {
if (carrier == null) {
return null;
}
Enumeration headerNames = carrier.getHeaderNames();
if (headerNames == null) {
return null;
}
while (headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
if (headerName.equals(key)) {
return carrier.getHeader(headerName);
}
}
return null;
}
@Override
public Iterable keys(HttpServletRequest carrier) {
Set set = new HashSet<>();
Enumeration headerNames = carrier.getHeaderNames();
if (headerNames != null) {
while (headerNames.hasMoreElements()) {
set.add(headerNames.nextElement());
}
}
return set;
}
};
public static Tracer getTracer() {
return GlobalOpenTelemetry.getTracer(INSTRUMENTATION_NAME,
INSTRUMENTATION_VERSION);
}
/**
* Creates and starts a new span with the provided name. Also adds common
* attributes provided by Vaadin contexts.
*
* @param spanName
* the name of the span
* @return the new span
*/
public static Span startSpan(String spanName) {
return startSpan(spanName, null);
}
/**
* Creates and starts a new span with the provided name, at the provided
* start timestamp. Also adds common attributes provided by Vaadin contexts.
*
* @param spanName
* the name of the span
* @param startTimestamp
* the start timestamp of the span
* @return the new span
*/
public static Span startSpan(String spanName, Instant startTimestamp) {
SpanBuilder spanBuilder = getTracer().spanBuilder(spanName);
if (startTimestamp != null) {
spanBuilder.setStartTimestamp(startTimestamp);
}
Span span = spanBuilder.startSpan();
Context context = currentContext();
String sessionId = context.get(ContextKeys.SESSION_ID);
if (sessionId != null && !sessionId.isEmpty()) {
span.setAttribute(SESSION_ID, sessionId);
}
return span;
}
/**
* Ends the provided span. If throwable is not null, then the error message
* and stacktrace will be added to the span, and the span status is set to
* {@link StatusCode#ERROR}. If a scope is provided, then the scope will be
* closed as well.
*
* @param span
* the span to end
* @param throwable
* the throwable to record, or null
* @param scope
* the scope to close, or null
*/
public static void endSpan(Span span, Throwable throwable, Scope scope) {
if (scope != null) {
scope.close();
}
if (span != null) {
handleException(span, throwable);
span.end();
}
}
/**
* Creates a server root span from the HTTP servlet request, and returns a
* context containing the created span
*
* @param servletRequest
* the servlet request
* @return the context that was created
*/
public static Context startRootSpan(HttpServletRequest servletRequest) {
Map spanMap = new HashMap<>();
// Add semantic HTTP attributes
spanMap.put(HTTP_SCHEME.getKey(), servletRequest.getScheme());
spanMap.put(HTTP_METHOD.getKey(), servletRequest.getMethod());
spanMap.put(HTTP_HOST.getKey(), servletRequest.getRemoteHost());
String httpTarget = servletRequest.getContextPath()
+ servletRequest.getPathInfo();
String queryString = servletRequest.getQueryString();
if (queryString != null) {
httpTarget += "?" + queryString;
}
spanMap.put(HTTP_TARGET.getKey(), httpTarget);
spanMap.put(HTTP_ROUTE.getKey(), servletRequest.getPathInfo());
String rootSpanName = servletRequest.getPathInfo();
InstrumentationRequest request = new InstrumentationRequest(
rootSpanName, SpanKind.SERVER, spanMap);
Context context = Context.current();
if (servletRequest.getHeader("traceparent") != null) {
context = GlobalOpenTelemetry.get().getPropagators()
.getTextMapPropagator().extract(context, servletRequest,
REQUEST_GETTER);
}
return INSTRUMENTER.start(context, request);
}
/**
* Ends the root span from the specified context.
*
* @param servletResponse
* the response for the current server root span
* @param context
* the context that contains the root span
* @param throwable
* the throwable to record, or null
*/
public static void endRootSpan(HttpServletResponse servletResponse,
Context context, Throwable throwable) {
Span rootSpan = LocalRootSpan.fromContextOrNull(context);
if (rootSpan != null) {
rootSpan.setAttribute(HTTP_STATUS_CODE.getKey(),
servletResponse.getStatus());
if (servletResponse.getStatus() == HttpStatusCode.NOT_FOUND
.getCode()) {
rootSpan.setStatus(StatusCode.ERROR, "Request was not handled");
}
}
INSTRUMENTER.end(context, null, null, throwable);
}
/**
* Enhances the root span with data from a servlet request adds additional
* data to the context, like session ID.
*
* @param servletRequest
* the request
* @return the created context
*/
public static Context enhanceRootSpan(HttpServletRequest servletRequest,
Context context) {
// Set Vaadin specific attributes on root span
Span rootSpan = LocalRootSpan.fromContext(context);
HttpSession session = servletRequest.getSession();
rootSpan.setAttribute(FLOW_VERSION, Version.getFullVersion());
rootSpan.setAttribute(SESSION_ID, session.getId());
rootSpan.setAttribute(REQUEST_TYPE, servletRequest
.getParameter(ApplicationConstants.REQUEST_TYPE_PARAMETER));
// Add session id to context
return context.with(ContextKeys.SESSION_ID, session.getId());
}
/**
* Determines whether a higher-level instrumentation, for example a servlet
* or application server instrumentation, has already created a root server
* span.
*
* @return whether a server root span already exists
*/
public static boolean checkRootSpan() {
// For now assume that a root span will be a server root span
Context currentContext = Context.current();
return LocalRootSpan.fromContextOrNull(currentContext) != null;
}
public static void updateHttpRoute(UI ui) {
Span localRootSpan = LocalRootSpan.fromContextOrNull(Context.current());
if (localRootSpan == null) {
return;
}
Optional routeTemplate = getActiveRouteTemplate(ui);
if (routeTemplate.isPresent()) {
String route = "/" + routeTemplate.get();
// Update root span name to contain the route.
localRootSpan.updateName(route);
localRootSpan.setAttribute(SemanticAttributes.HTTP_ROUTE, route);
// Also update using HttpRouteHolder to prevent subsequent
// instrumentations from overwriting the route.
// HttpRouteSource.NESTED_CONTROLLER is the most specific type of
// route
HttpRouteHolder.updateHttpRoute(Context.current(),
HttpRouteSource.NESTED_CONTROLLER, route);
}
// Update http.target to contain actual path with params
String locationPath = "/"
+ ui.getInternals().getActiveViewLocation().getPath();
localRootSpan.setAttribute(SemanticAttributes.HTTP_TARGET,
locationPath);
}
/**
* Get the route template for the currently active view.
*
* @param ui
* Current UI to get active view path for.
* @return view template if available, else {@link Optional#empty()}
*/
public static Optional getActiveRouteTemplate(UI ui) {
// Update root span name and http.route attribute to contain route
// template
List activeRouterTargetsChain = ui.getInternals()
.getActiveRouterTargetsChain();
if (activeRouterTargetsChain.isEmpty()) {
return Optional.empty();
}
return RouteConfiguration.forSessionScope().getTemplate(
((Component) activeRouterTargetsChain.get(0)).getClass());
}
/**
* Get the route template for the provided location
*
* @param location
* the location for which to get the route
* @return view template if available, else {@link Optional#empty()}
*/
public static Optional getRouteTemplateForLocation(
String location) {
RouteConfiguration routeConfiguration = RouteConfiguration
.forSessionScope();
Optional> route = routeConfiguration
.getRoute(location);
return route.flatMap(routeConfiguration::getTemplate);
}
public static void handleException(Span span, Throwable throwable) {
if (throwable != null) {
// Mark the span as error
span.setStatus(StatusCode.ERROR, throwable.getMessage());
// Add exception as event to the span
span.recordException(throwable);
// Also mark root span as having an error, as several monitoring
// solutions (New Relic, DataDog) only monitor for errors in root /
// server spans
String errorName = throwable.getClass().getCanonicalName() + ": "
+ throwable.getMessage();
final Span root = LocalRootSpan.current();
root.setStatus(StatusCode.ERROR, errorName);
}
}
/**
* Get the file name from the HTTP request.
*
* @param request
* http request to get file name from
* @return file name
*/
public static String getRequestFilename(HttpServletRequest request) {
// http://localhost:8888/context/servlet/folder/file.js
// ->
// /servlet/folder/file.js
//
// http://localhost:8888/context/servlet/VAADIN/folder/file.js
// ->
// /VAADIN/folder/file.js
//
// http://localhost:8888/context/servlet/sw.js
// ->
// /sw.js
if (request.getPathInfo() == null) {
return request.getServletPath();
} else if (request.getPathInfo().startsWith("/" + VAADIN_MAPPING)
|| request.getPathInfo().startsWith("/themes/")
|| request.getPathInfo().startsWith("/sw.js")) {
return request.getPathInfo();
}
return request.getServletPath() + request.getPathInfo();
}
public static boolean isRequestType(HttpServletRequest servletRequest,
String requestType) {
return requestType.equals(servletRequest.getParameter
(ApplicationConstants.REQUEST_TYPE_PARAMETER));
}
}