{"id":408,"date":"2016-07-14T20:19:18","date_gmt":"2016-07-14T09:19:18","guid":{"rendered":"http:\/\/scipilot.org\/blog\/?p=408"},"modified":"2016-07-14T20:19:18","modified_gmt":"2016-07-14T09:19:18","slug":"trialling-the-elk-stack-from-elastic","status":"publish","type":"post","link":"https:\/\/scipilot.org\/blog\/2016\/07\/14\/trialling-the-elk-stack-from-elastic\/","title":{"rendered":"Trialling the ELK stack from Elastic"},"content":{"rendered":"<p>As part of my ongoing research into big-data visualisation and infrastructure and application management tools it\u00a0came to give <a href=\"https:\/\/www.elastic.co\/\">ELK<\/a> a test run to check\u00a0if it&#8217;s suited to my needs. I&#8217;ve already looked at a few others (which I will detail in another article), and so far haven&#8217;t found somethings suitable for a SMB to collate and process both &#8220;live&#8221; information from applications, e.g. from existing databases or APIs, combined with &#8220;passive&#8221; information from log files.<\/p>\n<p>Some of the applications I work with are modifiable so\u00a0we can take advantage of APIs to push event-driven data out to analytics or monitoring platforms, but some legacy components are just too hard to upgrade thus\u00a0log-scraping could be the only viable option.\u00a0I already use tools at various stack levels such as New Relic (APM), Datadog\/AWS (infrastructure), Google Analytics (web\/user) and my special interest: custom business-event monitoring. Ideally these various sources could be combined to produce\u00a0some extremely powerful emergent information, but the tools at these different levels are often specific to the needs of that level such as hardware metrics vs. user events and thus difficult to integrate.<\/p>\n<p><strong>My Experiences Trialling Elastic\u00a0<\/strong><\/p>\n<p>It seemed from an initial look that the Elastic stack was flexible and agnostic enough to be able to provide any of these aspects. But would it be a jack-of-all and master-of-none?<\/p>\n<p>To give it a go, I looked at the three main components separately at first. Simply put:<\/p>\n<ol>\n<li><b>LogStash<\/b> &#8211; data acquisition pipeline<\/li>\n<li><b>Elastic Search &#8211; <\/b>search engine &amp; API<\/li>\n<li><b>Kibana &#8211; <\/b>visualisations dashboard<\/li>\n<\/ol>\n<p>The\u00a0provide\u00a0hosted services\u00a0but I didn&#8217;t feel like\u00a0committing just yet\u00a0and didn&#8217;t want to be rushed in a limited-time trial, so\u00a0I downloaded and installed the servers\u00a0locally. I mentally prepared myself for hours of installation and dependency hell\u00a0after my experiences with Graphite and Datalab.<\/p>\n<p>But &#8211; these were my <em>only notes<\/em> during the fantastically quick set-up:<\/p>\n<blockquote>\n<ul>\n<li>Too easy to setup, built in\u00a0Java, \u00a0<em>it<\/em>\u00a0<i>just ran<\/i> on my macbook!<\/li>\n<li>Tutorial: input from my local apache logs files -&gt; elasticsearch, processed <i>really <\/i>quick!<\/li>\n<li>Logstash Grok filter would be key to parsing our log files&#8230;<\/li>\n<\/ul>\n<\/blockquote>\n<p>I just unzipped it and ran it and it worked. I know that&#8217;s how the world <em>should work<\/em>, but this is the first time I&#8217;ve experienced that for years.<\/p>\n<p>Interest piqued, I decided to run through the tutorials and then move on to setting up a real-world log import scenario for a potential client. I noted down the\u00a0things I discovered,\u00a0hopefully they will help other people on a similar first journey. At least it\u00a0will help me when I return to this later and have predictably forgotten it all.<\/p>\n<p><b>LogStash<\/b> &#8211; data acquisition pipeline<\/p>\n<p>I ran through <a href=\"https:\/\/www.elastic.co\/guide\/en\/logstash\/current\/advanced-pipeline.html\">the Apache logs tutorial<\/a>, after completing the <a href=\"https:\/\/www.elastic.co\/guide\/en\/logstash\/current\/getting-started-with-logstash.html\">basic demos<\/a>.<\/p>\n<p>The default index of<a href=\"https:\/\/www.elastic.co\/guide\/en\/logstash\/current\/plugins-outputs-elasticsearch.html\"> logstash-output-elasticsearch<\/a> is &#8220;logstash-%{+YYYY.MM.dd}&#8221;, this is not mentioned in the tutorials. Thus all Apache logs are indexed under this, hence the default search like <code>http:\/\/localhost:9200\/<b>logstash-2016.07.11<\/b>\/_search?q=response=200<\/code><\/p>\n<p>I don&#8217;t think this will be useful in reality &#8211; having an index for every day, but I guess we&#8217;ll get to that later.\u00a0Furthermore the timestamp imported\u00a0is <span style=\"text-decoration: underline;\">today&#8217;s<\/span>\u00a0date, i.e. the time of the import, not the time parsed from the logs. <em>[I will\u00a0address\u00a0this later, below]<\/em><\/p>\n<p>Interesting initial API calls to explore:<\/p>\n<p><a href=\"http:\/\/localhost:9200\/_cat\/indices?v\">http:\/\/localhost:9200\/_cat\/indices?v<\/a> &#8211; all (top level) indices, v = verbose, with headers.<\/p>\n<p><a href=\"http:\/\/localhost:9200\/_cat\/health?v\">http:\/\/localhost:9200\/_cat\/health?v<\/a> \u00a0 &#8211; like Apache status<\/p>\n<p><a href=\"http:\/\/localhost:9200\/_cat\/\">http:\/\/localhost:9200\/_cat\/ <\/a>&#8211; all top level information<\/p>\n<p><b>Grok<\/b> &#8211; import parser<\/p>\n<p>Grok is one of the most important <a href=\"https:\/\/www.elastic.co\/guide\/en\/logstash\/current\/filter-plugins.html\">filter\u00a0plugins<\/a>&#8211; enabling you to parse any log file format, standard or custom. So I quickly tried to write an\u00a0<em>info\u00a0<\/em><i>trace log<\/i> grok filter for some legacy logs I often analyse manually and thus\u00a0know very well. This would make it easier to evaluate the quality and depth\u00a0of the tools &#8211; &#8220;how much more will these tools let me see?&#8221;<\/p>\n<p>My first noobish attempt was an &#8220;inline&#8221; pattern in the Grok configuration. A toe in the water.<\/p>\n<pre>filter {\r\n\u00a0\u00a0\u00a0grok {\r\n\r\n        # example 01\/03\/2016 23:59:43.15 INFO: SENT: 11:59:43 PM DEVICE:123:&lt;COMMAND&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 match =&gt; { \"message\" =&gt; \"%{DATESTAMP:date} %{WORD:logType}: %{WORD:direction}: %{TIME} %{WORD:ampm} DEVICE:%{INT:deviceId}:%{GREEDYDATA:command}\"}\r\n\u00a0\u00a0\u00a0}\r\n}\r\n<\/pre>\n<p>I found it hard to debug at first: it wasn\u2019t importing but I saw no errors. This was <i>because it was working!\u00a0<\/i>It took me a little while to get the trial-and-error configuration debug cycle right. Tips:<\/p>\n<ul>\n<li><a href=\"http:\/\/grokconstructor.appspot.com\/do\/constructionstep\">This grok-debug tool<\/a> was good.<\/li>\n<li><a href=\"http:\/\/grokdebug.herokuapp.com\/\">This one<\/a> also.<\/li>\n<li><a href=\"https:\/\/github.com\/logstash-plugins\/logstash-patterns-core\/tree\/master\/patterns\">Core Grok patterns<\/a> reference is vital<\/li>\n<li>A regex cheat sheet also helps, as Grok is built on it.<\/li>\n<li>Start Logstash with -v to get verbose log output, or even more with &#8211;debug<\/li>\n<li>Restart logstash when you make config changes (duh)<\/li>\n<li>The config is not JSON.\u00a0Obvious but this kept catching me out because most\u00a0other aspects you&#8217;ll need to learn simultaneously are in JSON.\u00a0(what&#8217;s with\u00a0the funny <em>PHP-looking<\/em> key =&gt; values, are they a thing?)<\/li>\n<\/ul>\n<p>OK. I got my custom log imported fairly easily &#8211; but how to I access it?<\/p>\n<p><b>Elastic Search &#8211; <\/b>search engine &amp; API<\/p>\n<p>During my first 5 minutes going through the ES tutorials\u00a0and I noted:<\/p>\n<ul>\n<li>Search uses a verbose <a href=\"https:\/\/www.elastic.co\/guide\/en\/elasticsearch\/reference\/current\/query-dsl.html\">JSON DSL<\/a> via POST (not so\u00a0handy\u00a0for quick browser hackery)<\/li>\n<li>However I found you can do quick GET <a href=\"https:\/\/www.elastic.co\/guide\/en\/elasticsearch\/reference\/current\/query-dsl-query-string-query.html#query-string-syntax\">queries via mini-language<\/a><\/li>\n<li>Search properties: <code>http:\/\/localhost:9200\/logstash-2016.07.12\/_search?q=deviceId:1234&amp;response=200&amp;pretty<\/code><\/li>\n<li>To return specific properties e.g.: <code>&amp;_source=logType,direction,command<\/code><\/li>\n<li><i>Scores<\/i> &#8211; this is search-engine stuff (relevance, probabilities, distances) as opposed to SQL \u201cperfect\u201d responses.<\/li>\n<li>Aggregates (like GROUP) for stats, didn\u2019t try them, POST only and I\u2019m lazy today, but they look good. Would probably explore them more in Kibana<\/li>\n<\/ul>\n<p>Great &#8211; the REST API looks really good, easily explorable with every feature I had hoped for and more. The &#8220;scores&#8221; aspect made me realise that this isn&#8217;t just a data API, this is a proper search engine too, with interesting features such as fuzziness and Levenshtein distances. I hadn&#8217;t really thought of using that &#8211; from a traditional data accuracy perspective this seemed\u00a0all a bit too gooey, but\u00a0perhaps there will be a niche I could use it for.<\/p>\n<p><b>Kibana &#8211; <\/b>for visualisations<\/p>\n<blockquote>\n<ul>\n<li>Download \u201cinstalled\u201d tar.gz &#8211; again worked perfectly, instantly.<\/li>\n<li>Ran on <a href=\"http:\/\/0.0.0.0:5601\">http:\/\/0.0.0.0:5601<\/a>, it set itself up<\/li>\n<li>Created default <i>index pattern<\/i> on <b>logstash-*<\/b> and instantly saw all the data from above import.<\/li>\n<\/ul>\n<\/blockquote>\n<p>So again great, this was &#8220;too easy&#8221; to get up and running, literally within 5 minutes I was exploring the data from the Apache tutorial in wonderful technicolor.<\/p>\n<p>So after a broad sweep I was feeling good about this stack so I felt it was time to go a bit deeper.<\/p>\n<p><b>Mappings<\/b><\/p>\n<p>It seems the next (<em>it should have been first!<\/em>) major task is to map the fields to types, else they all end up as <em>fieldname<\/em>.raw as strings.<\/p>\n<p>But&#8230;\u00a0<a href=\"https:\/\/www.elastic.co\/guide\/en\/elasticsearch\/reference\/current\/mapping.html#_updating_existing_mappings\">you cannot change mappings<\/a>\u00a0on existing data &#8211; so you must set them up first! However you can create a new index, and re-index the data\u2026 somehow, but I found it easier to start again for the moment.<\/p>\n<p>I couldn&#8217;t figure out (or there isn&#8217;t) a\u00a0mini-language for a GET to create mappings, so I used the example CURL commands which weren&#8217;t as annoying as I&#8217;d thought they&#8217;d be &#8211;\u00a0except I do use my browser URL history as my memory. It&#8217;s just a bit harder to re-edit and re-use SSH shell histories, than in-browser URLs.<\/p>\n<pre>curl -XPUT http:\/\/localhost:9200\/ra-info-tracelog -d '\r\n{\r\n\u00a0\"mappings\": {\r\n\u00a0\u00a0\u00a0\"log\": {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\"properties\": {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"date\": { \"type\" : \"date\" },\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"deviceId\": { \"type\" : \"integer\" }\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}\r\n\u00a0\u00a0\u00a0\u00a0\u00a0}\r\n\u00a0\u00a0\u00a0}\r\n\u00a0}\r\n}\r\n';\r\n<\/pre>\n<p>&nbsp;<\/p>\n<p><b>Getting the date format right\u2026<\/b><\/p>\n<p>The legacy server,\u00a0from which these logs came,\u00a0doesn\u2019t use a strict datetime format (sigh), and\u00a0 Logstash was erroring.<\/p>\n<p><code># example 01\/03\/2016 23:59:43.15<\/code><\/p>\n<p>Initially I tried to write\u00a0a \u201cCustom Pattern\u201d but then I found the Grok date property format <em>should<\/em> be able to handle it, even with 2 digits of microseconds (default is 3 digits). To figure this out, I had to manually traverse the <a href=\"https:\/\/github.com\/logstash-plugins\/logstash-patterns-core\/tree\/master\/patterns\">tree of patterns\u00a0in the core library<\/a>\u00a0from DATESTAMP down through its children. This was actually a good exercise to learn how the match patterns work &#8211; very much\u00a0like an NLP grammar definition (my AI degree is all coming back to me).<\/p>\n<p>So why is Grok erroring when the pattern is correct?<\/p>\n<p>It took me a while to realise it&#8217;s because the Grok DATESTAMP pattern is just a regexp to parse\u00a0the message data into pieces but\u00a0is <em>more permissive<\/em> than the default date field mapping specification in the subsequent Elasticsearch output stage. So it tokenises the date syntactically, but it\u2019s the field mapping which then tries to interpret it semantically which\u00a0fails.<\/p>\n<p>OK, so I felt\u00a0I should write a custom property mapping to accommodate the legacy format.<\/p>\n<pre>    \"date\": {\r\n        \"type\" : \"date\" ,\r\n        \"format\": \"dd\/MM\/yyyy HH:mm:ss.SS\"\r\n    },\r\n<\/pre>\n<p>Mild annoyance alert: To do these changes I had to keep re-creating the indexes and changing the output spec, restarting Logstash and changing my debug query URLs. So it&#8217;s worth\u00a0learning how to re-index data, or (when doing it for real) get this right first in an IA scoping stage.<\/p>\n<p><i>Tip: I debugged this\u00a0by pasting very specific single logs one line at a time into the test.log file which Logstash is monitoring. Don\u2019t just point it a huge log file!<\/i><\/p>\n<p><strong>So many formats!<\/strong><\/p>\n<p>The date mappings are in <i>yet another<\/i> language\/specification\/format\/standard called <a href=\"http:\/\/www.joda.org\/joda-time\/apidocs\/org\/joda\/time\/format\/DateTimeFormat.html\">Joda<\/a>. At this point I started to feel a little overwhelmed with all the different formats you need to learn to get a job done. I don\u2019t mind learning a format, but I\u2019m already juggling three or four new syntaxes and switching between them when I realise I need to move a filtering task to a different layer is an off-putting mix of laborious and confusing.<\/p>\n<p>For example I just learned how to make optional matches in Grok patterns, but I can\u2019t apply it here and do \u201cHH:mm:ss.SS(S)?\u201d to cope with log-oddities, which is a frustrating dead-end for this approach. So I have to look again at all the other layers to see how I can resolve this with a more flexible tool.<\/p>\n<p>OK, once the date mapping works\u2026 it all imports successfully.<\/p>\n<p><strong>Creating\u00a0Kibana visualisation<\/strong><\/p>\n<p>To use this time field you create a new <i>index pattern in <\/i>Kibana&gt;Settings&gt;Indices and select the \u201cdate\u201d field above as the \u201cTime-field name\u201d, otherwise it will use the <i>import<\/i> time \u00a0as the timestamp &#8211; which won\u2019t be right when we\u2019re importing older logs. (It will be<i> almost<\/i> right if\u00a0logs are being written and processed immediately but this won&#8217;t be accurate enough for me).<\/p>\n<p>I loaded in a few hundred thousand logs, and viewed them immediately in Kibana\u2026 which looks good! There are immediately all the UI filters from my dreams, it looks like it will do everything I want.<\/p>\n<p><strong>But there\u2019s an easier way!<\/strong><\/p>\n<p><a href=\"https:\/\/www.elastic.co\/guide\/en\/logstash\/current\/plugins-filters-date.html\">The Date filter<\/a> allows you to immediately parse a date from a field into the default @timestamp field.\u00a0\u201c<i>The date filter is especially important for sorting events and for backfilling old data.<\/i>\u201d which is exactly what I&#8217;m doing.<\/p>\n<p>So I made a new filter config:<\/p>\n<pre>filter {\r\n    grok {\r\n        match =&gt; { \"message\" =&gt; \"%{DATESTAMP:date} ... \u201c}\r\n    }\r\n    date {\r\n        match =&gt; [\"date\", \"dd\/MM\/yyyy HH:mm:ss.SS\"]\r\n    }\r\n}\r\n<\/pre>\n<p>And it turned up like this (note: without using the field mapping above, so the redundant \u201cdate\u201d property here is just a string). Also note the UTC conversion, which wqs a little confusing\u00a0at first especially as I unwittingly\u00a0chose a to test a\u00a0log\u00a0across the rare 29th of Feb!<\/p>\n<pre>    \"@timestamp\" : \"2016-02-29T13:00:23.600Z\",\r\n    \"date\" : \"01\/03\/2016 00:00:23.60\",\r\n<\/pre>\n<p>The desired result was achieved: this showed up in Kibana <em>instantly<\/em>, without having to specify a custom time-field name.<\/p>\n<p>I got it wrong initially, but at least that helped me to understand what these log-processing facilities\u00a0are saving you from having to do later (possibly many times).<\/p>\n<p><b>Making more complex patterns<\/b><\/p>\n<p>The legacy log format I&#8217;m trialling has typical info\/warning\/error logs, but each type also has a mix of a few formats for different events.\u00a0To break down these various log types, you need\u00a0to implement a\u00a0grammar tree of expressions in custom\u00a0Grok Patterns.<\/p>\n<p>The first entries should be the component \u201celements\u201d such as verbose booleans or enumerations<\/p>\n<p><code>\u00a0 \u00a0 ONOFF (?:On|Off)<\/code><br \/>\n<code>\u00a0 \u00a0 LOG_TYPE (?:INFO|WARNING|ERROR|DEBUG)<\/code>.<\/p>\n<p>If a log file has various entry types, like sent\/received, connection requests and other actions then the next entries should be matching the various log-line variants composed of those custom elements and any standard patterns from the core libraries.<br \/>\ne.g.<\/p>\n<pre># e.g. 01\/02\/2016 01:02:34.56 INFO: Connection request: device:1234 from 123.45.56.78:32770 ()\r\nINFO_CONNECTION_REQUEST %{DATESTAMP:date} %{LOG_TYPE:logType}: Connection request: device:%{INT:deviceId} from %{HOSTPORT:deviceIP} \\(%{HOSTNAME}\\)\r\n<\/pre>\n<p>Then finally you have one log-line super-type which matches any log-line variants<br \/>\ne.g.:<\/p>\n<pre>INFO_LINE %{INFO_COMMAND}|%{INFO_CONNECTION_REQUEST}|%{INFO_CONNECTION_CLOSED}|%{INFO_STATUS}\r\n<\/pre>\n<p>Again the tools mentioned above were crucial in diagnosing the records which arrive in Elasticsearch tagged with _grokparsefailure while you are developing these patterns.<\/p>\n<p>For annoyingly \u201cflexible\u201d legacy log formats I found these useful:<\/p>\n<ul>\n<li>Optional element: <code>( <i>exp<\/i> )?<\/code>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 e.g. <code>(%{HOSTNAME})?<\/code><\/li>\n<li>Escape brackets: \u00a0 \u00a0\\( \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 e.g. <code>\\(%{INT:userId}\\)<\/code><\/li>\n<li>Non-capturing group: <code>(?: <i>exp<\/i> )<\/code> e.g. <code>(?:INFO|WARNING|ERROR|DEBUG)<\/code><\/li>\n<\/ul>\n<p><b>Differentiating log variants in the resulting data<\/b><\/p>\n<p>Next I wanted to be able to differentiate which log-line variant each log had actually matched. This turned out to be harder than I had thought. There doesn\u2019t seem to be a mechanism within the regular-expression matching capabilities of the Grok patterns such as to be able to \u201cset a constant\u201d when a specific pattern matches.<\/p>\n<p>The accepted method is to use logic in the pipeline configuration file plus the abilities to add_tags or add_fields in the Grok configuration. This approach is sadly a bit wet (not DRY) as you have to repeat the common configuration options for each variant. I tried to find other solutions, but currently I haven\u2019t resolved the repetitions.<br \/>\ne.g.<\/p>\n<pre>grok {\r\n    match =&gt; { \"message\" =&gt; \"%{INFO_CONNECTION_CLOSED}\" }\r\n    patterns_dir =&gt; [\"mypatterns\"]\r\n    add_tag =&gt; [ \"connection\" ]\r\n}\r\ngrok {\r\n    match =&gt; { \"message\" =&gt; \"%{RA_INFO_LINE}\" }\r\n    patterns_dir =&gt; [\"mypatterns\"]\r\n}\r\n<\/pre>\n<p>However this can also result in a false _grokfailure tag, because the two configurations are run <em>sequentially<\/em>, regardless of a match. So if the first one matches, the second will fail.<\/p>\n<p>One <a href=\"http:\/\/stackoverflow.com\/a\/24708690\/209288\">solution<\/a> is to use logic to check the results of the match as you progress.<\/p>\n<pre>    grok {\r\n        match =&gt; { \"message\" =&gt; \"%{INFO_CONNECTION_CLOSED}\" }\r\n        patterns_dir =&gt; [\"mypatterns\"]\r\n        add_tag =&gt; [ \"connection\" ]\r\n    }\r\n    if (\"connection\" not in [tags]) {\r\n        grok {\r\n            match =&gt; { \"message\" =&gt; \"%{INFO_LINE}\" }\r\n            patterns_dir =&gt; [\"mypatterns\"]\r\n        }\r\n    }\r\n<\/pre>\n<p>This works well, and for these log-line variants, I\u2019m now getting a \u201cconnection\u201d tag, which can enable API queries\/Kibana to know to expect a totally different set of properties for items in the same index. I see this tag as a kind of \u201cclassname\u201d &#8211; but I don\u2019t know yet if I\u2019m going down the right road with that OO thought train!<\/p>\n<pre>    \"@timestamp\" : \"2016-02-29T13:00:25.520Z\",\r\n    \"logType\" : [ \"INFO\", \"INFO\" ],\r\n    \"deviceId\" : [ \"123\", \"123\" ],\r\n    \"connection_age\" : [ \"980\", \"980\" ],\r\n    \"tags\" : [ \"connection\" ]\r\n<\/pre>\n<p><a href=\"http:\/\/stackoverflow.com\/questions\/20849583\/how-to-handle-non-matching-logstash-grok-filters\">Another method<\/a> is to \u201cpre-parse\u201d the message and only perform certain groks for specific patterns. But again it still feels like this is duplicating work from the patterns.<\/p>\n<pre>    if [message] =~ \/took\\s\\d+\/ { grok { ... } }\r\n<\/pre>\n<p>Even with the conditional in place above,\u00a0the first filter technically fails before the second one succeeds. This means the first failure will <span style=\"text-decoration: underline;\">still<\/span> add a \u201c_grokparsefailure\u201d tag to an eventually successful import!<\/p>\n<p>The final workaround is to manually remove the failure tags in <i>all but the last<\/i> filter:<\/p>\n<pre>    grok {\r\n        match =&gt; { \"message\" =&gt; \"%{INFO_CONNECTION_CLOSED}\" }\r\n        add_tag =&gt; [ \"connection\" ]\r\n        # don't necessarily fail yet...\r\n        tag_on_failure =&gt; [ ]\r\n    }\r\n<\/pre>\n<p>So while I am still very impressed with the ELK stack, I am starting to see coping with real-world complexities isn&#8217;t straightforward and is leading to some relatively hacky and unscalable techniques due to the limited configuration language. It&#8217;s these details that will sway people from one platform to another, but it&#8217;s difficult to find those sticking points until you&#8217;ve really fought with it &#8211; as Seraph so wisely put it.<\/p>\n<p><b>Loading up some &#8220;big&#8221; data<\/b><\/p>\n<p>Now I was ready to import a chunk\u00a0of old logs and give it a good test run. I have a lot &#8211; a lot &#8211; of potential archive data going back years. It seemed to import fairly quickly even on my old 6yr-old MacBookPro Logstash chewed 200,000 logs into Eleasticsearch in a few\u00a0minutes. (I know this is tiny data, but it&#8217;s all I had in my clipboard at the time.) I&#8217;m looking forward to testing millions of logs on a more production tuned server and benchmarking it with proper indexing and schema set up.<\/p>\n<p>Heading back to Kibana, I was able to explore the data more thoroughly now it\u2019s a bit more organised. The main process goes through:<\/p>\n<ol>\n<li>data discovery<\/li>\n<li>to making visualisations<\/li>\n<li>and then arranging them on dashboards.<\/li>\n<\/ol>\n<p>This process is intuitive and exactly what you want to do. You can explore the data by building queries with the help of the GUI, or hand-craft some of it with more knowledge of the Elasticsearch API, then you can save these queries for re-use later in the visualisation tools.<\/p>\n<p>Even in the default\u00a0charts, I instantly saw some interesting patterns including blocks of missing data which looked like a server outage, unusual spikes of activity, and the typical camel-humps of the weekend traffic patterns. These patterns are difficult to spot in the raw logs, unless you have Cipher eyes.<\/p>\n<p>I had a quick look at the custom visualisations, particularly the bar-charts, and found you can quite easily create sub-groups from various fields and I started to realise how powerful the post-processing capabilities of Kibana could be in slicing up the resulting data further.<\/p>\n<p><b>Summary thoughts<\/b><\/p>\n<p>In summary I feel the ELK stack can certainly do what I set out to achieve &#8211; getting business value out of gigabytes of old logs and current logs without having to modify legacy servers. I feel it could handle both infrastructure level monitoring and the custom business-events both stored in logs and fired from our APIs and via MQs. <\/p>\n<p>The component architecture and exposed REST API is also flexible enough to be able to easily feed\u00a0into other existing data-processing pipelines instead of Kibana, including my latest pet-project <a href=\"https:\/\/github.com\/scipilot\/logline\/\">Logline<\/a>\u00a0which visualises mashups of event-driven logs from various sources using the <a href=\"http:\/\/visjs.org\/\">Vis.org<\/a> Timeline.<\/p>\n<p><b>Next steps<\/b><\/p>\n<p>I feel now I\u2019m ready to present this back to the folks at the organisations I consult for and confidently offer it as a viable solution. It offers tools for building a business intelligence analysis platform and with the addition of the monitoring tools such as Watcher potentially bring that post-rational intelligence into real-time.<\/p>\n<p>Beyond that &#8211; the next step could even be predicting the future, but that\u2019s another story.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>As part of my ongoing research into big-data visualisation and infrastructure and application management tools it\u00a0came to give ELK a test run to check\u00a0if it&#8217;s suited to my needs. I&#8217;ve already looked at a few others (which I will detail &hellip; <a href=\"https:\/\/scipilot.org\/blog\/2016\/07\/14\/trialling-the-elk-stack-from-elastic\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":3,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[17],"tags":[67,66,41,21,33,39],"class_list":["post-408","post","type-post","status-publish","format-standard","hentry","category-articles","tag-analytics","tag-big-data","tag-performance","tag-rd","tag-telematics","tag-visualisation"],"_links":{"self":[{"href":"https:\/\/scipilot.org\/blog\/wp-json\/wp\/v2\/posts\/408","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/scipilot.org\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/scipilot.org\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/scipilot.org\/blog\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/scipilot.org\/blog\/wp-json\/wp\/v2\/comments?post=408"}],"version-history":[{"count":5,"href":"https:\/\/scipilot.org\/blog\/wp-json\/wp\/v2\/posts\/408\/revisions"}],"predecessor-version":[{"id":413,"href":"https:\/\/scipilot.org\/blog\/wp-json\/wp\/v2\/posts\/408\/revisions\/413"}],"wp:attachment":[{"href":"https:\/\/scipilot.org\/blog\/wp-json\/wp\/v2\/media?parent=408"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/scipilot.org\/blog\/wp-json\/wp\/v2\/categories?post=408"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/scipilot.org\/blog\/wp-json\/wp\/v2\/tags?post=408"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}