1 /* 2 * Copyright 2017, OpenCensus Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package io.opencensus.contrib.zpages; 18 19 import static com.google.common.base.Preconditions.checkState; 20 21 import com.sun.net.httpserver.HttpServer; 22 import io.opencensus.stats.Measure; 23 import io.opencensus.stats.Stats; 24 import io.opencensus.stats.View; 25 import io.opencensus.trace.Tracing; 26 import java.io.IOException; 27 import java.net.InetSocketAddress; 28 import java.util.logging.Logger; 29 import javax.annotation.Nullable; 30 import javax.annotation.concurrent.GuardedBy; 31 import javax.annotation.concurrent.ThreadSafe; 32 33 /** 34 * A collection of HTML pages to display stats and trace data and allow library configuration 35 * control. 36 * 37 * <p>Example usage with private {@link HttpServer}: 38 * 39 * <pre>{@code 40 * public class Main { 41 * public static void main(String[] args) throws Exception { 42 * ZPageHandlers.startHttpServerAndRegisterAll(8000); 43 * ... // do work 44 * } 45 * } 46 * }</pre> 47 * 48 * <p>Example usage with shared {@link HttpServer}: 49 * 50 * <pre>{@code 51 * public class Main { 52 * public static void main(String[] args) throws Exception { 53 * HttpServer server = HttpServer.create(new InetSocketAddress(8000), 10); 54 * ZPageHandlers.registerAllToHttpServer(server); 55 * server.start(); 56 * ... // do work 57 * } 58 * } 59 * }</pre> 60 * 61 * @since 0.6 62 */ 63 @ThreadSafe 64 public final class ZPageHandlers { 65 // The HttpServer listening socket backlog (maximum number of queued incoming connections). 66 private static final int BACKLOG = 5; 67 // How many seconds to wait for the HTTP server to stop. 68 private static final int STOP_DELAY = 1; 69 private static final Logger logger = Logger.getLogger(ZPageHandler.class.getName()); 70 private static final ZPageHandler tracezZPageHandler = 71 TracezZPageHandler.create( 72 Tracing.getExportComponent().getRunningSpanStore(), 73 Tracing.getExportComponent().getSampledSpanStore()); 74 private static final ZPageHandler traceConfigzZPageHandler = 75 TraceConfigzZPageHandler.create(Tracing.getTraceConfig()); 76 private static final ZPageHandler rpczZpageHandler = 77 RpczZPageHandler.create(Stats.getViewManager()); 78 private static final ZPageHandler statszZPageHandler = 79 StatszZPageHandler.create(Stats.getViewManager()); 80 81 private static final Object monitor = new Object(); 82 private static volatile boolean isRunningSpanStoreInitialized = false; 83 84 @GuardedBy("monitor") 85 @Nullable 86 private static HttpServer server; 87 88 /** 89 * Returns a {@code ZPageHandler} for tracing debug. The page displays information about all 90 * active spans and all sampled spans based on latency and errors. 91 * 92 * <p>It prints a summary table which contains one row for each span name and data about number of 93 * active and sampled spans. 94 * 95 * <p>If no sampled spans based on latency and error codes are available for a given name, make 96 * sure that the span name is registered to the {@code SampledSpanStore}. 97 * 98 * <p>When this method is called for the first time, {@link 99 * io.opencensus.trace.export.RunningSpanStore} will be enabled automatically. Subsequent calls 100 * won't update {@link io.opencensus.trace.export.RunningSpanStore} again. 101 * 102 * @return a {@code ZPageHandler} for tracing debug. 103 * @since 0.6 104 */ getTracezZPageHandler()105 public static ZPageHandler getTracezZPageHandler() { 106 enableRunningSpanStore(); 107 return tracezZPageHandler; 108 } 109 110 /** 111 * Returns a {@code ZPageHandler} for tracing config. The page displays information about all 112 * active configuration and allow changing the active configuration. 113 * 114 * @return a {@code ZPageHandler} for tracing config. 115 * @since 0.6 116 */ getTraceConfigzZPageHandler()117 public static ZPageHandler getTraceConfigzZPageHandler() { 118 return traceConfigzZPageHandler; 119 } 120 121 /** 122 * Returns a {@code ZPageHandler} for gRPC stats. 123 * 124 * <p>It prints a summary table which contains rows for each gRPC method. 125 * 126 * @return a {@code ZPageHandler} for gRPC stats. 127 * @since 0.12.0 128 */ getRpczZpageHandler()129 public static ZPageHandler getRpczZpageHandler() { 130 return rpczZpageHandler; 131 } 132 133 /** 134 * Returns a {@code ZPageHandler} for all registered {@link View}s and {@link Measure}s. 135 * 136 * <p>Only {@code Cumulative} views are exported. {@link View}s are grouped by directories. 137 * 138 * @return a {@code ZPageHandler} for all registered {@code View}s and {@code Measure}s. 139 * @since 0.12.0 140 */ getStatszZPageHandler()141 public static ZPageHandler getStatszZPageHandler() { 142 return statszZPageHandler; 143 } 144 145 /** 146 * Registers all pages to the given {@code HttpServer}. 147 * 148 * @param server the server that exports the tracez page. 149 * @since 0.6 150 */ registerAllToHttpServer(HttpServer server)151 public static void registerAllToHttpServer(HttpServer server) { 152 server.createContext(tracezZPageHandler.getUrlPath(), new ZPageHttpHandler(tracezZPageHandler)); 153 server.createContext( 154 traceConfigzZPageHandler.getUrlPath(), new ZPageHttpHandler(traceConfigzZPageHandler)); 155 server.createContext(rpczZpageHandler.getUrlPath(), new ZPageHttpHandler(rpczZpageHandler)); 156 server.createContext(statszZPageHandler.getUrlPath(), new ZPageHttpHandler(statszZPageHandler)); 157 } 158 159 /** 160 * Starts an {@code HttpServer} and registers all pages to it. When the JVM shuts down the server 161 * is stopped. 162 * 163 * <p>Users must call this function only once per process. 164 * 165 * @param port the port used to bind the {@code HttpServer}. 166 * @throws IllegalStateException if the server is already started. 167 * @throws IOException if the server cannot bind to the requested address. 168 * @since 0.6 169 */ startHttpServerAndRegisterAll(int port)170 public static void startHttpServerAndRegisterAll(int port) throws IOException { 171 synchronized (monitor) { 172 checkState(server == null, "The HttpServer is already started."); 173 server = HttpServer.create(new InetSocketAddress(port), BACKLOG); 174 ZPageHandlers.registerAllToHttpServer(server); 175 server.start(); 176 logger.fine("HttpServer started on address " + server.getAddress().toString()); 177 } 178 179 // This does not need to be mutex protected because it is guaranteed that only one thread will 180 // get ever here. 181 Runtime.getRuntime() 182 .addShutdownHook( 183 new Thread() { 184 @Override 185 public void run() { 186 // Use stderr here since the logger may have been reset by its JVM shutdown hook. 187 logger.fine("*** Shutting down gRPC server (JVM shutting down)"); 188 ZPageHandlers.stop(); 189 logger.fine("*** Server shut down"); 190 } 191 }); 192 } 193 stop()194 private static void stop() { 195 synchronized (monitor) { 196 // This should never happen because we register the shutdown hook only if we start the server. 197 if (server == null) { 198 throw new IllegalStateException("The HttpServer is already stopped."); 199 } 200 server.stop(STOP_DELAY); 201 server = null; 202 } 203 } 204 205 // Sets the maximum number of elements as Integer.MAX_VALUE to enable RunningSpanStore. 206 // This method will only execute once even if called multiple times. enableRunningSpanStore()207 private static void enableRunningSpanStore() { 208 if (!isRunningSpanStoreInitialized) { 209 synchronized (monitor) { 210 if (isRunningSpanStoreInitialized) { 211 return; // Already initialized, small race 212 } 213 Tracing.getExportComponent().getRunningSpanStore().setMaxNumberOfSpans(Integer.MAX_VALUE); 214 isRunningSpanStoreInitialized = true; 215 } 216 } 217 } 218 ZPageHandlers()219 private ZPageHandlers() {} 220 } 221