001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.client.trace; 019 020import static org.apache.hadoop.hbase.trace.HBaseSemanticAttributes.NET_PEER_NAME; 021import static org.apache.hadoop.hbase.trace.HBaseSemanticAttributes.NET_PEER_PORT; 022import static org.apache.hadoop.hbase.trace.HBaseSemanticAttributes.RPC_METHOD; 023import static org.apache.hadoop.hbase.trace.HBaseSemanticAttributes.RPC_SERVICE; 024import static org.apache.hadoop.hbase.trace.HBaseSemanticAttributes.RPC_SYSTEM; 025 026import io.opentelemetry.api.common.AttributeKey; 027import io.opentelemetry.api.trace.Span; 028import io.opentelemetry.api.trace.SpanBuilder; 029import io.opentelemetry.api.trace.SpanKind; 030import java.util.HashMap; 031import java.util.Map; 032import java.util.function.Supplier; 033import org.apache.hadoop.hbase.net.Address; 034import org.apache.hadoop.hbase.trace.HBaseSemanticAttributes.RpcSystem; 035import org.apache.hadoop.hbase.trace.TraceUtil; 036import org.apache.yetus.audience.InterfaceAudience; 037 038import org.apache.hbase.thirdparty.com.google.protobuf.Descriptors; 039 040/** 041 * Construct {@link Span} instances originating from the client side of an IPC. 042 * @see <a href= 043 * "https://github.com/open-telemetry/opentelemetry-specification/blob/3e380e249f60c3a5f68746f5e84d10195ba41a79/specification/trace/semantic_conventions/rpc.md">Semantic 044 * conventions for RPC spans</a> 045 */ 046@InterfaceAudience.Private 047public class IpcClientSpanBuilder implements Supplier<Span> { 048 049 private String name; 050 private final Map<AttributeKey<?>, Object> attributes = new HashMap<>(); 051 052 @Override 053 public Span get() { 054 return build(); 055 } 056 057 public IpcClientSpanBuilder setMethodDescriptor(final Descriptors.MethodDescriptor md) { 058 final String packageAndService = getRpcPackageAndService(md.getService()); 059 final String method = getRpcName(md); 060 this.name = buildSpanName(packageAndService, method); 061 populateMethodDescriptorAttributes(attributes, md); 062 return this; 063 } 064 065 public IpcClientSpanBuilder setRemoteAddress(final Address remoteAddress) { 066 attributes.put(NET_PEER_NAME, remoteAddress.getHostName()); 067 attributes.put(NET_PEER_PORT, (long) remoteAddress.getPort()); 068 return this; 069 } 070 071 @SuppressWarnings("unchecked") 072 public Span build() { 073 final SpanBuilder builder = TraceUtil.getGlobalTracer().spanBuilder(name) 074 // TODO: what about clients embedded in Master/RegionServer/Gateways/&c? 075 .setSpanKind(SpanKind.CLIENT); 076 attributes.forEach((k, v) -> builder.setAttribute((AttributeKey<? super Object>) k, v)); 077 return builder.startSpan(); 078 } 079 080 /** 081 * Static utility method that performs the primary logic of this builder. It is visible to other 082 * classes in this package so that other builders can use this functionality as a mix-in. 083 * @param attributes the attributes map to be populated. 084 * @param md the source of the RPC attribute values. 085 */ 086 static void populateMethodDescriptorAttributes(final Map<AttributeKey<?>, Object> attributes, 087 final Descriptors.MethodDescriptor md) { 088 final String packageAndService = getRpcPackageAndService(md.getService()); 089 final String method = getRpcName(md); 090 attributes.put(RPC_SYSTEM, RpcSystem.HBASE_RPC.name()); 091 attributes.put(RPC_SERVICE, packageAndService); 092 attributes.put(RPC_METHOD, method); 093 } 094 095 /** 096 * Retrieve the combined {@code $package.$service} value from {@code sd}. 097 */ 098 public static String getRpcPackageAndService(final Descriptors.ServiceDescriptor sd) { 099 // it happens that `getFullName` returns a string in the $package.$service format required by 100 // the otel RPC specification. Use it for now; might have to parse the value in the future. 101 return sd.getFullName(); 102 } 103 104 /** 105 * Retrieve the {@code $method} value from {@code md}. 106 */ 107 public static String getRpcName(final Descriptors.MethodDescriptor md) { 108 return md.getName(); 109 } 110 111 /** 112 * Construct an RPC span name. 113 */ 114 public static String buildSpanName(final String packageAndService, final String method) { 115 return packageAndService + "/" + method; 116 } 117}