forked from tensorflow/tensorboard
-
Notifications
You must be signed in to change notification settings - Fork 0
/
program.py
293 lines (241 loc) · 10.7 KB
/
program.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
# Copyright 2017 The TensorFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
"""Utilities for TensorBoard command line program.
This is a lightweight module for bringing up a TensorBoard HTTP server
or emulating the `tensorboard` shell command.
Those wishing to create custom builds of TensorBoard can use this module
by swapping out `tensorboard.main` with the custom definition that
modifies the set of plugins and static assets.
This module does not depend on first-party plugins or the default web
server assets. Those are defined in `tensorboard.default_plugins`.
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import errno
import os
import socket
import sys
import types # pylint: disable=unused-import
import tensorflow as tf
from werkzeug import serving
from tensorboard import util
from tensorboard import version
from tensorboard.backend import application
from tensorboard.backend.event_processing import event_file_inspector as efi
tf.flags.DEFINE_string('logdir', '', """logdir specifies the directory where
TensorBoard will look to find TensorFlow event files that it can display.
TensorBoard will recursively walk the directory structure rooted at logdir,
looking for .*tfevents.* files.
You may also pass a comma separated list of log directories, and TensorBoard
will watch each directory. You can also assign names to individual log
directories by putting a colon between the name and the path, as in
tensorboard --logdir name1:/path/to/logs/1,name2:/path/to/logs/2
""")
tf.flags.DEFINE_string(
'host', '', 'What host to listen to. Defaults to '
'serving on all interfaces, set to 127.0.0.1 (localhost) to '
'disable remote access (also quiets security warnings).')
tf.flags.DEFINE_integer('port', 6006, 'What port to serve TensorBoard on.')
tf.flags.DEFINE_boolean(
'purge_orphaned_data', True, 'Whether to purge data that '
'may have been orphaned due to TensorBoard restarts. '
'Disabling purge_orphaned_data can be used to debug data '
'disappearance.')
tf.flags.DEFINE_integer(
'reload_interval', 5,
'How often the backend should load more data, in seconds. Set to 0 to load '
'just once at startup and a negative number to never reload at all.')
tf.flags.DEFINE_string('db', "", """\
[Experimental] Sets SQL database URI.
This mode causes TensorBoard to persist experiments to a SQL database. The
following databases are supported:
- sqlite: Use SQLite built in to Python. URI must specify the path of the
database file, which will be created if it doesn't exist. For example:
--db sqlite:~/.tensorboard.db
Warning: This feature is a work in progress and only has limited support.
""")
tf.flags.DEFINE_boolean('inspect', False, """Use this flag to print out a digest
of your event files to the command line, when no data is shown on TensorBoard or
the data shown looks weird.
Example usages:
tensorboard --inspect --event_file myevents.out
tensorboard --inspect --event_file myevents.out --tag loss
tensorboard --inspect --logdir mylogdir
tensorboard --inspect --logdir mylogdir --tag loss
See tensorflow/python/summary/event_file_inspector.py for more info and
detailed usage.
""")
tf.flags.DEFINE_string(
'tag', '',
'The particular tag to query for. Only used if --inspect is present')
tf.flags.DEFINE_string(
'event_file', '',
'The particular event file to query for. Only used if --inspect is present '
'and --logdir is not specified.')
tf.flags.DEFINE_string(
'path_prefix', '',
'An optional, relative prefix to the path, e.g. "/path/to/tensorboard". '
'resulting in the new base url being located at '
'localhost:6006/path/to/tensorboard under default settings. A leading '
'slash is required when specifying the path_prefix, however trailing '
'slashes can be omitted. The path_prefix can be leveraged for path '
'based routing of an elb when the website base_url is not available '
'e.g. "example.site.com/path/to/tensorboard/"')
tf.flags.DEFINE_string(
'window_title', '',
'The title of the browser window.')
FLAGS = tf.flags.FLAGS
def main(plugins, assets_zip_provider=None):
"""Main function for TensorBoard.
This function makes some global changes to the Python environment and
then delegates to other functions in this module.
Since this function will generally run forever, it should only be
called if the Python process is only meant to be a TensorBoard
server.
Args:
plugins: A list of constructor functions for TBPlugin subclasses.
assets_zip_provider: Delegates to TBContext or uses default if None.
Returns:
Process exit code, i.e. 0 if successful or non-zero on failure. In
practice, an exception will most likely be raised instead of
returning non-zero.
:type plugins: list[:class:`base_plugin.TBPlugin`]
:type assets_zip_provider: () -> file
:rtype: int
"""
util.setup_logging()
if FLAGS.inspect:
tf.logging.info('Not bringing up TensorBoard, but inspecting event files.')
event_file = os.path.expanduser(FLAGS.event_file)
efi.inspect(FLAGS.logdir, event_file, FLAGS.tag)
return 0
# The default is HTTP/1.0 for some strange reason. If we don't use
# HTTP/1.1 then a new TCP socket and Python thread is created for
# each HTTP request. The tradeoff is we must always specify the
# Content-Length header, or do chunked encoding for streaming.
serving.WSGIRequestHandler.protocol_version = 'HTTP/1.1'
tb = create_tb_app(plugins, assets_zip_provider)
run_simple_server(tb)
return 0
def create_tb_app(plugins, assets_zip_provider=None):
"""Read the flags, and create a TensorBoard WSGI application.
Args:
plugins: A list of constructor functions for TBPlugin subclasses.
assets_zip_provider: Delegates to TBContext or uses default if None.
Raises:
ValueError: if a logdir is not specified.
Returns:
A new TensorBoard WSGI application.
:type plugins: list[:class:`base_plugin.TBPlugin`]
:type assets_zip_provider: () -> file
:rtype: types.FunctionType
"""
if not FLAGS.db and not FLAGS.logdir:
raise ValueError('A logdir must be specified when db is not specified. '
'Run `tensorboard --help` for details and examples.')
return application.standard_tensorboard_wsgi(
assets_zip_provider=assets_zip_provider,
db_uri=FLAGS.db,
logdir=os.path.expanduser(FLAGS.logdir),
purge_orphaned_data=FLAGS.purge_orphaned_data,
reload_interval=FLAGS.reload_interval,
plugins=plugins,
path_prefix=FLAGS.path_prefix,
window_title=FLAGS.window_title)
def make_simple_server(tb_app, host=None, port=None, path_prefix=None):
"""Create an HTTP server for TensorBoard.
Args:
tb_app: The TensorBoard WSGI application to create a server for.
host: Indicates the interfaces to bind to ('::' or '0.0.0.0' for all
interfaces, '::1' or '127.0.0.1' for localhost). A blank value ('')
indicates protocol-agnostic all interfaces. If not specified, will
default to the flag value.
port: The port to bind to (0 indicates an unused port selected by the
operating system). If not specified, will default to the flag value.
path_prefix: Optional relative prefix to the path, e.g. "/service/tf"
Returns:
A tuple of (server, url):
server: An HTTP server object configured to host TensorBoard.
url: A best guess at a URL where TensorBoard will be accessible once the
server has been started.
Raises:
socket.error: If a server could not be constructed with the host and port
specified. Also logs an error message.
"""
if host is None:
host = FLAGS.host
if port is None:
port = FLAGS.port
if path_prefix is None:
path_prefix = FLAGS.path_prefix
try:
if host:
# The user gave us an explicit host
server = serving.make_server(host, port, tb_app, threaded=True)
if ':' in host and not host.startswith('['):
# Display IPv6 addresses as [::1]:80 rather than ::1:80
final_host = '[{}]'.format(host)
else:
final_host = host
else:
# We've promised to bind to all interfaces on this host. However, we're
# not sure whether that means IPv4 or IPv6 interfaces.
try:
# First try passing in a blank host (meaning all interfaces). This,
# unfortunately, defaults to IPv4 even if no IPv4 interface is available
# (yielding a socket.error).
server = serving.make_server(host, port, tb_app, threaded=True)
except socket.error:
# If a blank host didn't work, we explicitly request IPv6 interfaces.
server = serving.make_server('::', port, tb_app, threaded=True)
final_host = socket.gethostname()
server.daemon_threads = True
except socket.error:
if port == 0:
msg = 'TensorBoard unable to find any open port'
else:
msg = (
'TensorBoard attempted to bind to port %d, but it was already in use'
% port)
tf.logging.error(msg)
print(msg)
raise
server.handle_error = _handle_error
final_port = server.socket.getsockname()[1]
tensorboard_url = 'http://%s:%d%s' % (final_host, final_port, path_prefix)
return server, tensorboard_url
def run_simple_server(tb_app):
"""Run a TensorBoard HTTP server, and print some messages to the console."""
try:
server, url = make_simple_server(tb_app)
except socket.error:
# An error message was already logged
# TODO(@jart): Remove log and throw anti-pattern.
sys.exit(-1)
sys.stderr.write('TensorBoard %s at %s (Press CTRL+C to quit)\n' %
(version.VERSION, url))
sys.stderr.flush()
server.serve_forever()
# Kludge to override a SocketServer.py method so we can get rid of noisy
# EPIPE errors. They're kind of a red herring as far as errors go. For
# example, `curl -N http://localhost:6006/ | head` will cause an EPIPE.
def _handle_error(unused_request, client_address):
exc_info = sys.exc_info()
e = exc_info[1]
if isinstance(e, IOError) and e.errno == errno.EPIPE:
tf.logging.warn('EPIPE caused by %s:%d in HTTP serving' % client_address)
else:
tf.logging.error('HTTP serving error', exc_info=exc_info)