Skip to content

Commit

Permalink
Add Debug Example
Browse files Browse the repository at this point in the history
  • Loading branch information
lidizheng committed Jun 10, 2019
1 parent b7e1b6f commit f7cb9c9
Show file tree
Hide file tree
Showing 6 changed files with 375 additions and 0 deletions.
59 changes: 59 additions & 0 deletions examples/python/debug/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Copyright 2019 The gRPC Authors
#
# 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.

load("@grpc_python_dependencies//:requirements.bzl", "requirement")

py_binary(
name = "debug_server",
testonly = 1,
srcs = ["debug_server.py"],
deps = [
"//src/python/grpcio/grpc:grpcio",
"//src/python/grpcio_channelz/grpc_channelz/v1:grpc_channelz",
"//examples:py_helloworld",
],
)

py_binary(
name = "send_message",
testonly = 1,
srcs = ["send_message.py"],
deps = [
"//src/python/grpcio/grpc:grpcio",
"//examples:py_helloworld",
],
)

py_binary(
name = "get_stats",
testonly = 1,
srcs = ["get_stats.py"],
deps = [
"//src/python/grpcio/grpc:grpcio",
"//src/python/grpcio_channelz/grpc_channelz/v1:grpc_channelz",
],
)

py_test(
name = "_debug_example_test",
srcs = ["test/_debug_example_test.py"],
deps = [
"//src/python/grpcio/grpc:grpcio",
"//src/python/grpcio_channelz/grpc_channelz/v1:grpc_channelz",
"//examples:py_helloworld",
":debug_server",
":send_message",
":get_stats",
],
)
43 changes: 43 additions & 0 deletions examples/python/debug/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# gRPC Python Debug Example

