GraphLab: Distributed Graph-Parallel API  2.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
metrics_server.cpp
1 /*
2  * Copyright (c) 2009 Carnegie Mellon University.
3  * All rights reserved.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing,
12  * software distributed under the License is distributed on an "AS
13  * IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
14  * express or implied. See the License for the specific language
15  * governing permissions and limitations under the License.
16  *
17  * For more about this software visit:
18  *
19  * http://www.graphlab.ml.cmu.edu
20  *
21  */
22 
23 #include <unistd.h>
24 #include <string>
25 #include <map>
26 #include <utility>
27 #include <sstream>
28 #include <boost/function.hpp>
29 
30 #include <graphlab/util/stl_util.hpp>
31 #include <graphlab/parallel/pthread_tools.hpp>
32 #include <graphlab/rpc/distributed_event_log.hpp>
33 #include <graphlab/rpc/get_last_dc_procid.hpp>
34 
35 #include <graphlab/ui/mongoose/mongoose.h>
36 #include <graphlab/ui/metrics_server.hpp>
37 
38 #include <graphlab/macros_def.hpp>
39 
40 
41 namespace graphlab {
42 
43 
44 static mg_context* metric_context = NULL;
45 
46 static rwlock& callback_lock() {
47  static rwlock clock;
48  return clock;
49 }
50 
51 
52 static std::map<std::string, http_redirect_callback_type>& callbacks() {
53  static std::map<std::string, http_redirect_callback_type> cback;
54  return cback;
55 }
56 
57 
58 
59 
60 static void* process_request(enum mg_event event,
61  struct mg_connection* conn,
62  const struct mg_request_info* info) {
63  if (event == MG_NEW_REQUEST) {
64 
65  // get the URL being requested
66  std::string url;
67  if (info->uri != NULL) url = info->uri;
68  // strip the starting /
69  if (url.length() >= 1) url = url.substr(1, url.length() - 1);
70  // get all the variables
71  std::map<std::string, std::string> variable_map;
72  if (info->query_string != NULL) {
73  std::string qs = info->query_string;
74  std::vector<std::string> terms = strsplit(qs, "&", true);
75  // now for each term..
76  foreach(std::string& term, terms) {
77  // get the variable name
78  std::vector<std::string> key_val = strsplit(term, "=", true);
79  if (key_val.size() > 0) {
80  // use mg_get_var to read the actual variable.
81  // since mg_get_var does http escape sequence decoding
82  std::string key = key_val[0];
83  char val_target[8192];
84  int ret = mg_get_var(qs.c_str(), qs.length(),
85  key.c_str(), val_target, 8192);
86  if (ret >= 0) variable_map[key] = val_target;
87  }
88  }
89  }
90  callback_lock().readlock();
91  // now redirect to the callback handlers. if we find one
92  std::map<std::string, http_redirect_callback_type>::iterator iter =
93  callbacks().find(url);
94 
95  if (iter != callbacks().end()) {
96  std::pair<std::string, std::string> returnval = iter->second(variable_map);
97 
98  callback_lock().rdunlock();
99 
100  std::string ctype = returnval.first;
101  std::string body = returnval.second;
102  mg_printf(conn,
103  "HTTP/1.1 200 OK\r\n"
104  "Access-Control-Allow-Origin: *\r\n"
105  "Access-Control-Allow-Methods: GET\r\n"
106  "Content-Type: %s\r\n"
107  "Content-Length: %d\r\n"
108  "\r\n",
109  ctype.c_str(), (int) body.length());
110  mg_write(conn, body.c_str(), body.length());
111  }
112  else {
113  std::map<std::string, http_redirect_callback_type>::iterator iter404 =
114  callbacks().find("404");
115  std::pair<std::string, std::string> returnval;
116  if (iter404 != callbacks().end()) returnval = iter404->second(variable_map);
117 
118  callback_lock().rdunlock();
119 
120  std::string ctype = returnval.first;
121  std::string body = returnval.second;
122 
123  mg_printf(conn,
124  "HTTP/1.1 404 Not Found\r\n"
125  "Access-Control-Allow-Origin: *\r\n"
126  "Content-Type: %s\r\n"
127  "Content-Length: %d\r\n"
128  "\r\n",
129  ctype.c_str(), (int)body.length());
130  mg_write(conn, body.c_str(), body.length());
131  }
132 
133  return (void*)"";
134  }
135  else {
136  return NULL;
137  }
138 }
139 
140 
141 /*
142  Simple 404 handler. Just reuturns a string "Page Not Found"
143  */
144 std::pair<std::string, std::string>
145 four_oh_four(std::map<std::string, std::string>& varmap) {
146  return std::make_pair(std::string("text/html"),
147  std::string("Page Not Found"));
148 }
149 
150 
151 /*
152  Echo handler. Returns a html with get keys and values
153  */
154 std::pair<std::string, std::string>
155 echo(std::map<std::string, std::string>& varmap) {
156  std::stringstream ret;
157  std::map<std::string, std::string>::iterator iter = varmap.begin();
158  ret << "<html>\n";
159  while (iter != varmap.end()) {
160  ret << iter->first << " = " << iter->second << "<br>\n";
161  ++iter;
162  }
163  ret << "</html>\n";
164  ret.flush();
165  return std::make_pair(std::string("text/html"), ret.str());
166 }
167 
168 std::pair<std::string, std::string>
169 index_page(std::map<std::string, std::string>& varmap) {
170  std::stringstream ret;
171  ret << "<html>\n";
172  ret << "<h3>Registered Handlers:</h3>\n";
173  callback_lock().readlock();
174  std::map<std::string, http_redirect_callback_type>::const_iterator iter =
175  callbacks().begin();
176  while (iter != callbacks().end()) {
177  // don't put in the index page callback
178  if (iter->first != "") {
179  ret << iter->first << "<br>\n";
180  }
181  ++iter;
182  }
183  callback_lock().rdunlock();
184  ret << "</html>\n";
185  ret.flush();
186  return std::make_pair(std::string("text/html"), ret.str());
187 }
188 
189 
190 static void fill_builtin_callbacks() {
191  callbacks()["404"] = four_oh_four;
192  callbacks()["echo"] = echo;
193  callbacks()[""] = index_page;
194  callbacks()["index.html"] = index_page;
195 }
196 
197 
198 void add_metric_server_callback(std::string page,
199  http_redirect_callback_type callback) {
200  callback_lock().writelock();
201  callbacks()[page] = callback;
202  callback_lock().wrunlock();
203 }
204 
206  if (dc_impl::get_last_dc_procid() == 0) {
207  const char *options[] = {"listening_ports", "8090", NULL};
208  metric_context = mg_start(process_request, (void*)(&(callbacks())), options);
209  if(metric_context == NULL) {
210  logstream(LOG_ERROR) << "Unable to launch metrics server on port 8090. "
211  << "Metrics server will not be available" << std::endl;
212  return;
213  }
214  fill_builtin_callbacks();
215 
216  char hostname[1024];
217  std::string strhostname;
218  if (gethostname(hostname, 1024) == 0) strhostname = hostname;
219  logstream(LOG_EMPH) << "Metrics server now listening on "
220  << "http://" << strhostname << ":8090" << std::endl;
221  }
222 }
223 
225  if (dc_impl::get_last_dc_procid() == 0 && metric_context != NULL) {
226  std::cout << "Metrics server stopping." << std::endl;
227  mg_stop(metric_context);
228  }
229 }
230 
232  if (dc_impl::get_last_dc_procid() == 0 && metric_context != NULL) {
233  char buff[128];
234  // wait for ctrl-d
235  logstream(LOG_EMPH) << "Hit Ctrl-D to stop the metrics server" << std::endl;
236  while (fgets(buff, 128, stdin) != NULL );
238  }
239 }
240 
241 } // namespace graphlab