You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@metron.apache.org by ce...@apache.org on 2016/09/28 21:21:36 UTC

incubator-metron git commit: METRON-456: Create stellar management functions to test grok statements closes apache/incubator-metron#281

Repository: incubator-metron
Updated Branches:
  refs/heads/master 2f6c8bc70 -> 1c6de7fe5


METRON-456: Create stellar management functions to test grok statements closes apache/incubator-metron#281


Project: http://git-wip-us.apache.org/repos/asf/incubator-metron/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-metron/commit/1c6de7fe
Tree: http://git-wip-us.apache.org/repos/asf/incubator-metron/tree/1c6de7fe
Diff: http://git-wip-us.apache.org/repos/asf/incubator-metron/diff/1c6de7fe

Branch: refs/heads/master
Commit: 1c6de7fe50260b3648d758093c08a4701abb95f4
Parents: 2f6c8bc
Author: cstella <ce...@gmail.com>
Authored: Wed Sep 28 17:21:27 2016 -0400
Committer: cstella <ce...@gmail.com>
Committed: Wed Sep 28 17:21:27 2016 -0400

----------------------------------------------------------------------
 metron-platform/metron-management/README.md     |  93 +++++++++-
 metron-platform/metron-management/pom.xml       |  36 ++--
 .../apache/metron/management/GrokFunctions.java | 168 +++++++++++++++++++
 .../metron/management/GrokFunctionsTest.java    |  90 ++++++++++
 4 files changed, 374 insertions(+), 13 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/1c6de7fe/metron-platform/metron-management/README.md
----------------------------------------------------------------------
diff --git a/metron-platform/metron-management/README.md b/metron-platform/metron-management/README.md
index 49da9b9..624a9c0 100644
--- a/metron-platform/metron-management/README.md
+++ b/metron-platform/metron-management/README.md
@@ -19,12 +19,27 @@ project.
 
 The functions are split roughly into a few sections:
 * Shell functions - Functions surrounding interacting with the shell in either a nicer way or a more functional way.
+* Grok Functions - Functions that allow you to evaluate grok expressions.
 * File functions - Functions around interacting with local or HDFS files
 * Configuration functions - Functions surrounding pulling and pushing configs from zookeeper
 * Parser functions - Functions surrounding adding, viewing, and removing Parser functions.
 * Enrichment functions - Functions surrounding adding, viewing and removing Stellar enrichments as well as managing batch size and index names for the enrichment topology configuration
 * Threat Triage functions - Functions surrounding adding, viewing and removing threat triage functions.
 
+### Grok Functions
+
+* `GROK_EVAL`
+  * Description: Evaluate a grok expression for a statement.
+  * Input:
+    * grokExpression - The grok expression to evaluate
+    * data - Either a data message or a list of data messages to evaluate using the grokExpression
+  * Returns: The Map associated with the grok expression being evaluated on the list of messages.
+* `GROK_PREDICT`
+  * Description: Discover a grok statement for an input doc
+  * Input:
+    * data - The data to discover a grok expression from
+  * Returns: A grok expression that should match the data.
+
 ### File Functions
 
 * Local Files
@@ -84,7 +99,6 @@ The functions are split roughly into a few sections:
       * path - The path of the file
     * Returns: true if the file was written and false otherwise.
 
-
 ### Shell Functions 
 
 * `SHELL_EDIT`
@@ -226,6 +240,83 @@ Deployment is as simple as dropping the jar created by this project into
 Included for description and education purposes are a couple example Stellar REPL transcripts
 with helpful comments to illustrate some common operations.
 
