From dff8bbd1a12730ad38a3d7d308c80869d318d424 Mon Sep 17 00:00:00 2001 From: Peter Giacomo Lombardo Date: Tue, 16 Jan 2018 15:05:27 +0100 Subject: [PATCH 1/5] User configurable EUM API key --- instana/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/instana/__init__.py b/instana/__init__.py index e76cb6ac..6af19303 100644 --- a/instana/__init__.py +++ b/instana/__init__.py @@ -58,5 +58,8 @@ # instana.service_name = "myservice" service_name = None +# User configurable EUM API key for instana.helpers.eum_snippet() +eum_api_key = '' + if "INSTANA_SERVICE_NAME" in os.environ: service_name = os.environ["INSTANA_SERVICE_NAME"] From 2f2a016394311a92454bd1357ade8453a68e968c Mon Sep 17 00:00:00 2001 From: Peter Giacomo Lombardo Date: Tue, 16 Jan 2018 15:05:40 +0100 Subject: [PATCH 2/5] EUM Snippet source files. --- instana/eum.js | 10 ++++++++++ instana/eum_test.js | 12 ++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 instana/eum.js create mode 100644 instana/eum_test.js diff --git a/instana/eum.js b/instana/eum.js new file mode 100644 index 00000000..c5390faf --- /dev/null +++ b/instana/eum.js @@ -0,0 +1,10 @@ + diff --git a/instana/eum_test.js b/instana/eum_test.js new file mode 100644 index 00000000..e1fff8bc --- /dev/null +++ b/instana/eum_test.js @@ -0,0 +1,12 @@ + From 472ebb6df5a773410e5ec74355035e4e60e02a9d Mon Sep 17 00:00:00 2001 From: Peter Giacomo Lombardo Date: Tue, 16 Jan 2018 15:06:05 +0100 Subject: [PATCH 3/5] EUM helper & tests --- instana/helpers.py | 100 ++++++++++++++++++++++++++++++++++++++++++ tests/test_helpers.py | 81 ++++++++++++++++++++++++++++++++++ 2 files changed, 181 insertions(+) create mode 100644 instana/helpers.py create mode 100644 tests/test_helpers.py diff --git a/instana/helpers.py b/instana/helpers.py new file mode 100644 index 00000000..0bb06ee0 --- /dev/null +++ b/instana/helpers.py @@ -0,0 +1,100 @@ +import os +from string import Template +from instana import internal_tracer, config as instana_config +from instana.log import logger + +# Usage: +# +# from instana.helpers import eum_snippet +# meta_kvs = { 'userId': user.id } +# eum_snippet(meta=meta_kvs) + + +def eum_snippet(trace_id=None, eum_api_key=None, meta={}): + """ + Return an EUM snippet for use in views, templates and layouts that reports + client side metrics to Instana that will automagically be linked to the + current trace. + + @param trace_id [optional] the trace ID to insert into the EUM string + @param eum_api_key [optional] the EUM API key from your Instana dashboard + @param meta [optional] optional additional KVs you want reported with the + EUM metrics + + @return string + """ + try: + eum_file = open(os.path.dirname(__file__) + '/eum.js') + eum_src = Template(eum_file.read()) + + # Prepare the standard required IDs + ids = {} + ids['meta_kvs'] = '' + + current_ctx = internal_tracer.current_context() + + if trace_id or current_ctx: + ids['trace_id'] = trace_id or current_ctx.trace_id + else: + # No trace_id passed in and tracer doesn't show an active span so + # return nothing, nada & zip. + return '' + + if eum_api_key: + ids['eum_api_key'] = eum_api_key + else: + ids['eum_api_key'] = instana_config['eum_api_key'] + + # Process passed in EUM 'meta' key/values + for key, value in meta.items(): + ids['meta_kvs'] += ("'ineum('meta', '%s', '%s');'" % (key, value)) + + return eum_src.substitute(ids) + except Exception as e: + logger.debug(e) + return '' + +def eum_test_snippet(trace_id=None, eum_api_key=None, meta={}): + """ + Return an EUM snippet for use in views, templates and layouts that reports + client side metrics to Instana that will automagically be linked to the + current trace. + + @param trace_id [optional] the trace ID to insert into the EUM string + @param eum_api_key [optional] the EUM API key from your Instana dashboard + @param meta [optional] optional additional KVs you want reported with the + EUM metrics + + @return string + """ + + try: + eum_file = open(os.path.dirname(__file__) + '/eum_test.js') + eum_src = Template(eum_file.read()) + + # Prepare the standard required IDs + ids = {} + ids['meta_kvs'] = '' + + current_ctx = internal_tracer.current_context() + + if trace_id or current_ctx: + ids['trace_id'] = trace_id or current_ctx.trace_id + else: + # No trace_id passed in and tracer doesn't show an active span so + # return nothing, nada & zip. + return '' + + if eum_api_key: + ids['eum_api_key'] = eum_api_key + else: + ids['eum_api_key'] = instana_config['eum_api_key'] + + # Process passed in EUM 'meta' key/values + for key, value in meta.items(): + ids['meta_kvs'] += ("'ineum('meta', '%s', '%s');'" % (key, value)) + + return eum_src.substitute(ids) + except Exception as e: + logger.debug(e) + return '' diff --git a/tests/test_helpers.py b/tests/test_helpers.py new file mode 100644 index 00000000..cfb6cc01 --- /dev/null +++ b/tests/test_helpers.py @@ -0,0 +1,81 @@ +from nose.tools import assert_equals +from instana.helpers import eum_snippet, eum_test_snippet + +# fake trace_id to test against +trace_id = "aMLx9G2GnnQ6QyMCLJLuCM8nw" +# fake api key to test against +eum_api_key = "FJB66VjwGgGQX6jiCpekoR4vf" + +# fake meta key/values +meta1 = "Z7RmMKQAiyCLEAmseNy7e6Vm4" +meta2 = "Dp2bowfm6kJVD9CccmyBt4ePD" +meta3 = "N4poUwbNz98YcvWRAizy2phCo" + + +def test_vanilla_eum_snippet(): + eum_string = eum_snippet(trace_id=trace_id, eum_api_key=eum_api_key) + assert type(eum_string) is str + + assert eum_string.find(trace_id) != -1 + assert eum_string.find(eum_api_key) != -1 + +def test_eum_snippet_with_meta(): + meta_kvs = {} + meta_kvs['meta1'] = meta1 + meta_kvs['meta2'] = meta2 + meta_kvs['meta3'] = meta3 + + eum_string = eum_snippet(trace_id=trace_id, eum_api_key=eum_api_key, meta=meta_kvs) + assert type(eum_string) is str + + assert eum_string.find(trace_id) != -1 + assert eum_string.find(eum_api_key) != -1 + assert eum_string.find(meta1) != -1 + assert eum_string.find(meta2) != -1 + assert eum_string.find(meta3) != -1 + +def test_eum_snippet_error(): + meta_kvs = {} + meta_kvs['meta1'] = meta1 + meta_kvs['meta2'] = meta2 + meta_kvs['meta3'] = meta3 + + # No active span on tracer & no trace_id passed in. + eum_string = eum_snippet(eum_api_key=eum_api_key, meta=meta_kvs) + assert_equals('', eum_string) + +def test_vanilla_eum_test_snippet(): + eum_string = eum_test_snippet(trace_id=trace_id, eum_api_key=eum_api_key) + assert type(eum_string) is str + + assert eum_string.find(trace_id) != -1 + assert eum_string.find(eum_api_key) != -1 + assert eum_string.find('reportingUrl') != -1 + assert eum_string.find('//eum-test-fullstack-0-us-west-2.instana.io') != -1 + +def test_eum_test_snippet_with_meta(): + meta_kvs = {} + meta_kvs['meta1'] = meta1 + meta_kvs['meta2'] = meta2 + meta_kvs['meta3'] = meta3 + + eum_string = eum_test_snippet(trace_id=trace_id, eum_api_key=eum_api_key, meta=meta_kvs) + assert type(eum_string) is str + assert eum_string.find('reportingUrl') != -1 + assert eum_string.find('//eum-test-fullstack-0-us-west-2.instana.io') != -1 + + assert eum_string.find(trace_id) != -1 + assert eum_string.find(eum_api_key) != -1 + assert eum_string.find(meta1) != -1 + assert eum_string.find(meta2) != -1 + assert eum_string.find(meta3) != -1 + +def test_eum_test_snippet_error(): + meta_kvs = {} + meta_kvs['meta1'] = meta1 + meta_kvs['meta2'] = meta2 + meta_kvs['meta3'] = meta3 + + # No active span on tracer & no trace_id passed in. + eum_string = eum_test_snippet(eum_api_key=eum_api_key, meta=meta_kvs) + assert_equals('', eum_string) From f1cd290b5d073e743b73d18a5a47a8e36fe40105 Mon Sep 17 00:00:00 2001 From: Peter Giacomo Lombardo Date: Tue, 16 Jan 2018 15:20:51 +0100 Subject: [PATCH 4/5] Cleanup usage of user configured EUM API key --- instana/helpers.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/instana/helpers.py b/instana/helpers.py index 0bb06ee0..0fe08680 100644 --- a/instana/helpers.py +++ b/instana/helpers.py @@ -1,6 +1,6 @@ import os from string import Template -from instana import internal_tracer, config as instana_config +from instana import internal_tracer, eum_api_key as global_eum_api_key from instana.log import logger # Usage: @@ -43,7 +43,7 @@ def eum_snippet(trace_id=None, eum_api_key=None, meta={}): if eum_api_key: ids['eum_api_key'] = eum_api_key else: - ids['eum_api_key'] = instana_config['eum_api_key'] + ids['eum_api_key'] = global_eum_api_key # Process passed in EUM 'meta' key/values for key, value in meta.items(): @@ -88,7 +88,7 @@ def eum_test_snippet(trace_id=None, eum_api_key=None, meta={}): if eum_api_key: ids['eum_api_key'] = eum_api_key else: - ids['eum_api_key'] = instana_config['eum_api_key'] + ids['eum_api_key'] = global_eum_api_key # Process passed in EUM 'meta' key/values for key, value in meta.items(): From 8f950398a6bbd460ac02510d6761409a5475f15b Mon Sep 17 00:00:00 2001 From: Peter Giacomo Lombardo Date: Tue, 16 Jan 2018 15:45:06 +0100 Subject: [PATCH 5/5] Add section documenting EUM helper usage. --- README.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/README.md b/README.md index b5954dd0..728f654a 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,31 @@ If you use uWSGI in forking workers mode, you must specify `--lazy-apps` (or `la The instana package will automatically collect key metrics from your Python processes. Just install and go. +## Want End User Monitoring? + +Instana provides deep end user monitoring that links server side traces with browser events to give you a complete view from server to browser. + +For Python templates and views, get your EUM API key from your Instana dashboard and you can call `instana.helpers.eum_snippet(api_key='abc')` from within your layout file. This will output +a small javascript snippet of code to instrument browser events. It's based on [Weasel](https://github.com/instana/weasel). Check it out. + +As an example, you could do the following: + +```python +from instana.helpers import eum_snippet + +instana.api_key = 'abc' +meta_kvs = { 'username': user.name } + +# This will return a string containing the EUM javascript for the layout or view. +eum_snippet(meta=meta_kvs) +``` + +The optional second argument to `eum_snippet()` is a hash of metadata key/values that will be reported along with the browser instrumentation. + +![Instana EUM example with metadata](https://s3.amazonaws.com/instana/Instana+Gameface+EUM+with+metadata+2016-12-22+at+15.32.01.png) + +See also the [End User Monitoring](https://docs.instana.io/products/website_monitoring/#configuration) in the Instana documentation portal. + ## OpenTracing This Python package supports [OpenTracing](http://opentracing.io/). When using this package, the OpenTracing tracer (`opentracing.tracer`) is automatically set to the `InstanaTracer`.