{"id":113,"date":"2015-04-28T18:42:40","date_gmt":"2015-04-28T18:42:40","guid":{"rendered":"http:\/\/svops.com\/blog\/?p=113"},"modified":"2015-04-28T18:42:40","modified_gmt":"2015-04-28T18:42:40","slug":"handling-log-levels-in-logstash","status":"publish","type":"post","link":"http:\/\/svops.com\/blog\/handling-log-levels-in-logstash\/","title":{"rendered":"Handling log levels in logstash"},"content":{"rendered":"<p>Most logging frameworks include a concept of severity or priority, including tags like &#8220;WARNING&#8221;, &#8220;CRITICAL&#8221;, etc.<\/p>\n<p>Breaking these off into their own fields in logstash makes a lot of sense. \u00a0Unfortunately, when you go to look for problems, you end up with a search like this:<\/p>\n<pre>log_level:(\"EMERG\" OR \"ALERT\" OR \"CRIT\" OR \"ERROR\")<\/pre>\n<p>which is both inefficient (5 string comparisons) and unclear (did I miss or misspell one?).<\/p>\n<p>What I like to do is create an <em>additional,<\/em>\u00a0<em>numeric<\/em> representation of the log level, so that my search looks like this:<\/p>\n<pre>log_code:&lt;=3<\/pre>\n<p>With the two fields, you can easily query for bad stuff, but still use log_level for display (in aggregations, etc).<\/p>\n<p>I have standardized on the <a href=\"http:\/\/httpd.apache.org\/docs\/2.2\/mod\/core.html#loglevel\">Apache LogLevel definitions<\/a>:<\/p>\n<table class=\"bordered\" style=\"height: 612px;\" width=\"811\">\n<tbody>\n<tr>\n<th><strong>Level<\/strong><\/th>\n<th><strong>Description<\/strong><\/th>\n<th><strong>Example<\/strong><\/th>\n<\/tr>\n<tr>\n<td><code>emerg<\/code><\/td>\n<td>Emergencies &#8211; system is unusable.<\/td>\n<td>&#8220;Child cannot open lock file. Exiting&#8221;<\/td>\n<\/tr>\n<tr>\n<td><code>alert<\/code><\/td>\n<td>Action must be taken immediately.<\/td>\n<td>&#8220;getpwuid: couldn&#8217;t determine user name from uid&#8221;<\/td>\n<\/tr>\n<tr>\n<td><code>crit<\/code><\/td>\n<td>Critical Conditions.<\/td>\n<td>&#8220;socket: Failed to get a socket, exiting child&#8221;<\/td>\n<\/tr>\n<tr>\n<td><code>error<\/code><\/td>\n<td>Error conditions.<\/td>\n<td>&#8220;Premature end of script headers&#8221;<\/td>\n<\/tr>\n<tr>\n<td><code>warn<\/code><\/td>\n<td>Warning conditions.<\/td>\n<td>&#8220;child process 1234 did not exit, sending another SIGHUP&#8221;<\/td>\n<\/tr>\n<tr>\n<td><code>notice<\/code><\/td>\n<td>Normal but significant condition.<\/td>\n<td>&#8220;httpd: caught SIGBUS, attempting to dump core in &#8230;&#8221;<\/td>\n<\/tr>\n<tr>\n<td><code>info<\/code><\/td>\n<td>Informational.<\/td>\n<td>&#8220;Server seems busy, (you may need to increase StartServers, or Min\/MaxSpareServers)&#8230;&#8221;<\/td>\n<\/tr>\n<tr>\n<td><code>debug<\/code><\/td>\n<td>Debug-level messages<\/td>\n<td>&#8220;Opening config file &#8230;&#8221;<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>My logstash config is broken into many smaller pieces. \u00a0The filter{} stanza for each type of log is contained in a separate file, and there are generic stanzas that run before and after the business logic.<\/p>\n<p>If you&#8217;re processing a log file whose levels are different, you need to normalize them. \u00a0The translate{} filter is great for this:<\/p>\n<pre>translate {\r\n  dictionary =&gt; [\r\n    \"WRN\", \"warn\",\r\n    \"INF\", \"info\",\r\n    \"DBG\", \"debug\"\r\n  ]\r\n  field =&gt; \"[loglevel][tmp]\"\r\n  destination =&gt; \"[loglevel][name]\"\r\n  remove_field =&gt; [ \"[loglevel][tmp]\" ]\r\n}<\/pre>\n<p>From what I can tell, translate{} won&#8217;t replace the source field, so the earlier grok{} matches into a temporary variable that is removed here.<\/p>\n<p>Once [loglevel][name] is normalized, I use a post-processing config file to add the second [loglevel][code] field:<\/p>\n<pre>if [loglevel] and [loglevel][name] {\r\n  translate {\r\n    dictionary =&gt; [\r\n      \"emerg\", \"0\",\r\n      \"alert\", \"1\",\r\n      \"crit\", \"2\",\r\n      \"error\", \"3\",\r\n      \"warn\", \"4\",\r\n      \"notice\", \"5\",\r\n      \"info\", \"6\",\r\n      \"debug\", \"7\"\r\n    ]\r\n    field =&gt; \"[loglevel][name]\"\r\n    destination =&gt; \"[loglevel][code]\"\r\n  }\r\n\r\n  # make it an int\r\n  mutate {\r\n    convert =&gt; [ \"[loglevel][code]\", \"integer\" ]\r\n  }<\/pre>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Most logging frameworks include a concept of severity or priority, including tags like &#8220;WARNING&#8221;, &#8220;CRITICAL&#8221;, etc. Breaking these off into their own fields in logstash makes a lot of sense. \u00a0Unfortunately, when you go to look for problems, you end &hellip; <a href=\"http:\/\/svops.com\/blog\/handling-log-levels-in-logstash\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[14],"tags":[],"_links":{"self":[{"href":"http:\/\/svops.com\/blog\/wp-json\/wp\/v2\/posts\/113"}],"collection":[{"href":"http:\/\/svops.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/svops.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/svops.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/svops.com\/blog\/wp-json\/wp\/v2\/comments?post=113"}],"version-history":[{"count":1,"href":"http:\/\/svops.com\/blog\/wp-json\/wp\/v2\/posts\/113\/revisions"}],"predecessor-version":[{"id":114,"href":"http:\/\/svops.com\/blog\/wp-json\/wp\/v2\/posts\/113\/revisions\/114"}],"wp:attachment":[{"href":"http:\/\/svops.com\/blog\/wp-json\/wp\/v2\/media?parent=113"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/svops.com\/blog\/wp-json\/wp\/v2\/categories?post=113"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/svops.com\/blog\/wp-json\/wp\/v2\/tags?post=113"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}