Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
DataPower Latency Visualization
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# DataPower Latency Logs into Pandas #\n",
"\n",
"This notebook describes how to read DataPower latency logs into\n",
"a Python [Pandas](http://pandas.pydata.org/) data frame."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Convert Latency Log to CSV ##\n",
"\n",
"Convert the latency log created by DataPower into a CSV file.\n",
"File names are hard-coded for the demo."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import re"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"reg = re.compile(r'\\w\\w\\w (\\w\\w\\w \\d\\d \\d\\d\\d\\d \\d\\d:\\d\\d:\\d\\d) \\[0x80e00073\\]\\[latency\\]\\[info\\] \\w+\\(([^)]+)\\): tid\\((\\d+)\\).+ Latency:\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+\\[(https?://[^/]+)([^?]+)?(\\?.+)?\\]')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"That was an insane regular expression to divide up a DataPower latency line into\n",
"individual fields we care about. A raw latency line looks like this.\n",
"\n",
"```\n",
"Wed Dec 02 2015 07:03:23 [0x80e00073][latency][info] mpgw(proxy-mpgw): tid(77904)[192.168.152.1] gtid(77904): Latency: 0 580 0 15 15 10 0 580 595 580 595 595 589 580 15 580 [http://192.168.152.24:8082/request3?t=563]\n",
"```\n",
"\n",
"The following steps show how the regular expressions chops it up.\n",
"\n",
"1. `\\w\\w\\w (\\w\\w\\w \\d\\d \\d\\d\\d\\d \\d\\d:\\d\\d:\\d\\d)` - This is the first\n",
" grouping. It makes the date (not including the week day) the first\n",
" element.\n",
"2. `\\[0x80e00073\\]\\[latency\\]\\[info\\] \\w+\\(([^)]+)\\)` - This skips a\n",
" bunch of constants and captures the name of the MPGW. It expects\n",
" it to be between the matching parenthesis.\n",
"3. `: tid\\((\\d+)\\)` - This captures the Transaction ID.\n",
"4. `.+ Latency:` - This makes sure we're matching a Latency record.\n",
" The constant in Step 2 probably did this already. The space before\n",
" the capital `L` makes sure this is not an extended latency record.\n",
"5. `\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)` - These are the 16 numbers\n",
" separated by spaces.\n",
"6. `\\s+\\[(https?://[^/]+)` - this matches the host and port.\n",
"7. `([^?]+)?` - this matches the optional URI after the host.\n",
"8. `(\\?.+)?` - this matches an optional parameter list.\n",
"9. `\\]` - The expression should end with a square bracket.\n",
"\n",
"There are usually lots of other lines that are not latency entries. So the regex\n",
"will pluck out only the latency log entries and group the fields for us. Our raw\n",
"log is `sample5.log`."
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"dplog = open('sample5.log', 'r')\n",
"output = open('sample5.csv', 'w')\n",
"output.write('Time,ProxyName,TxnID,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,url,uri,q\\n')\n",
"lines_processed = 0"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"'Tue Dec 01 2015 21:34:26 [0x80e00073][latency][info] mpgw(proxy-mpgw): tid(84483)[192.168.152.1] gtid(84483): Latency: 0 450 0 18 18 13 0 450 462 450 462 462 457 450 18 450 [http://192.168.152.24:8082/request5?t=429]\\r\\n'"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"line = dplog.readline()\n",
"line"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Check if the line matches our regex."
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"<_sre.SRE_Match at 0x106a69750>"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"fields = reg.match(line)\n",
"fields"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"When we get an object reference like this (not a `None`), that's a match.\n",
"Let's see what our grouping looks like given the input line above."
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"('Dec 01 2015 21:34:26',\n",
" 'proxy-mpgw',\n",
" '84483',\n",
" '0',\n",
" '450',\n",
" '0',\n",
" '18',\n",
" '18',\n",
" '13',\n",
" '0',\n",
" '450',\n",
" '462',\n",
" '450',\n",
" '462',\n",
" '462',\n",
" '457',\n",
" '450',\n",
" '18',\n",
" '450',\n",
" 'http://192.168.152.24:8082',\n",
" '/request5',\n",
" '?t=429')"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"fields.groups()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This all looks good. So let's create a comma-separated line from this\n",
"and send it to the output file."
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"'Dec 01 2015 21:34:26,proxy-mpgw,84483,0,450,0,18,18,13,0,450,462,450,462,462,457,450,18,450,http://192.168.152.24:8082,/request5,?t=429'"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"csvline = ','.join(fields.groups())\n",
"csvline"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"output.write(csvline + '\\n')\n",
"lines_processed += 1"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now do this for the rest of the file."
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Processed 150 lines.\n"
]
}
],
"source": [
"for line in dplog:\n",
" fields = reg.match(line)\n",
" if fields != None:\n",
" csvline = ','.join(fields.groups())\n",
" output.write(csvline + '\\n')\n",
" lines_processed += 1\n",
"\n",
"print(\"Processed {0} lines.\".format(lines_processed))"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"dplog.close()\n",
"output.close()"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Time,ProxyName,TxnID,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,url,uri,q\r\n",
"Dec 01 2015 21:34:26,proxy-mpgw,84483,0,450,0,18,18,13,0,450,462,450,462,462,457,450,18,450,http://192.168.152.24:8082,/request5,?t=429\r\n",
"Dec 01 2015 21:34:28,proxy-mpgw,89186,0,246,0,14,14,9,0,246,259,246,259,259,254,246,14,246,http://192.168.152.24:8082,/request1,?t=229\r\n",
"Dec 01 2015 21:34:28,proxy-mpgw,89170,0,1887,0,13,13,7,0,1887,1900,1887,1900,1900,1896,1887,13,1887,http://192.168.152.24:8082,/request4,?t=1871\r\n",
"Dec 01 2015 21:34:28,proxy-mpgw,84515,0,1988,0,14,14,8,0,1988,2000,1988,2000,2001,1996,1988,14,1988,http://192.168.152.24:8082,/request1,?t=1971\r\n"
]
}
],
"source": [
"!head -n 5 sample5.csv"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"From here we may switch to R or continue with Pandas.\n",
"Since this is an IPython notebook, we'll go with Pandas.\n",
"\n",
"## Creating Pandas Data Frames from CSV ##\n"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import numpy as np\n",
"import pandas as pd\n",
"from matplotlib import pyplot as plt\n",
"%matplotlib inline"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Create a Pandas data frame from the CSV and indicate that the `Time` field\n",
"is to be parsed as a Numpy `datetime64`."
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>Time</th>\n",
" <th>ProxyName</th>\n",
" <th>TxnID</th>\n",
" <th>1</th>\n",
" <th>2</th>\n",
" <th>3</th>\n",
" <th>4</th>\n",
" <th>5</th>\n",
" <th>6</th>\n",
" <th>7</th>\n",
" <th>...</th>\n",
" <th>10</th>\n",
" <th>11</th>\n",
" <th>12</th>\n",
" <th>13</th>\n",
" <th>14</th>\n",
" <th>15</th>\n",
" <th>16</th>\n",
" <th>url</th>\n",
" <th>uri</th>\n",
" <th>q</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>2015-12-01 21:34:26</td>\n",
" <td>proxy-mpgw</td>\n",
" <td>84483</td>\n",
" <td>0</td>\n",
" <td>450</td>\n",
" <td>0</td>\n",
" <td>18</td>\n",
" <td>18</td>\n",
" <td>13</td>\n",
" <td>0</td>\n",
" <td>...</td>\n",
" <td>450</td>\n",
" <td>462</td>\n",
" <td>462</td>\n",
" <td>457</td>\n",
" <td>450</td>\n",
" <td>18</td>\n",
" <td>450</td>\n",
" <td>http://192.168.152.24:8082</td>\n",
" <td>/request5</td>\n",
" <td>?t=429</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>2015-12-01 21:34:28</td>\n",
" <td>proxy-mpgw</td>\n",
" <td>89186</td>\n",
" <td>0</td>\n",
" <td>246</td>\n",
" <td>0</td>\n",
" <td>14</td>\n",
" <td>14</td>\n",
" <td>9</td>\n",
" <td>0</td>\n",
" <td>...</td>\n",
" <td>246</td>\n",
" <td>259</td>\n",
" <td>259</td>\n",
" <td>254</td>\n",
" <td>246</td>\n",
" <td>14</td>\n",
" <td>246</td>\n",
" <td>http://192.168.152.24:8082</td>\n",
" <td>/request1</td>\n",
" <td>?t=229</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>2015-12-01 21:34:28</td>\n",
" <td>proxy-mpgw</td>\n",
" <td>89170</td>\n",
" <td>0</td>\n",
" <td>1887</td>\n",
" <td>0</td>\n",
" <td>13</td>\n",
" <td>13</td>\n",
" <td>7</td>\n",
" <td>0</td>\n",
" <td>...</td>\n",
" <td>1887</td>\n",
" <td>1900</td>\n",
" <td>1900</td>\n",
" <td>1896</td>\n",
" <td>1887</td>\n",
" <td>13</td>\n",
" <td>1887</td>\n",
" <td>http://192.168.152.24:8082</td>\n",
" <td>/request4</td>\n",
" <td>?t=1871</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>2015-12-01 21:34:28</td>\n",
" <td>proxy-mpgw</td>\n",
" <td>84515</td>\n",
" <td>0</td>\n",
" <td>1988</td>\n",
" <td>0</td>\n",
" <td>14</td>\n",
" <td>14</td>\n",
" <td>8</td>\n",
" <td>0</td>\n",
" <td>...</td>\n",
" <td>1988</td>\n",
" <td>2000</td>\n",
" <td>2001</td>\n",
" <td>1996</td>\n",
" <td>1988</td>\n",
" <td>14</td>\n",
" <td>1988</td>\n",
" <td>http://192.168.152.24:8082</td>\n",
" <td>/request1</td>\n",
" <td>?t=1971</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>2015-12-01 21:34:29</td>\n",
" <td>proxy-mpgw</td>\n",
" <td>89202</td>\n",
" <td>0</td>\n",
" <td>703</td>\n",
" <td>0</td>\n",
" <td>15</td>\n",
" <td>15</td>\n",
" <td>9</td>\n",
" <td>0</td>\n",
" <td>...</td>\n",
" <td>703</td>\n",
" <td>713</td>\n",
" <td>714</td>\n",
" <td>710</td>\n",
" <td>703</td>\n",
" <td>15</td>\n",
" <td>703</td>\n",
" <td>http://192.168.152.24:8082</td>\n",
" <td>/request4</td>\n",
" <td>?t=687</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"<p>5 rows × 22 columns</p>\n",
"</div>"
],
"text/plain": [
" Time ProxyName TxnID 1 2 3 4 5 6 7 ... \\\n",
"0 2015-12-01 21:34:26 proxy-mpgw 84483 0 450 0 18 18 13 0 ... \n",
"1 2015-12-01 21:34:28 proxy-mpgw 89186 0 246 0 14 14 9 0 ... \n",
"2 2015-12-01 21:34:28 proxy-mpgw 89170 0 1887 0 13 13 7 0 ... \n",
"3 2015-12-01 21:34:28 proxy-mpgw 84515 0 1988 0 14 14 8 0 ... \n",
"4 2015-12-01 21:34:29 proxy-mpgw 89202 0 703 0 15 15 9 0 ... \n",
"\n",
" 10 11 12 13 14 15 16 url \\\n",
"0 450 462 462 457 450 18 450 http://192.168.152.24:8082 \n",
"1 246 259 259 254 246 14 246 http://192.168.152.24:8082 \n",
"2 1887 1900 1900 1896 1887 13 1887 http://192.168.152.24:8082 \n",
"3 1988 2000 2001 1996 1988 14 1988 http://192.168.152.24:8082 \n",
"4 703 713 714 710 703 15 703 http://192.168.152.24:8082 \n",
"\n",
" uri q \n",
"0 /request5 ?t=429 \n",
"1 /request1 ?t=229 \n",
"2 /request4 ?t=1871 \n",
"3 /request1 ?t=1971 \n",
"4 /request4 ?t=687 \n",
"\n",
"[5 rows x 22 columns]"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"df1 = pd.read_csv('sample5.csv', parse_dates=['Time'])\n",
"df1.head()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The first issue to address is what to do with 16 columns of times that have\n",
"[non-intuitive interpretations](https://www.ibm.com/developerworks/community/blogs/HermannSW/entry/latency_messages_in_datapower_appliance21?lang=en).\n",
"We'll take the simple approach of taking the maximum of all 16 columns\n",
"for each row and assigning it to a new columns generically named `Latency`."
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"df1['Latency'] = df1.loc[:,'1':'16'].apply(max, axis=1)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The `loc` method indexes the data frame. The first element specifies rows.\n",
"The `:` means all rows. The second element specifies columns. The expression\n",
"'1':'16' means columns starting with the column named `1` to the column named\n",
"`16`. These are string values (columns names), not integers.\n",
"\n",
"The result is a 16-column data frame with all the rows of the original data\n",
"frame. The `apply` function applies a function to the data frame. We chose\n",
"to apply the `max` function. By default, it is applied across rows\n",
"(`axis=0`). We override this to apply the max across columns, `axis=1`.\n",
"\n",
"Let's create a new data frame that abandons the 16 partial values and only\n",
"includes columns we care about for now."
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>Time</th>\n",
" <th>Latency</th>\n",
" <th>TxnID</th>\n",
" <th>uri</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>2015-12-01 21:34:26</td>\n",
" <td>462</td>\n",
" <td>84483</td>\n",
" <td>/request5</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>2015-12-01 21:34:28</td>\n",
" <td>259</td>\n",
" <td>89186</td>\n",
" <td>/request1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>2015-12-01 21:34:28</td>\n",
" <td>1900</td>\n",
" <td>89170</td>\n",
" <td>/request4</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>2015-12-01 21:34:28</td>\n",
" <td>2001</td>\n",
" <td>84515</td>\n",
" <td>/request1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>2015-12-01 21:34:29</td>\n",
" <td>714</td>\n",
" <td>89202</td>\n",
" <td>/request4</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" Time Latency TxnID uri\n",
"0 2015-12-01 21:34:26 462 84483 /request5\n",
"1 2015-12-01 21:34:28 259 89186 /request1\n",
"2 2015-12-01 21:34:28 1900 89170 /request4\n",
"3 2015-12-01 21:34:28 2001 84515 /request1\n",
"4 2015-12-01 21:34:29 714 89202 /request4"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"df2 = df1.loc[:,['Time', 'Latency', 'TxnID', 'uri']]\n",
"df2.head()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now we can knock ourselves out with all kinds of graphs at our disposal.\n",
"Here is a set of box plots grouped by `uri`."
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"<matplotlib.axes._subplots.AxesSubplot at 0x109c38a50>"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"image/png": "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\n",
"text/plain": [
"<matplotlib.figure.Figure at 0x109b95710>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"df2.boxplot(column=['Latency'], by=['uri'])"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 2",
"language": "python",
"name": "python2"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 2
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython2",
"version": "2.7.10"
}
},
"nbformat": 4,
"nbformat_minor": 0
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.