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 83 @GuardedBy("monitor") 84 @Nullable 85 private static HttpServer server; 86 87 /** 88 * Returns a {@code ZPageHandler} for tracing debug. The page displays information about all 89 * active spans and all sampled spans based on latency and errors. 90 * 91 * <p>It prints a summary table which contains one row for each span name and data about number of 92 * active and sampled spans. 93 * 94 * <p>If no sampled spans based on latency and error codes are available for a given name, make 95 * sure that the span name is registered to the {@code SampledSpanStore}. 96 * 97 * @return a {@code ZPageHandler} for tracing debug. 98 * @since 0.6 99 */ getTracezZPageHandler()100 public static ZPageHandler getTracezZPageHandler() { 101 return tracezZPageHandler; 102 } 103 104 /** 105 * Returns a {@code ZPageHandler} for tracing config. The page displays information about all 106 * active configuration and allow changing the active configuration. 107 * 108 * @return a {@code ZPageHandler} for tracing config. 109 * @since 0.6 110 */ getTraceConfigzZPageHandler()111 public static ZPageHandler getTraceConfigzZPageHandler() { 112 return traceConfigzZPageHandler; 113 } 114 115 /** 116 * Returns a {@code ZPageHandler} for gRPC stats. 117 * 118 * <p>It prints a summary table which contains rows for each gRPC method. 119 * 120 * @return a {@code ZPageHandler} for gRPC stats. 121 * @since 0.12.0 122 */ getRpczZpageHandler()123 public static ZPageHandler getRpczZpageHandler() { 124 return rpczZpageHandler; 125 } 126 127 /** 128 * Returns a {@code ZPageHandler} for all registered {@link View}s and {@link Measure}s. 129 * 130 * <p>Only {@code Cumulative} views are exported. {@link View}s are grouped by directories. 131 * 132 * @return a {@code ZPageHandler} for all registered {@code View}s and {@code Measure}s. 133 * @since 0.12.0 134 */ getStatszZPageHandler()135 public static ZPageHandler getStatszZPageHandler() { 136 return statszZPageHandler; 137 } 138 139 /** 140 * Registers all pages to the given {@code HttpServer}. 141 * 142 * @param server the server that exports the tracez page. 143 * @since 0.6 144 */ registerAllToHttpServer(HttpServer server)145 public static void registerAllToHttpServer(HttpServer server) { 146 server.createContext(tracezZPageHandler.getUrlPath(), new ZPageHttpHandler(tracezZPageHandler)); 147 server.createContext( 148 traceConfigzZPageHandler.getUrlPath(), new ZPageHttpHandler(traceConfigzZPageHandler)); 149 server.createContext(rpczZpageHandler.getUrlPath(), new ZPageHttpHandler(rpczZpageHandler)); 150 server.createContext(statszZPageHandler.getUrlPath(), new ZPageHttpHandler(statszZPageHandler)); 151 } 152 153 /** 154 * Starts an {@code HttpServer} and registers all pages to it. When the JVM shuts down the server 155 * is stopped. 156 * 157 * <p>Users must call this function only once per process. 158 * 159 * @param port the port used to bind the {@code HttpServer}. 160 * @throws IllegalStateException if the server is already started. 161 * @throws IOException if the server cannot bind to the requested address. 162 * @since 0.6 163 */ startHttpServerAndRegisterAll(int port)164 public static void startHttpServerAndRegisterAll(int port) throws IOException { 165 synchronized (monitor) { 166 checkState(server == null, "The HttpServer is already started."); 167 server = HttpServer.create(new InetSocketAddress(port), BACKLOG); 168 ZPageHandlers.registerAllToHttpServer(server); 169 server.start(); 170 logger.fine("HttpServer started on address " + server.getAddress().toString()); 171 } 172 173 // This does not need to be mutex protected because it is guaranteed that only one thread will 174 // get ever here. 175 Runtime.getRuntime() 176 .addShutdownHook( 177 new Thread() { 178 @Override 179 public void run() { 180 // Use stderr here since the logger may have been reset by its JVM shutdown hook. 181 logger.fine("*** Shutting down gRPC server (JVM shutting down)"); 182 ZPageHandlers.stop(); 183 logger.fine("*** Server shut down"); 184 } 185 }); 186 } 187 stop()188 private static void stop() { 189 synchronized (monitor) { 190 // This should never happen because we register the shutdown hook only if we start the server. 191 if (server == null) { 192 throw new IllegalStateException("The HttpServer is already stopped."); 193 } 194 server.stop(STOP_DELAY); 195 server = null; 196 } 197 } 198 ZPageHandlers()199 private ZPageHandlers() {} 200 } 201