+### Iterate in finding a valid Grok pattern
+```
+Stellar, Go!
+Please note that functions are loading lazily in the background and will be unavailable until loaded fully.
+[Stellar]>>> # We are going to debug a squid grok statement with a bug in it
+[Stellar]>>> squid_grok_orig := '%{NUMBER:timestamp} %{SPACE:UNWANTED}  %{INT:elapsed} %{IP:ip_src_addr} %{WORD:action}/%{NUMBER:code} %{NUMBER:bytes} %{WORD:method} %{NOTSPACE:url} 
+ - %{WORD:UNWANTED}/%{IP:ip_dst_addr} %{WORD:UNWANTED}/%{WORD:UNWANTED}'
+[Stellar]>>> # We have gone ot a couple of domains in squid:
+[Stellar]>>> #   1475022887.362    256 127.0.0.1 TCP_MISS/301 803 GET http://www.youtube.com/ - DIRECT/216.58.216.238 text/html
+[Stellar]>>> #   1475022915.731      1 127.0.0.1 NONE/400 3520 GET flimflammadeupdomain.com - NONE/- text/html
+[Stellar]>>> #   1475022938.661      0 127.0.0.1 NONE/400 3500 GET www.google.com - NONE/- text/html
+[Stellar]>>> # Note that flimflammadeupdomain.com and www.google.com did not resolve to IPs
+[Stellar]>>> # We can load up these messages from disk into a list of messages
+[Stellar]>>> messages := LOCAL_READ_LINES( '/var/log/squid/access.log')
+27687 [Thread-1] INFO  o.r.Reflections - Reflections took 26542 ms to scan 22 urls, producing 17906 keys and 121560 values 
+27837 [Thread-1] INFO  o.a.m.c.d.FunctionResolverSingleton - Found 97 Stellar Functions...
+Functions loaded, you may refer to functions now...
+[Stellar]>>> # and evaluate the messages against our grok statement
+[Stellar]>>> GROK_EVAL(squid_grok_orig, messages)
+\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2564\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2564\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2564\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2564\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2564\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2564\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2564\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2564\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
+\u2551 action   \u2502 bytes   \u2502 code    \u2502 elapsed \u2502 ip_dst_addr    \u2502 ip_src_addr \u2502 method  \u2502 timestamp      \u2502 url                     \u2551
+\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563
+\u2551 TCP_MISS \u2502 803     \u2502 301     \u2502 256     \u2502 216.58.216.238 \u2502 127.0.0.1   \u2502 GET     \u2502 1475022887.362 \u2502 http://www.youtube.com/ \u2551
+\u255f\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2562
+\u2551 MISSING  \u2502 MISSING \u2502 MISSING \u2502 MISSING \u2502 MISSING        \u2502 MISSING     \u2502 MISSING \u2502 MISSING        \u2502 MISSING                 \u2551
+\u255f\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2562
+\u2551 MISSING  \u2502 MISSING \u2502 MISSING \u2502 MISSING \u2502 MISSING        \u2502 MISSING     \u2502 MISSING \u2502 MISSING        \u2502 MISSING                 \u2551
+\u255a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2567\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2567\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2567\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2567\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2567\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2567\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2567\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2567\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255d
+
+[Stellar]>>> # Uh oh, looks like the messages without destination IPs do not parse
+[Stellar]>>> # We can start peeling off groups from the end of the message until things parse
+[Stellar]>>> squid_grok := '%{NUMBER:timestamp} %{SPACE:UNWANTED}  %{INT:elapsed} %{IP:ip_src_addr} %{WORD:action}/%{NUMBER:code} %{NUMBER:bytes} %{WORD:method} %{NOTSPACE:url} - %{ 
+WORD:UNWANTED}/%{IP:ip_dst_addr}'
+[Stellar]>>> GROK_EVAL(squid_grok, messages)
+\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2564\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2564\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2564\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2564\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2564\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2564\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2564\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2564\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
+\u2551 action   \u2502 bytes   \u2502 code    \u2502 elapsed \u2502 ip_dst_addr    \u2502 ip_src_addr \u2502 method  \u2502 timestamp      \u2502 url                     \u2551
+\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563
+\u2551 TCP_MISS \u2502 803     \u2502 301     \u2502 256     \u2502 216.58.216.238 \u2502 127.0.0.1   \u2502 GET     \u2502 1475022887.362 \u2502 http://www.youtube.com/ \u2551
+\u255f\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2562
+\u2551 MISSING  \u2502 MISSING \u2502 MISSING \u2502 MISSING \u2502 MISSING        \u2502 MISSING     \u2502 MISSING \u2502 MISSING        \u2502 MISSING                 \u2551
+\u255f\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2562
+\u2551 MISSING  \u2502 MISSING \u2502 MISSING \u2502 MISSING \u2502 MISSING        \u2502 MISSING     \u2502 MISSING \u2502 MISSING        \u2502 MISSING                 \u2551
+\u255a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2567\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2567\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2567\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2567\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2567\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2567\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2567\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2567\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255d
+
+[Stellar]>>> # Still looks like it is having issues...
+[Stellar]>>> squid_grok := '%{NUMBER:timestamp} %{SPACE:UNWANTED}  %{INT:elapsed} %{IP:ip_src_addr} %{WORD:action}/%{NUMBER:code} %{NUMBER:bytes} %{WORD:method} %{NOTSPACE:url} - %{ 
+WORD:UNWANTED}/%{IP:ip_dst_addr}'
+[Stellar]>>> GROK_EVAL(squid_grok, messages)
+\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2564\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2564\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2564\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2564\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2564\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2564\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2564\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2564\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
+\u2551 action   \u2502 bytes   \u2502 code    \u2502 elapsed \u2502 ip_dst_addr    \u2502 ip_src_addr \u2502 method  \u2502 timestamp      \u2502 url                     \u2551
+\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563
+\u2551 TCP_MISS \u2502 803     \u2502 301     \u2502 256     \u2502 216.58.216.238 \u2502 127.0.0.1   \u2502 GET     \u2502 1475022887.362 \u2502 http://www.youtube.com/ \u2551
+\u255f\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2562
+\u2551 MISSING  \u2502 MISSING \u2502 MISSING \u2502 MISSING \u2502 MISSING        \u2502 MISSING     \u2502 MISSING \u2502 MISSING        \u2502 MISSING                 \u2551
+\u255f\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2562
+\u2551 MISSING  \u2502 MISSING \u2502 MISSING \u2502 MISSING \u2502 MISSING        \u2502 MISSING     \u2502 MISSING \u2502 MISSING        \u2502 MISSING                 \u2551
+\u255a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2567\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2567\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2567\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2567\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2567\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2567\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2567\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2567\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255d
+
+[Stellar]>>> # Still looks wrong.  Hmm, I bet it is due to that dst_addr not being there; we can make it optional
+[Stellar]>>> squid_grok := '%{NUMBER:timestamp} %{SPACE:UNWANTED}  %{INT:elapsed} %{IP:ip_src_addr} %{WORD:action}/%{NUMBER:code} %{NUMBER:bytes} %{WORD:method} %{NOTSPACE:url} - %{ 
+WORD:UNWANTED}/(%{IP:ip_dst_addr})?'
+[Stellar]>>> GROK_EVAL(squid_grok, messages)
+\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2564\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2564\u2550\u2550\u2550\u2550\u2550\u2550\u2564\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2564\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2564\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2564\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2564\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2564\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
+\u2551 action   \u2502 bytes \u2502 code \u2502 elapsed \u2502 ip_dst_addr    \u2502 ip_src_addr \u2502 method \u2502 timestamp      \u2502 url                      \u2551
+\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256a\u2550\u2550\u2550\u2550\u2550\u2550\u256a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563
+\u2551 TCP_MISS \u2502 803   \u2502 301  \u2502 256     \u2502 216.58.216.238 \u2502 127.0.0.1   \u2502 GET    \u2502 1475022887.362 \u2502 http://www.youtube.com/  \u2551
+\u255f\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2562
+\u2551 NONE     \u2502 3520  \u2502 400  \u2502 1       \u2502 null           \u2502 127.0.0.1   \u2502 GET    \u2502 1475022915.731 \u2502 flimflammadeupdomain.com \u2551
+\u255f\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2562
+\u2551 NONE     \u2502 3500  \u2502 400  \u2502 0       \u2502 null           \u2502 127.0.0.1   \u2502 GET    \u2502 1475022938.661 \u2502 www.google.com           \u2551
+\u255a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2567\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2567\u2550\u2550\u2550\u2550\u2550\u2550\u2567\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2567\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2567\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2567\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2567\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2567\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255d
+
+[Stellar]>>> # Ahh, that is much better.
+[Stellar]>>> 
+[Stellar]>>> 
+```
+
 ### Manage Stellar Field Transformations
 
 ```

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/1c6de7fe/metron-platform/metron-management/pom.xml
----------------------------------------------------------------------
diff --git a/metron-platform/metron-management/pom.xml b/metron-platform/metron-management/pom.xml
index e0ef0bc..d791809 100644
--- a/metron-platform/metron-management/pom.xml
+++ b/metron-platform/metron-management/pom.xml
@@ -30,18 +30,30 @@
         <antlr.version>4.5</antlr.version>
     </properties>
     <dependencies>
-      <dependency>
-        <groupId>org.apache.metron</groupId>
-        <artifactId>metron-common</artifactId>
-        <version>${project.parent.version}</version>
-        <scope>provided</scope>
-      </dependency>
-      <dependency>
-        <groupId>com.jakewharton.fliptables</groupId>
-        <artifactId>fliptables</artifactId>
-        <version>1.0.2</version>
-      </dependency>
-      <dependency>
+        <dependency>
+            <groupId>org.apache.metron</groupId>
+            <artifactId>metron-common</artifactId>
+            <version>${project.parent.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.metron</groupId>
+            <artifactId>metron-parsers</artifactId>
+            <version>${project.parent.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.jakewharton.fliptables</groupId>
+            <artifactId>fliptables</artifactId>
+            <version>1.0.2</version>
+        </dependency>
+        <dependency>
+            <groupId>io.thekraken</groupId>
+            <artifactId>grok</artifactId>
+            <version>0.1.0</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
             <groupId>com.google.guava</groupId>
             <artifactId>guava</artifactId>
             <version>${global_guava_version}</version>

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/1c6de7fe/metron-platform/metron-management/src/main/java/org/apache/metron/management/GrokFunctions.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-management/src/main/java/org/apache/metron/management/GrokFunctions.java b/metron-platform/metron-management/src/main/java/org/apache/metron/management/GrokFunctions.java
new file mode 100644
index 0000000..2100ae5
--- /dev/null
+++ b/metron-platform/metron-management/src/main/java/org/apache/metron/management/GrokFunctions.java
@@ -0,0 +1,168 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+package org.apache.metron.management;
+
+import com.jakewharton.fliptables.FlipTable;
+import oi.thekraken.grok.api.Grok;
+import oi.thekraken.grok.api.Match;
+import oi.thekraken.grok.api.exception.GrokException;
+import org.apache.log4j.Logger;
+import org.apache.metron.common.dsl.Context;
+import org.apache.metron.common.dsl.ParseException;
+import org.apache.metron.common.dsl.Stellar;
+import org.apache.metron.common.dsl.StellarFunction;
+
+import java.io.InputStreamReader;
+import java.io.StringReader;
+import java.util.*;
+
+public class GrokFunctions {
+
+  private static final Logger LOG = Logger.getLogger(GrokFunctions.class);
+  private static Grok getGrok(String grokExpr) throws GrokException {
+    Grok grok = new Grok();
+    grok.addPatternFromReader(new InputStreamReader(GrokFunctions.class.getResourceAsStream("/patterns/common")));
+    if(grokExpr != null) {
+      grok.addPatternFromReader(new StringReader("pattern " + grokExpr));
+      grok.compile("%{pattern}");
+    }
+    return grok;
+  }
+
+  @Stellar( namespace="GROK"
+          , name="EVAL"
+          , description = "Evaluate a grok expression for a statement"
+          , params = {
+                "grokExpression - The grok expression to evaluate"
+               ,"data - Either a data message or a list of data messages to evaluate using the grokExpression"
+                     }
+          ,returns="The Map associated with the grok expression being evaluated on the list of messages."
+  )
+  public static class Evaluate implements StellarFunction {
+
+    @Override
+    public Object apply(List<Object> args, Context context) throws ParseException {
+      String grokExpression = (String) args.get(0);
+      Object arg =  args.get(1);
+      if(grokExpression == null || arg == null) {
+        return null;
+      }
+      List<String> strs = null;
+      if(arg instanceof List) {
+        strs = (List<String>) arg;
+      }
+      else if(arg instanceof String) {
+        strs = new ArrayList<>();
+        strs.add((String)arg);
+      }
+      else {
+        return null;
+      }
+      Grok grok = null;
+      try {
+        grok = getGrok(grokExpression);
+      } catch (GrokException e) {
+        LOG.error("unable to parse " + grokExpression + ": " + e.getMessage(), e);
+        return null;
+      }
+      List<Map<String, Object>> outputMap = new ArrayList<>();
+      Set<String> keys = new TreeSet<>();
+      for(String str : strs) {
+        Match m = grok.match(str);
+        m.captures();
+        Map<String, Object> ret = m.toMap();
+        if (ret != null && ret.isEmpty()) {
+          outputMap.add(new HashMap<>());
+        } else {
+          ret.remove("pattern");
+          keys.addAll(ret.keySet());
+          outputMap.add(ret);
+        }
+      }
+      if(keys.isEmpty()){
+        return "NO MATCH";
+      }
+      String[] headers = new String[keys.size()];
+      String[][] data = new String[outputMap.size()][keys.size()];
+      {
+        int i = 0;
+        for (String key : keys) {
+          headers[i++] = key;
+        }
+      }
+      int rowNum = 0;
+      for(Map<String, Object> output : outputMap) {
+        String[] row = new String[keys.size()];
+        int colNum = 0;
+        for(String key : keys) {
+          row[colNum++] = "" + output.getOrDefault(key, "MISSING");
+        }
+        data[rowNum++] = row;
+      }
+      return FlipTable.of(headers, data);
+    }
+
+    @Override
+    public void initialize(Context context) {
+
+    }
+
+    @Override
+    public boolean isInitialized() {
+      return true;
+    }
+  }
+
+  @Stellar( namespace="GROK"
+          , name="PREDICT"
+          , description = "Discover a grok statement for an input doc"
+          , params = {
+               "data - The data to discover a grok expression from"
+                     }
+          ,returns="A grok expression that should match the data."
+  )
+  public static class Predict implements StellarFunction {
+
+    @Override
+    public Object apply(List<Object> args, Context context) throws ParseException {
+      String str = (String) args.get(0);
+      if(str == null) {
+        return null;
+      }
+      Grok grok = null;
+      try {
+        grok = getGrok(null);
+      } catch (GrokException e) {
+        LOG.error("unable to construct grok object: " + e.getMessage(), e);
+        return null;
+      }
+      return grok.discover(str);
+    }
+
+    @Override
+    public void initialize(Context context) {
+
+    }
+
+    @Override
+    public boolean isInitialized() {
+      return true;
+    }
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/1c6de7fe/metron-platform/metron-management/src/test/java/org/apache/metron/management/GrokFunctionsTest.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-management/src/test/java/org/apache/metron/management/GrokFunctionsTest.java b/metron-platform/metron-management/src/test/java/org/apache/metron/management/GrokFunctionsTest.java
new file mode 100644
index 0000000..969798b
--- /dev/null
+++ b/metron-platform/metron-management/src/test/java/org/apache/metron/management/GrokFunctionsTest.java
@@ -0,0 +1,90 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+package org.apache.metron.management;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import org.apache.metron.common.dsl.Context;
+import org.apache.metron.common.stellar.StellarTest;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class GrokFunctionsTest {
+  private String grokExpr = "%{NUMBER:timestamp}[^0-9]*%{INT:elapsed} %{IP:ip_src_addr} %{WORD:action}/%{NUMBER:code} %{NUMBER:bytes} %{WORD:method} %{NOTSPACE:url}[^0-9]*(%{IP:ip_dst_addr})?";
+
+
+  @Test
+  public void testGrokEvalSingleMessage() {
+    String message = "1474583120.343    142 127.0.0.1 TCP_MISS/301 494 GET http://cnn.com/ - DIRECT/157.166.226.26 text/html";
+    String out = (String) StellarTest.run( "GROK_EVAL( grok, messages )"
+                                                      , ImmutableMap.of("messages", ImmutableList.of(message), "grok", grokExpr)
+                                                      , Context.EMPTY_CONTEXT()
+                                                      );
+    Assert.assertTrue(out.contains("TCP_MISS"));
+    Assert.assertTrue(out.contains(" 494 "));
+    Assert.assertTrue(out.contains("157.166.226.26"));
+  }
+
+  @Test
+  public void testGrokEvalMultiMessages() {
+    String message = "1474583120.343    142 127.0.0.1 TCP_MISS/301 494 GET http://cnn.com/ - DIRECT/157.166.226.26 text/html";
+    String message2 = "1474583120.343    142 127.0.0.1 TCP_MISS/404 494 GET http://google.com/ - DIRECT/157.166.226.26 text/html";
+    String out = (String) StellarTest.run( "GROK_EVAL( grok, messages )"
+                                                      , ImmutableMap.of("messages", ImmutableList.of(message, message2), "grok", grokExpr)
+                                                      , Context.EMPTY_CONTEXT()
+                                                      );
+    Assert.assertTrue(out.contains("TCP_MISS"));
+    Assert.assertTrue(out.contains(" 494 "));
+    Assert.assertTrue(out.contains("157.166.226.26"));
+    Assert.assertTrue(out.contains("404"));
+  }
+
+  @Test
+  public void testGrokEvalBadData() {
+    String message = "1474583120.343    142 foo TCP_MISS/301 494 GET http://cnn.com/ - DIRECT/157.166.226.26 text/html";
+    String out = (String) StellarTest.run( "GROK_EVAL( grok, message )"
+                                                      , ImmutableMap.of("message", message, "grok", grokExpr)
+                                                      , Context.EMPTY_CONTEXT()
+                                                      );
+    Assert.assertEquals("NO MATCH", out);
+  }
+
+  @Test
+  public void testGrokEvalBadDataMultiMessages() {
+    String message = "1474583120.343    142 foo TCP_MISS/301 494 GET http://cnn.com/ - DIRECT/157.166.226.26 text/html";
+    String message2 = "1474583120.343    142 127.0.0.1 TCP_MISS/404 494 GET http://google.com/ - DIRECT/157.166.226.26 text/html";
+    String out = (String) StellarTest.run( "GROK_EVAL( grok, messages )"
+                                                      , ImmutableMap.of("messages", ImmutableList.of(message, message2), "grok", grokExpr)
+                                                      , Context.EMPTY_CONTEXT()
+                                                      );
+    Assert.assertTrue(out.contains("MISSING"));
+    Assert.assertTrue(out.contains("404"));
+  }
+
+  @Test
+  public void testGrokDiscover() {
+    String out = (String) StellarTest.run("GROK_PREDICT( '1474583120.343    142 127.0.0.1 TCP_MISS/301')"
+                                                      , new HashMap<>()
+                                                      , Context.EMPTY_CONTEXT()
+                                                      );
+    Assert.assertEquals("%{BASE10NUM}    142 %{IP} TCP_MISS%{PATH}", out);
+  }
+}