This example demonstrate the usage of Channelz. For a better looking website, the [gdebug](https://github.com/grpc/grpc-experiments/tree/master/gdebug) uses gRPC-Web protocol and will serve all useful information in web pages.

## Channelz: Live Channel Tracing

Channelz is a channel tracing feature. It will track statistics like how many messages have been sent, how many of them failed, what are the connected sockets. Since it is implemented in C-Core and has low-overhead, it is recommended to turn on for production services. See [Channelz design doc](https://github.com/grpc/proposal/blob/master/A14-channelz.md).

## How to enable tracing log
The tracing log generation might have larger overhead, especially when you try to trace transport. It would result in replicating the traffic loads. However, it is still the most powerful tool when you need to dive in.

### The Most Verbose Tracing Log

Specify environment variables, then run your application:

```
GRPC_VERBOSITY=debug
GRPC_TRACE=all
```

For more granularity, please see [environment_variables](https://github.com/grpc/grpc/blob/master/doc/environment_variables.md).

### Debug Transport Protocol

```
GRPC_VERBOSITY=debug
GRPC_TRACE=tcp,http,secure_endpoint,transport_security
```

### Debug Connection Behavior

```
GRPC_VERBOSITY=debug
GRPC_TRACE=call_error,connectivity_state,pick_first,round_robin,glb
```

## How to debug your application?

`pdb` is a debugging tool that is available for Python interpreters natively. You can set breakpoint, and execute commands while the application is stopped.

The simplest usage is add a single line in the place you want to inspect: `import pdb; pdb.set_trace()`. When interpreter see this line, it would pop out a interactive command line interface for you to inspect the application state.

For more detailed usage, see https://docs.python.org/3/library/pdb.html.
91 changes: 91 additions & 0 deletions examples/python/debug/debug_server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# Copyright 2019 The gRPC Authors
#
# 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.
"""The Python example of utilizing Channelz feature."""

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import argparse
import logging
import time
from concurrent import futures
import random

import grpc
from grpc_channelz.v1 import channelz

from examples import helloworld_pb2
from examples import helloworld_pb2_grpc

_LOGGER = logging.getLogger(__name__)
_LOGGER.setLevel(logging.INFO)

_ONE_DAY_IN_SECONDS = 60 * 60 * 24
_RANDOM_FAILURE_RATE = 0.3


class FaultInjectGreeter(helloworld_pb2_grpc.GreeterServicer):

def __init__(self, failure_rate):
self._failure_rate = failure_rate

def SayHello(self, request, context):
if random.random() < self._failure_rate:
context.abort(grpc.StatusCode.UNAVAILABLE,
'Randomly injected failure.')
return helloworld_pb2.HelloReply(
message='Hello, %s!' % request.name)


def create_server(addr, failure_rate):
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
helloworld_pb2_grpc.add_GreeterServicer_to_server(
FaultInjectGreeter(failure_rate), server)

# Add Channelz Servicer to the gRPC server
channelz.add_channelz_servicer(server)

server.add_insecure_port(addr)
return server


def main():
parser = argparse.ArgumentParser()
parser.add_argument(
'--addr',
nargs=1,
type=str,
default='[::]:50051',
help='the address to listen on')
parser.add_argument(
'--failure_rate',
nargs=1,
type=float,
default=0.3,
help='a float indicates the percentage of failed message injections')
args = parser.parse_args()

server = create_server(addr=args.addr, failure_rate=args.failure_rate)
server.start()
try:
while True:
time.sleep(_ONE_DAY_IN_SECONDS)
except KeyboardInterrupt:
server.stop(0)


if __name__ == '__main__':
logging.basicConfig(level=logging.INFO)
main()
52 changes: 52 additions & 0 deletions examples/python/debug/get_stats.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Copyright 2019 The gRPC Authors
#
# 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.
"""Poll statistics from the server."""

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import logging
import argparse
import grpc
from grpc_channelz.v1 import channelz_pb2
from grpc_channelz.v1 import channelz_pb2_grpc


def run(addr):
# NOTE(gRPC Python Team): .close() is possible on a channel and should be
# used in circumstances in which the with statement does not fit the needs
# of the code.
with grpc.insecure_channel(addr) as channel:
channelz_stub = channelz_pb2_grpc.ChannelzStub(channel)
response = channelz_stub.GetServers(
channelz_pb2.GetServersRequest(start_server_id=0))
print('Info for all servers: %s' % response)


def main():
parser = argparse.ArgumentParser()
parser.add_argument(
'--addr',
nargs=1,
type=str,
default='[::]:50051',
help='the address to request')
args = parser.parse_args()
run(addr=args.addr)


if __name__ == '__main__':
logging.basicConfig()
main()
67 changes: 67 additions & 0 deletions examples/python/debug/send_message.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Copyright 2019 The gRPC Authors
#
# 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.
"""Send multiple greeting messages to the backend."""

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import logging
import argparse
import grpc
from examples import helloworld_pb2
from examples import helloworld_pb2_grpc


def process(stub, request):
try:
response = stub.SayHello(request)
except grpc.RpcError as rpc_error:
print('Received error: %s' % rpc_error)
else:
print('Received message: %s' % response)


def run(addr, n):
# NOTE(gRPC Python Team): .close() is possible on a channel and should be
# used in circumstances in which the with statement does not fit the needs
# of the code.
with grpc.insecure_channel(addr) as channel:
stub = helloworld_pb2_grpc.GreeterStub(channel)
request = helloworld_pb2.HelloRequest(name='you')
for _ in range(n):
process(stub, request)


def main():
parser = argparse.ArgumentParser()
parser.add_argument(
'--addr',
nargs=1,
type=str,
default='[::]:50051',
help='the address to request')
parser.add_argument(
'-n',
nargs=1,
type=int,
default=10,
help='an integer for number of messages to sent')
args = parser.parse_args()
run(addr=args.addr, n=args.n)


if __name__ == '__main__':
logging.basicConfig()
main()
63 changes: 63 additions & 0 deletions examples/python/debug/test/_debug_example_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Copyright 2019 The gRPC Authors
#
# 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.
"""Test for gRPC Python debug example."""

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import logging
import unittest
from contextlib import contextmanager
import socket

from examples.python.debug import debug_server
from examples.python.debug import send_message
from examples.python.debug import get_stats

_LOGGER = logging.getLogger(__name__)
_LOGGER.setLevel(logging.INFO)

_FAILURE_RATE = 0.5
_NUMBER_OF_MESSAGES = 100


@contextmanager
def get_free_loopback_tcp_port():
if socket.has_ipv6:
tcp_socket = socket.socket(socket.AF_INET6)
else:
tcp_socket = socket.socket(socket.AF_INET)
tcp_socket.bind(('', 0))
address_tuple = tcp_socket.getsockname()
yield "localhost:%s" % (address_tuple[1])
tcp_socket.close()


class DebugExampleTest(unittest.TestCase):

def test_channelz_example(self):
with get_free_loopback_tcp_port() as addr:
server = debug_server.create_server(
addr=addr, failure_rate=_FAILURE_RATE)
server.start()
send_message.run(addr=addr, n=_NUMBER_OF_MESSAGES)
get_stats.run(addr=addr)
server.stop(None)
# No unhandled exception raised, test passed!


if __name__ == '__main__':
logging.basicConfig()
unittest.main(verbosity=2)

0 comments on commit f7cb9c9

Please sign in to comment.