• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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