Java 应用通过 OpenTelemetry API 实现手动埋点( 七 )

这里我们首先通过请求属性获取到 span context,这里我们添加了两个属性,如果出现了异常则会记录异常信息,最后在 finally 代码块中结束 Span 。
现在我们重新启动容器,当我们访问订单列表后就可以看到 Jaeger UI 中多了一个 GET /api/orders 的 span 了,并且该 span 和前端 frontend 服务的 span 关联起来了 。

Java 应用通过 OpenTelemetry API 实现手动埋点

文章插图
当然这还不够,因为我们的订单列表接口还会去请求 user-service 服务来获取用户信息,还会去请求 catalog-service 服务获取书籍信息,所以我们还需要在这两个请求中也注入我们这里的 span,这样就可以将整个链路串联起来了 。
首先针对 TokenInterceptor 拦截器我们先创建一个子 span,代码如下所示:
// src/main/java/com/youdianzhishi/orderservice/interceptor/TokenInterceptor.javapackage com.youdianzhishi.orderservice.interceptor;// ......@Componentpublic class TokenInterceptor implements HandlerInterceptor {    @Autowired    private WebClient webClient;    @Autowired    private Tracer tracer;    @Override    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {        // 先获取 Span        Span currentSpan = (Span) request.getAttribute("currentSpan");        Context context = Context.current().with(currentSpan);        // 创建新的 Span,作为子 Span        Span span = tracer.spanBuilder("GET /api/userinfo")            .setParent(context).startSpan();        // 将子 Span 设置为当前上下文,相当于切换上下文到子 Span        try (Scope scope = span.makeCurrent()) {            try {                String token = request.getHeader("Authorization");                if (token == null) {                    response.setStatus(HttpStatus.UNAUTHORIZED.value());                    span.addEvent("Token is null").setStatus(StatusCode.ERROR);                    return false;                }                // 从环境变量中获取 userServiceUrl                String userServiceEnv = System.getenv("USER_SERVICE_URL");                String userServiceUrl = userServiceEnv != null ? userServiceEnv : "http://localhost:8080";                User user = webClient.get()                        .uri(userServiceUrl + "/api/userinfo")                        .header(HttpHeaders.AUTHORIZATION, token)                        .retrieve()                        .onStatus(httpStatus -> httpStatus.equals(HttpStatus.UNAUTHORIZED),                                clientResponse -> Mono.error(new RuntimeException("Unauthorized")))                        .onStatus(                                httpStatus -> httpStatus.is4xxClientError()                                        && !httpStatus.equals(HttpStatus.UNAUTHORIZED),                                clientResponse -> Mono.error(new RuntimeException("Other Client Error")))                        .bodyToMono(User.class)                        .block();                if (user != null) {                    request.setAttribute("user", user);                    span.setAttribute("user_id", user.getId());                    return true;                } else {                    response.setStatus(HttpStatus.UNAUTHORIZED.value());                    span.addEvent("User is null").setStatus(StatusCode.ERROR);                    return false;                }            } catch (RuntimeException e) {                span.recordException(e).setStatus(StatusCode.ERROR, e.getMessage());                if (e.getMessage().equals("Unauthorized")) {                    response.setStatus(HttpStatus.UNAUTHORIZED.value());                } else {                    response.setStatus(HttpStatus.BAD_REQUEST.value());                }                return false;            } catch (Exception e) {                span.recordException(e).setStatus(StatusCode.ERROR, e.getMessage());                response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());                return false;            } finally {                request.setAttribute("parentSpan", span);                span.end();            }        }    }}


推荐阅读