{"id":19960,"date":"2018-01-08T12:15:13","date_gmt":"2018-01-08T10:15:13","guid":{"rendered":"https:\/\/www.webcodegeeks.com\/?p=19960"},"modified":"2018-01-03T11:35:16","modified_gmt":"2018-01-03T09:35:16","slug":"exploring-security-metrics-error-handling-grpc-python","status":"publish","type":"post","link":"https:\/\/www.webcodegeeks.com\/python\/exploring-security-metrics-error-handling-grpc-python\/","title":{"rendered":"Exploring Security, Metrics, and Error-handling with gRPC in Python"},"content":{"rendered":"<p>In my post <a href=\"https:\/\/www.webcodegeeks.com\/python\/using-grpc-python\/\">\u201cUsing gRPC in Python,\u201d<\/a> we wrote a basic gRPC server implementing a <code>users<\/code> service. We are going to expand on it and explore more gRPC concepts, such as secure client-server communication via self-signed SSL certificates, implementing gRPC middleware (or <code>interceptors<\/code>), and error handling.<\/p>\n<p>We will be using Python 3.6 for our demos in this article. The <a href=\"https:\/\/github.com\/amitsaha\/python-grpc-demo\">git repository<\/a> has all the code listings we will be discussing in this article in the subdirectory <code>demo2<\/code>.<\/p>\n<h2>Securing Client-server Communication<\/h2>\n<p>We initialized the server in the first article as follows:<\/p>\n<pre class=\"brush:py\">..\r\nserver.add_insecure_port('127.0.0.1:50051')\r\n..<\/pre>\n<p>The <code>add_insecure_port()<\/code> method initializes an <code>insecure<\/code> server and client-server communication is unencrypted. It is however recommended that a gRPC client-server communication takes place over a secure channel via <a href=\"https:\/\/grpc.io\/docs\/guides\/auth.html\">SSL\/TLS<\/a>.<\/p>\n<p>First, we will create a self-signed certificate using <code>openssl<\/code>:<\/p>\n<pre class=\"brush:py\">$ # Generate a private key\r\n\r\n$ openssl genrsa -out server.key 2048\r\nGenerating RSA private key, 2048 bit long modulus\r\n.........+++\r\n.......................................................................+++\r\ne is 65537 (0x10001)\r\n\r\n$ openssl req -new -x509 -sha256 -key server.key -out server.crt -days 3650\r\nYou are about to be asked to enter information that will be incorporated\r\ninto your certificate request.\r\nWhat you are about to enter is what is called a Distinguished Name or a DN.\r\nThere are quite a few fields but you can leave some blank\r\nFor some fields there will be a default value,\r\nIf you enter '.', the field will be left blank.\r\n-----\r\nCountry Name (2 letter code) [AU]:AU\r\nState or Province Name (full name) [Some-State]:NSW\r\nLocality Name (eg, city) []:Sydney\r\nOrganization Name (eg, company) [Internet Widgits Pty Ltd]:gRPC Demo\r\nOrganizational Unit Name (eg, section) []:gRPC\r\nCommon Name (e.g. server FQDN or YOUR name) []:localhost\r\nEmail Address []:a@a.com\r\n(<\/pre>\n<p>At this stage, we will have two files: <code>server.key<\/code> and <code>server.crt<\/code>. On the server side, we will need both these files; on the client side, we will only need the <code>server.crt<\/code> file.<\/p>\n<p>Using the above certificate, we will now modify our server code as follows:<\/p>\n<pre class=\"brush:py\">server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))\r\nusers_service.add_UsersServicer_to_server(UsersService(), server)\r\n# read in key and certificate\r\nwith open(os.path.join(os.path.split(__file__)[0], 'server.key')) as f:\r\n    private_key = f.read().encode()\r\nwith open(os.path.join(os.path.split(__file__)[0], 'server.crt')) as f:\r\n    certificate_chain = f.read().encode()\r\n# create server credentials\r\nserver_creds = grpc.ssl_server_credentials(((private_key, certificate_chain,),))\r\nserver.add_secure_port('localhost:50051', server_creds)<\/pre>\n<p>Comparing it to the earlier version of our server, <code>add_secure_port<\/code> has replaced <code>add_insecure_port()<\/code>. We have kept things simple here and have copied the <code>server.key<\/code> and <code>server.crt<\/code> file to the same directory as the <code>server.py<\/code> file. For all purposes beyond demonstration, these locations should be passed as configuration values (environment variables, for example).<\/p>\n<p>On the client side, we will do a similar modification. Earlier, we created a channel as follows:<\/p>\n<pre class=\"brush:py\">channel = grpc.insecure_channel('localhost:50051')<\/pre>\n<p>Now to communicate with our server over TLS, we will do the following:<\/p>\n<pre class=\"brush:py\">with open('server.crt') as f:\r\n    trusted_certs = f.read().encode()\r\n# create credentials\r\ncredentials = grpc.ssl_channel_credentials(root_certificates=trusted_certs)\r\nchannel = grpc.secure_channel('localhost:50051', credentials)\r\n...<\/pre>\n<h2>Implementing Server-side gRPC Interceptors<\/h2>\n<p>Interceptors are custom code that run on servers and clients during a request\/response lifecycle. If you are familiar with the concept of middleware in HTTP, interceptors are middleware for gRPC. Support for interceptors in Python is still under discussion, but here we will play with the current <a href=\"https:\/\/github.com\/grpc\/grpc\/pull\/12778\">proposed implementation<\/a>.<\/p>\n<p>The <code>demo2\/grpc-services\/grpc_interceptors<\/code> sub-directory contains a copy of the proposed implementation (<a href=\"https:\/\/github.com\/grpc\/grpc\/pull\/12778\/commits\/a938f50ffd8edf26216e00936b63f20456d5c376\">commit hash<\/a>).<\/p>\n<p>Using the above implementation, we will be writing our first server interceptor.<\/p>\n<h3>Exporting server metrics<\/h3>\n<p>The <code>metric interceptor<\/code> we will implement will push request latency in seconds (for every request) to <code>statsd<\/code>, which we will then convert to <code>prometheus<\/code> metrics via a <code>statsd bridge<\/code>. I recommend my <a href=\"https:\/\/www.webcodegeeks.com\/python\/monitoring-synchronous-python-web-applications-using-prometheus\/\">earlier post<\/a> to understand more about my choice of the approach.<\/p>\n<p>To write an interceptor, we first subclass the appropriate classes from the <code>grpc_interceptors<\/code> package:<\/p>\n<pre class=\"brush:py\">...\r\nfrom grpc_interceptors import UnaryUnaryServerInterceptor, UnaryStreamServerInterceptor\r\nclass MetricInterceptor(UnaryUnaryServerInterceptor, UnaryStreamServerInterceptor):\r\n\r\n    def __init__(self):\r\n        print(\"Initializing metric interceptor\")\r\n\r\n    @send_metrics\r\n    def intercept_unary_unary_handler(self, handler, method, request, servicer_context):\r\n        return handler(request, servicer_context)\r\n\r\n    @send_metrics\r\n    def intercept_unary_stream_handler(self, handler, method, request, servicer_context):\r\n        result = handler(request, servicer_context)\r\n        for response in result:\r\n            yield response\r\n...<\/pre>\n<p>Our class, <code>MetricInterceptor<\/code>, derives from <code>UnaryUnaryServerInterceptor<\/code> and <code>UnaryStreamServerInterceptor<\/code> and then overrides the <code>intercept_unary_unary_handler<\/code> and <code>intercept_unary_stream_handler<\/code> methods respectively. This is because our service implements only two kinds of RPC methods:<\/p>\n<ul>\n<li>unary request and response (<code>CreateUser<\/code>)<\/li>\n<li>unary request and streaming response (<code>UsersGet<\/code>)<\/li>\n<\/ul>\n<p>If we implemented other kinds of RPC methods, we would be deriving from the corresponding interceptor base class and implementing the corresponding method as well.<\/p>\n<p>The <code>send_metrics<\/code> decorator wraps around the interceptor method to implement the metric calculation and push it to the configured <code>statsd<\/code> server (the entire function is in the module <code>metric_interceptor<\/code> in the <code>server<\/code> sub-directory).<\/p>\n<p>Let\u2019s first go through the following snippet from the function:<\/p>\n<pre class=\"brush:py\">@wraps(func)\r\ndef wrapper(*args, **kw):\r\n    service_method = None\r\n    service_name = None\r\n    if isinstance(args[4], grpc._server._Context):\r\n        servicer_context = args[4]\r\n        # This gives us &lt;service&gt;\/&lt;method name&gt;\r\n        service_method = servicer_context._rpc_event.request_call_details.method\r\n        service_name, method_name = str(service_method).rsplit('\/')[1::]\r\n    else:\r\n        logging.warning('Cannot derive the service name and method')\r\n...<\/pre>\n<p>The <code>func<\/code> function here refers to the original <code>interceptor<\/code> method that we wrapped above. We extract two important pieces of data here to attach as <code>labels<\/code> to the metric:<\/p>\n<ul>\n<li>The gRPC service name<\/li>\n<li>The method name<\/li>\n<\/ul>\n<p>Both of these are available via the <code>_rpc_event.request_call_details.method<\/code> attribute of the <code>context<\/code> object, which is an instance of the <code>grpc._server._Context<\/code> class. I have the <code>isinstance<\/code> check here since I am working with the experimental implementation and probably will not be necessary.<\/p>\n<p>Next, we store the starting time and call the actual RPC method:<\/p>\n<pre class=\"brush:py\">try:\r\n    start_time = time.time()\r\n    result = func(*args, **kw)\r\n    result_status = 'success'\r\nexcept Exception:\r\n    result_status = 'error'\r\n    raise\r\nfinally:\r\n    resp_time = time.time() - start_time\r\n    push_to_statsd_histogram(\r\n        REQUEST_LATENCY_METRIC_NAME,\r\n        resp_time, [\r\n            'service:{0}'.format(service_name),\r\n            'method:{0}'.format(method_name),\r\n            'status:{0}'.format(result_status),\r\n        ])\r\nreturn result<\/pre>\n<p>We add three <code>statsd<\/code> tags here:<\/p>\n<ul>\n<li>the service name<\/li>\n<li>the method name<\/li>\n<li>the status of the call when we push the time taken to complete the call<\/li>\n<\/ul>\n<p>To register the interceptor with the server, we call create a new object of type <code>MetricInterceptor()<\/code> and then call the <code>intercept_server()<\/code> function (part of the proposed <code>grpc_interceptors<\/code> package):<\/p>\n<pre class=\"brush:py\">..\r\nserver = grpc.server(futures.ThreadPoolExecutor(max_workers=10))\r\nmetric_interceptor = MetricInterceptor()\r\nserver = intercept_server(server, metric_interceptor)\r\n...<\/pre>\n<p>!Sign up for a free Codeship Account<\/p>\n<h2>Error Handling and Logging<\/h2>\n<p>If there is an unhandled exception that happens when serving a request, we will see a traceback on the server corresponding to the runtime error that occured. On the client, we will see a traceback of the form:<\/p>\n<pre class=\"brush:py\">...\r\nraise _Rendezvous(state, None, None, deadline)\r\ngrpc._channel._Rendezvous: &lt;_Rendezvous of RPC that terminated with (StatusCode.UNKNOWN, Exception calling application: division by zero)&gt;<\/pre>\n<p>The unhandled error here is an <code>1\/0<\/code> statement that was injected into the <code>CreateUser<\/code> function implementation.<\/p>\n<p>With a couple of additional steps \u2014 one on the server and one on the client side \u2014 we can have more control over the traceback logging for unhandled runtime errors. Let\u2019s modify the <code>sample_client_demo.py<\/code> client so that we have <code>try..except<\/code> block in which we make the call to the RPC method:<\/p>\n<pre class=\"brush:py\">...\r\ntry:\r\n    response = stub.CreateUser(\r\n          users_messages.CreateUserRequest(username='tom'),\r\n          metadata=metadata,\r\n     )\r\nexcept grpc.RpcError as e:\r\n    print('CreateUser failed with {0}: {1}'.format(e.code(), e.details()))\r\nelse:\r\n    print(\"User created:\", response.user.username)\r\n...<\/pre>\n<p>Now, if we run the client again:<\/p>\n<pre class=\"brush:py\">CreateUser failed with StatusCode.UNKNOWN: Exception calling application: division by zero<\/pre>\n<p>The <code>code()<\/code> method of the returned exception gives us access to the status code returned by the server. It\u2019s value is one of the <a href=\"https:\/\/grpc.io\/grpc\/python\/grpc.html?highlight=method#grpc.StatusCode\">gRPC status codes<\/a>. The <code>details()<\/code> method gives us access to additional details sent by the server. Here it gives us the exception message associated with the runtime error on the server.<\/p>\n<p>Ideally, we should write a client interceptor to always make the call in a <code>try..except<\/code> block as above and hence not have to do so individually for all method calls.<\/p>\n<p>On the server side, we will write a logging interceptor (similar to the metric interceptor above) so that it looks for any unhandled errors and logs it with any metadata associated with the request. This will be useful to identify and correlate the failure to the request. In addition, we can also perform a translation of the error message to a more user friendly error if we so desired.<\/p>\n<p>The module, <code>logging_interceptor.py<\/code> in the <code>server\/<\/code> sub-directory implements a logging interceptor. The most important snippet is as follows:<\/p>\n<pre class=\"brush:py\">from pythonjsonlogger import jsonlogger\r\n\r\nfrom grpc_interceptors import UnaryUnaryServerInterceptor, UnaryStreamServerInterceptor\r\n\r\nlogger = logging.getLogger()\r\nlogHandler = logging.StreamHandler()\r\nformatter = jsonlogger.JsonFormatter()\r\nlogHandler.setFormatter(formatter)\r\nlogger.addHandler(logHandler)\r\n\r\n...\r\n...\r\nmetadata = {}\r\nmetadata['timestamp'] = datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S.%fZ')\r\nservicer_context = None\r\nif isinstance(args[4], grpc._server._Context):\r\n    servicer_context = args[4]\r\n    metadata.update(dict(servicer_context.invocation_metadata()))\r\ntry:\r\n    result = func(*args, **kw)\r\nexcept Exception as e:\r\n    logger.error(e, exc_info=True, extra=metadata)\r\n    if servicer_context:\r\n        servicer_context.set_details(str(e))\r\n        servicer_context.set_code(grpc.StatusCode.UNKNOWN)\r\n    # TODO: need to return an appropriate response type here\r\n    # Currently this will raise a serialization error on the server\r\n    # side\r\n    # see https:\/\/github.com\/grpc\/grpc\/issues\/12824\r\n    return None\r\nelse:\r\n    return result<\/pre>\n<p>We use the <code>set_details()<\/code> method on the servicer context object to send back unhandled exception message. This is where we can customize what the client\u2019s <code>get_details()<\/code> method returns. Then we set the grpc status code to <code>UNKNOWN<\/code>. This tells the client that this is a non-OK response and will be manifested as an exception of type <code>grpc.RpcError<\/code>. Since this is an error, we don\u2019t have anything meaningful to return here, so we return <code>None<\/code>. This however, results in a serialization here on the server side. Please see <a href=\"https:\/\/github.com\/grpc\/grpc\/issues\/12824\">issue<\/a> to learn more.<\/p>\n<p>We then modify our server code to register the logging interceptor:<\/p>\n<pre class=\"brush:py\">...\r\nserver = grpc.server(futures.ThreadPoolExecutor(max_workers=10))\r\nmetric_interceptor = MetricInterceptor()\r\nlogging_interceptor = LoggingInterceptor()\r\nserver = intercept_server(server, metric_interceptor, logging_interceptor)\r\n...<\/pre>\n<p>The order of registration of the interceptors is important here. As we have it above, the call flow is as follows:<\/p>\n<ol>\n<li>Logging interceptor invoked<\/li>\n<li>Metric interceptor invoked<\/li>\n<li>Service method called<\/li>\n<li>Metric interceptor returns<\/li>\n<li>Logging interceptor returns<\/li>\n<\/ol>\n<h2>Running the server<\/h2>\n<p>In addition to Python 3.6, we will be using <code>docker<\/code> and <code>docker-compose<\/code> to run the gRPC server as the other software we will be using (promethus, statsd exporter and grafana). If you don\u2019t have these installed, please follow the official install guide to install these on your operating system.<\/p>\n<p>Starting with a clone of the <a href=\"https:\/\/github.com\/amitsaha\/python-grpc-demo\/\">repo<\/a>:<\/p>\n<pre class=\"brush:py\">$ cd demo2\/grpc-services<\/pre>\n<p>We will first build the docker image for the gRPC server:<\/p>\n<pre class=\"brush:py\">$ docker build -t amitsaha\/grpc-users -f Dockerfile.users .<\/pre>\n<p>Start the server along with statsd-exporter, Prometheus, and Grafana:<\/p>\n<pre class=\" brush:php\">$ docker-compose -f docker-compse.yml -f docker-compose-infra.yml up<\/pre>\n<p>Run the client:<\/p>\n<pre class=\"brush:py\">$ docker exect -ti users bash\r\n# cd \/client\r\n# # run the sample_client_demo.py file 10 times\r\n#  .\/run-client.sh 10<\/pre>\n<p>The <code>server.py<\/code> file (in <code>users\/server\/server.py<\/code>) has a random runtime error injected into the <code>CreateUser<\/code> method which happens 50 percent of the time.<\/p>\n<p>On the server console, you will see that the exception is being logged as a JSON object with a timestamp and additional metadata added.<\/p>\n<p>Then open <code>http:\/\/127.0.0.1:9090\/graph?g0.range_input=5m&amp;g0.expr=request_latency_seconds_timer&amp;g0.tab=0<\/code> in your browser. You will see the Prometheus expression browser showing the latency of the requests and you will see different metrics for each unique combination of the labels we added.<\/p>\n<figure id=\"attachment_19976\" aria-describedby=\"caption-attachment-19976\" style=\"width: 860px\" class=\"wp-caption aligncenter\"><a href=\"http:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2018\/01\/prometheus1.png\"><img decoding=\"async\" class=\"wp-image-19976\" src=\"http:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2018\/01\/prometheus1.png\" alt=\"\" width=\"860\" height=\"301\" srcset=\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2018\/01\/prometheus1.png 1236w, https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2018\/01\/prometheus1-300x105.png 300w, https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2018\/01\/prometheus1-768x269.png 768w, https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2018\/01\/prometheus1-1024x359.png 1024w\" sizes=\"(max-width: 860px) 100vw, 860px\" \/><\/a><figcaption id=\"caption-attachment-19976\" class=\"wp-caption-text\">Server metrics<\/figcaption><\/figure>\n<h2>Conclusion<\/h2>\n<p>We extended our gRPC server in this article to be secure, export metrics, and log errors. We also learned about the in-progress work on bringing support for interceptors to gRPC Python. Next, here are a few resources to learn more about these and other gRPC features:<\/p>\n<ul>\n<li><a href=\"https:\/\/github.com\/grpc\/proposal\/pull\/39\">gRPC Interceptors proposal<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/grpc\/grpc\/issues\/12824\">gRPC response status as None<\/a><\/li>\n<li><a href=\"https:\/\/grpc.io\/docs\/guides\/error.html\">Error Handling<\/a><\/li>\n<li><a href=\"http:\/\/www.sandtable.com\/using-ssl-with-grpc-in-python\/\">Using ssl with grpc in Python<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/grpc\/grpc\/blob\/master\/doc\/health-checking.md\">gRPC health checks<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/grpc\/grpc\/blob\/master\/doc\/load-balancing.md\">gRPC load balancing<\/a><\/li>\n<li><a href=\"https:\/\/bbengfort.github.io\/programmer\/2017\/03\/03\/secure-grpc.html\">Golang: Secure gRPC with SSL\/TLS<\/a><\/li>\n<li><a href=\"http:\/\/mycodesmells.com\/post\/authentication-in-grpc\">Golang: Authentication in gRPC<\/a><\/li>\n<\/ul>\n<div class=\"attribution\">\n<table>\n<tbody>\n<tr>\n<td>Published on Web Code Geeks with permission by Amit Saha, partner at our <a href=\"http:\/\/www.webcodegeeks.com\/join-us\/wcg\/\" target=\"_blank\" rel=\"noopener\">WCG program<\/a>. See the original article here: <a href=\"https:\/\/blog.codeship.com\/exploring-security-metrics-and-error-handling-with-grpc-in-python\/\" target=\"_blank\" rel=\"noopener\">Exploring Security, Metrics, and Error-handling with gRPC in Python<\/a><\/p>\n<p>Opinions expressed by Web Code Geeks contributors are their own.<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>In my post \u201cUsing gRPC in Python,\u201d we wrote a basic gRPC server implementing a users service. We are going to expand on it and explore more gRPC concepts, such as secure client-server communication via self-signed SSL certificates, implementing gRPC middleware (or interceptors), and error handling. We will be using Python 3.6 for our demos &hellip;<\/p>\n","protected":false},"author":1138,"featured_media":1651,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[53],"tags":[],"class_list":["post-19960","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-python"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v26.5 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Exploring Security, Metrics, and Error-handling with gRPC in Python - Web Code Geeks - 2026<\/title>\n<meta name=\"description\" content=\"In my post \u201cUsing gRPC in Python,\u201d we wrote a basic gRPC server implementing a users service. We are going to expand on it and explore more gRPC concepts,\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.webcodegeeks.com\/python\/exploring-security-metrics-error-handling-grpc-python\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Exploring Security, Metrics, and Error-handling with gRPC in Python - Web Code Geeks - 2026\" \/>\n<meta property=\"og:description\" content=\"In my post \u201cUsing gRPC in Python,\u201d we wrote a basic gRPC server implementing a users service. We are going to expand on it and explore more gRPC concepts,\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.webcodegeeks.com\/python\/exploring-security-metrics-error-handling-grpc-python\/\" \/>\n<meta property=\"og:site_name\" content=\"Web Code Geeks\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/webcodegeeks\" \/>\n<meta property=\"article:published_time\" content=\"2018-01-08T10:15:13+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/11\/python-logo.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"150\" \/>\n\t<meta property=\"og:image:height\" content=\"150\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Amit Saha\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@webcodegeeks\" \/>\n<meta name=\"twitter:site\" content=\"@webcodegeeks\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Amit Saha\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"11 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.webcodegeeks.com\/python\/exploring-security-metrics-error-handling-grpc-python\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/python\/exploring-security-metrics-error-handling-grpc-python\/\"},\"author\":{\"name\":\"Amit Saha\",\"@id\":\"https:\/\/www.webcodegeeks.com\/#\/schema\/person\/4be953a89cfde2033d2d0ccb8352a39e\"},\"headline\":\"Exploring Security, Metrics, and Error-handling with gRPC in Python\",\"datePublished\":\"2018-01-08T10:15:13+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/python\/exploring-security-metrics-error-handling-grpc-python\/\"},\"wordCount\":1315,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/python\/exploring-security-metrics-error-handling-grpc-python\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/11\/python-logo.jpg\",\"articleSection\":[\"Python\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/www.webcodegeeks.com\/python\/exploring-security-metrics-error-handling-grpc-python\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.webcodegeeks.com\/python\/exploring-security-metrics-error-handling-grpc-python\/\",\"url\":\"https:\/\/www.webcodegeeks.com\/python\/exploring-security-metrics-error-handling-grpc-python\/\",\"name\":\"Exploring Security, Metrics, and Error-handling with gRPC in Python - Web Code Geeks - 2026\",\"isPartOf\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/python\/exploring-security-metrics-error-handling-grpc-python\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/python\/exploring-security-metrics-error-handling-grpc-python\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/11\/python-logo.jpg\",\"datePublished\":\"2018-01-08T10:15:13+00:00\",\"description\":\"In my post \u201cUsing gRPC in Python,\u201d we wrote a basic gRPC server implementing a users service. We are going to expand on it and explore more gRPC concepts,\",\"breadcrumb\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/python\/exploring-security-metrics-error-handling-grpc-python\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.webcodegeeks.com\/python\/exploring-security-metrics-error-handling-grpc-python\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.webcodegeeks.com\/python\/exploring-security-metrics-error-handling-grpc-python\/#primaryimage\",\"url\":\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/11\/python-logo.jpg\",\"contentUrl\":\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/11\/python-logo.jpg\",\"width\":150,\"height\":150},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.webcodegeeks.com\/python\/exploring-security-metrics-error-handling-grpc-python\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.webcodegeeks.com\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Python\",\"item\":\"https:\/\/www.webcodegeeks.com\/category\/python\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"Exploring Security, Metrics, and Error-handling with gRPC in Python\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.webcodegeeks.com\/#website\",\"url\":\"https:\/\/www.webcodegeeks.com\/\",\"name\":\"Web Code Geeks\",\"description\":\"Web Developers Resource Center\",\"publisher\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.webcodegeeks.com\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.webcodegeeks.com\/#organization\",\"name\":\"Exelixis Media P.C.\",\"url\":\"https:\/\/www.webcodegeeks.com\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.webcodegeeks.com\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2022\/06\/exelixis-logo.png\",\"contentUrl\":\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2022\/06\/exelixis-logo.png\",\"width\":864,\"height\":246,\"caption\":\"Exelixis Media P.C.\"},\"image\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.facebook.com\/webcodegeeks\",\"https:\/\/x.com\/webcodegeeks\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.webcodegeeks.com\/#\/schema\/person\/4be953a89cfde2033d2d0ccb8352a39e\",\"name\":\"Amit Saha\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.webcodegeeks.com\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/9970b1486d07f680cee5d46882be11a25b90a35fd6dae0b43c589a71b885edea?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/9970b1486d07f680cee5d46882be11a25b90a35fd6dae0b43c589a71b885edea?s=96&d=mm&r=g\",\"caption\":\"Amit Saha\"},\"description\":\"Amit Saha is a software engineer and author of Doing Math with Python. He's written for Linux Journal, Linux Voice, and Linux Magazine.\",\"sameAs\":[\"https:\/\/blog.codeship.com\/\"],\"url\":\"https:\/\/www.webcodegeeks.com\/author\/amit-saha\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Exploring Security, Metrics, and Error-handling with gRPC in Python - Web Code Geeks - 2026","description":"In my post \u201cUsing gRPC in Python,\u201d we wrote a basic gRPC server implementing a users service. We are going to expand on it and explore more gRPC concepts,","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.webcodegeeks.com\/python\/exploring-security-metrics-error-handling-grpc-python\/","og_locale":"en_US","og_type":"article","og_title":"Exploring Security, Metrics, and Error-handling with gRPC in Python - Web Code Geeks - 2026","og_description":"In my post \u201cUsing gRPC in Python,\u201d we wrote a basic gRPC server implementing a users service. We are going to expand on it and explore more gRPC concepts,","og_url":"https:\/\/www.webcodegeeks.com\/python\/exploring-security-metrics-error-handling-grpc-python\/","og_site_name":"Web Code Geeks","article_publisher":"https:\/\/www.facebook.com\/webcodegeeks","article_published_time":"2018-01-08T10:15:13+00:00","og_image":[{"width":150,"height":150,"url":"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/11\/python-logo.jpg","type":"image\/jpeg"}],"author":"Amit Saha","twitter_card":"summary_large_image","twitter_creator":"@webcodegeeks","twitter_site":"@webcodegeeks","twitter_misc":{"Written by":"Amit Saha","Est. reading time":"11 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.webcodegeeks.com\/python\/exploring-security-metrics-error-handling-grpc-python\/#article","isPartOf":{"@id":"https:\/\/www.webcodegeeks.com\/python\/exploring-security-metrics-error-handling-grpc-python\/"},"author":{"name":"Amit Saha","@id":"https:\/\/www.webcodegeeks.com\/#\/schema\/person\/4be953a89cfde2033d2d0ccb8352a39e"},"headline":"Exploring Security, Metrics, and Error-handling with gRPC in Python","datePublished":"2018-01-08T10:15:13+00:00","mainEntityOfPage":{"@id":"https:\/\/www.webcodegeeks.com\/python\/exploring-security-metrics-error-handling-grpc-python\/"},"wordCount":1315,"commentCount":0,"publisher":{"@id":"https:\/\/www.webcodegeeks.com\/#organization"},"image":{"@id":"https:\/\/www.webcodegeeks.com\/python\/exploring-security-metrics-error-handling-grpc-python\/#primaryimage"},"thumbnailUrl":"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/11\/python-logo.jpg","articleSection":["Python"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.webcodegeeks.com\/python\/exploring-security-metrics-error-handling-grpc-python\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.webcodegeeks.com\/python\/exploring-security-metrics-error-handling-grpc-python\/","url":"https:\/\/www.webcodegeeks.com\/python\/exploring-security-metrics-error-handling-grpc-python\/","name":"Exploring Security, Metrics, and Error-handling with gRPC in Python - Web Code Geeks - 2026","isPartOf":{"@id":"https:\/\/www.webcodegeeks.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.webcodegeeks.com\/python\/exploring-security-metrics-error-handling-grpc-python\/#primaryimage"},"image":{"@id":"https:\/\/www.webcodegeeks.com\/python\/exploring-security-metrics-error-handling-grpc-python\/#primaryimage"},"thumbnailUrl":"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/11\/python-logo.jpg","datePublished":"2018-01-08T10:15:13+00:00","description":"In my post \u201cUsing gRPC in Python,\u201d we wrote a basic gRPC server implementing a users service. We are going to expand on it and explore more gRPC concepts,","breadcrumb":{"@id":"https:\/\/www.webcodegeeks.com\/python\/exploring-security-metrics-error-handling-grpc-python\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.webcodegeeks.com\/python\/exploring-security-metrics-error-handling-grpc-python\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.webcodegeeks.com\/python\/exploring-security-metrics-error-handling-grpc-python\/#primaryimage","url":"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/11\/python-logo.jpg","contentUrl":"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/11\/python-logo.jpg","width":150,"height":150},{"@type":"BreadcrumbList","@id":"https:\/\/www.webcodegeeks.com\/python\/exploring-security-metrics-error-handling-grpc-python\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.webcodegeeks.com\/"},{"@type":"ListItem","position":2,"name":"Python","item":"https:\/\/www.webcodegeeks.com\/category\/python\/"},{"@type":"ListItem","position":3,"name":"Exploring Security, Metrics, and Error-handling with gRPC in Python"}]},{"@type":"WebSite","@id":"https:\/\/www.webcodegeeks.com\/#website","url":"https:\/\/www.webcodegeeks.com\/","name":"Web Code Geeks","description":"Web Developers Resource Center","publisher":{"@id":"https:\/\/www.webcodegeeks.com\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.webcodegeeks.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.webcodegeeks.com\/#organization","name":"Exelixis Media P.C.","url":"https:\/\/www.webcodegeeks.com\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.webcodegeeks.com\/#\/schema\/logo\/image\/","url":"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2022\/06\/exelixis-logo.png","contentUrl":"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2022\/06\/exelixis-logo.png","width":864,"height":246,"caption":"Exelixis Media P.C."},"image":{"@id":"https:\/\/www.webcodegeeks.com\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/webcodegeeks","https:\/\/x.com\/webcodegeeks"]},{"@type":"Person","@id":"https:\/\/www.webcodegeeks.com\/#\/schema\/person\/4be953a89cfde2033d2d0ccb8352a39e","name":"Amit Saha","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.webcodegeeks.com\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/9970b1486d07f680cee5d46882be11a25b90a35fd6dae0b43c589a71b885edea?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/9970b1486d07f680cee5d46882be11a25b90a35fd6dae0b43c589a71b885edea?s=96&d=mm&r=g","caption":"Amit Saha"},"description":"Amit Saha is a software engineer and author of Doing Math with Python. He's written for Linux Journal, Linux Voice, and Linux Magazine.","sameAs":["https:\/\/blog.codeship.com\/"],"url":"https:\/\/www.webcodegeeks.com\/author\/amit-saha\/"}]}},"_links":{"self":[{"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/posts\/19960","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/users\/1138"}],"replies":[{"embeddable":true,"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/comments?post=19960"}],"version-history":[{"count":0,"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/posts\/19960\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/media\/1651"}],"wp:attachment":[{"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/media?parent=19960"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/categories?post=19960"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/tags?post=19960"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}