You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pulsar.apache.org by si...@apache.org on 2019/08/14 01:25:56 UTC

[pulsar] branch master updated: [dashboard] fix peek parse message error (#4918)

This is an automated email from the ASF dual-hosted git repository.

sijie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pulsar.git


The following commit(s) were added to refs/heads/master by this push:
     new a8b57c9  [dashboard] fix peek parse message error (#4918)
a8b57c9 is described below

commit a8b57c97c058862bce1c5b9d3435768d2071f09b
Author: Yi Tang <ss...@gmail.com>
AuthorDate: Wed Aug 14 09:25:50 2019 +0800

    [dashboard] fix peek parse message error (#4918)
    
    Fixes #4917
    
    ### Motivation
    
    dashboard peek message api raise exception when format response cause treat all message as a JSON string
    
    ### Modifications
    
    * format as JSON only if message is a JSON,
    * otherwise if message is printable, return the original message,
    * otherwise print hex like command with --hex option.
---
 dashboard/Dockerfile                             |  2 +-
 dashboard/django/stats/templates/stats/peek.html |  5 ++-
 dashboard/django/stats/views.py                  | 47 ++++++++++++++++++++++--
 3 files changed, 49 insertions(+), 5 deletions(-)

diff --git a/dashboard/Dockerfile b/dashboard/Dockerfile
index 01c84ea..6af19c4 100644
--- a/dashboard/Dockerfile
+++ b/dashboard/Dockerfile
@@ -25,7 +25,7 @@ RUN apt-get update
 RUN apt-get -y install postgresql python sudo nginx supervisor
 
 # Python dependencies
-RUN pip install uwsgi 'Django<2.0' psycopg2 pytz requests
+RUN pip install uwsgi 'Django<2.0' psycopg2 pytz requests hexdump
 
 # Postgres configuration
 COPY conf/postgresql.conf /etc/postgresql/9.6/main/
diff --git a/dashboard/django/stats/templates/stats/peek.html b/dashboard/django/stats/templates/stats/peek.html
index ebe4821..17d66b0 100644
--- a/dashboard/django/stats/templates/stats/peek.html
+++ b/dashboard/django/stats/templates/stats/peek.html
@@ -19,4 +19,7 @@
 
 -->
 
-<pre id="output" class="autoscroll">{{ message_body }}</pre>
\ No newline at end of file
+<div id="output" class="autoscroll">
+    <pre>{{ message_type }}</pre>
+    <pre>{{ message_body }}</pre>
+</div>
diff --git a/dashboard/django/stats/views.py b/dashboard/django/stats/views.py
index b49994b..d0a7af4 100644
--- a/dashboard/django/stats/views.py
+++ b/dashboard/django/stats/views.py
@@ -18,12 +18,15 @@
 #
 
 import logging
+import struct
+
 from django.shortcuts import render, get_object_or_404, redirect
 from django.template import loader
 from django.urls import reverse
 from django.views import generic
 from django.db.models import Q, IntegerField
 from dashboard import settings
+import hexdump
 import requests, json, re
 
 from django.http import HttpResponseRedirect, HttpResponse
@@ -363,13 +366,51 @@ def messages(request, topic_name, subscription_name):
         'subtitle' : subscription_name,
     })
 
+
+def message_skip_meta(message_view):
+    if not message_view or len(message_view) < 4:
+        raise ValueError("invalid message")
+    meta_size = struct.unpack(">I", message_view[:4])
+    message_index = 4 + meta_size[0]
+    if len(message_view) < message_index:
+        raise ValueError("invalid message")
+    return message_view[message_index:]
+
+
+def get_message_from_http_response(response):
+    if response.status_code != 200:
+        return "ERROR", "status_code=%d" % response.status_code
+    message_view = memoryview(response.content)
+    if 'X-Pulsar-num-batch-message' in response.headers:
+        batch_size = int(response.headers['X-Pulsar-num-batch-message'])
+        if batch_size == 1:
+            message_view = message_skip_meta(message_view)
+        else:
+            # TODO: can not figure out multi-message batch for now
+            return "Batch(size=%d)" % batch_size, "<omitted>"
+
+    try:
+        text = str(message_view,
+                   encoding=response.encoding or response.apparent_encoding,
+                   errors='replace')
+        if not text.isprintable():
+            return "Hex", hexdump.hexdump(message_view, result='return')
+    except (LookupError, TypeError):
+        return "Hex", hexdump.hexdump(message_view, result='return')
+    try:
+        return "JSON", json.dumps(json.loads(text),
+                                  ensure_ascii=False, indent=4)
+    except json.JSONDecodeError:
+        return "Text", text
+
+
 def peek(request, topic_name, subscription_name, message_number):
     url = settings.SERVICE_URL + '/admin/v2/' + topic_name + '/subscription/' + subscription_name + '/position/' + message_number
     response = requests.get(url)
-    message = response.text
-    message = message[message.index('{'):]
+    message_type, message = get_message_from_http_response(response)
     context = {
-        'message_body' : json.dumps(json.loads(message), indent=4),
+        'message_type': message_type,
+        'message_body': message,
     }
     return render(request, 'stats/peek.html', context)