Tech Topics

Writing Custom Field Formatters for Kibana

Kibana 4.1 introduced a new feature called field formatters, giving us the ability to visually convert fields on the fly.  Field formatters provide a great way to display data in a different way without changing how it is stored.  For a more in depth overview of what field formatters do, you can read a previous blog post here.

The goal for this post is to walk through the steps of creating a new formatter.  We will start by going over the field formatter API, then implement a basic formatter that will draw attention to the word "error", and finally generalize our solution.

Getting Started

Instructions on pulling down the Kibana development environment can be found at the Kibana repository.

Starting from the Kibana root directory, field formatters are stored in /src/ui/public/stringify. The resulting folder structure looks like:

/stringify
|--type  //Contains different formatters
|--icons
|--editors  //HTML used by the formatter to request or display additional information
|--__tests__
|--register.js //Every formatter is registered with the application here

In Kibana 4.1, formatters can be found at /src/kibana/components/stringify. If using 4.1, paths in the code samples will need to be updated.

Let’s create a new file called Highlight.js in the type folder and add some initialization code:

define(function (require) {
  return function HighlightFormatProvider(Private) {
    var _ = require('lodash');
    var FieldFormat = Private(require('ui/index_patterns/_field_format/FieldFormat'));
    _.class(Highlight).inherits(FieldFormat);
    function Highlight(params) {
      Highlight.Super.call(this, params);
    }
    Highlight.id = 'highlight';
    Highlight.title = 'Highlight';
    Highlight.fieldType = ['string'];
    Highlight.prototype._convert = {
      text: _.escape,
      html: _.escape
    };
    return Highlight;
  };
});

A class extending FieldFormat is implemented for each field format.  Highlight.id is used internally by Kibana to keep track of the formatter and should not conflict with any other formatter id.  Highlight.title is the displayed text when selecting from a list of formatters, and Highlight.fieldType describes what types of fields this formatter is available for.

Highlight.prototype._convert is where the formatting happens. It has methods for converting both text and html. The text method is used for tooltips, filters, legends, and axis markers. The html method is used for search tables. Both take a field value as input, and return what we want displayed visually. If you want both methods to behave the same, Highlight.prototype._convert can be assigned as a function instead. Adding code to draw attention to errors looks like:

Highlight.prototype._highlight = function (val, replace) {
  return _.escape(val).replace(/(error)/g, replace);
};
Highlight.prototype._convert = {
  text: function(val) {
    return this._highlight(val, function convertToUpperCase(match) {
      return match.toUpperCase();
    });
  },
  html: function(val) {
    return this._highlight(val, '<mark>$&</mark>');
  }
};

Any time a field has the text "error", we either wrap it in a mark element or convert it to uppercase depending on whether HTML or text is requested. It's important to use _.escape(val) here, we want to avoid HTML injection and cross-site scripting attacks.

We also need to register this field formatter with the application. In register.js we add:

fieldFormats.register(require('ui/stringify/types/Highlight'));

In the future, we (the Kibana team) will be working on providing this functionality as a plugin, which will help simplify the registration process.

We should now be able to add Highlight as a field formatter on fields with a string type!

Testing it on the Discover page shows us it is working:

Generalize

This works, but what if we want to highlight more than just errors? Instead of making a new field formatter for every input, let’s match against an inputted regular expression.

In the editor folder, add a new file called highlight.html with the following:

<div class="form-group">
  <label>Pattern</label>
  <input class="form-control" ng-model="editor.formatParams.pattern"/>
</div>

Back in Highlight.js we need to define highlight.html as our editor and update our _highlight method to use its input field as our pattern to match on.

Highlight.editor = require('ui/stringify/editors/highlight.html');
Highlight.prototype._highlight = function (val, replace) {
  var escapedVal = _.escape(val);
  var highlightPattern;
  try {
    var inputRegex = this.param('pattern').split('/');
    var pattern = inputRegex[0] || inputRegex[1];
    var flags = inputRegex[2];
    highlightPattern = new RegExp(pattern, flags);
  } catch(e) {
    return escapedVal;
  }
  return escapedVal.replace(highlightPattern, replace);
};

Samples

It may be beneficial to see how an inputted pattern behaves before applying the formatter. There’s a directive that has already been built that will let us see some example conversions as we modify our pattern.

We can use this by adding sample field values, and including the directive in our template. In highlight.html, append:

<field-format-editor-samples inputs="editor.field.format.type.sampleInputs"></field-format-editor-samples>

In Highlight.js:

Highlight.sampleInputs = [
  'Hello world',
  'The quick brown fox jumps over the lazy dog',
  '112345'
];

The end result is:

Conclusion

The field formatter API provides an easy way to customize how your fields are displayed. Kibana comes with several built in formatters but in the event that none match your needs, this feature was built in a way to let anyone add their own. If you are planning on adding your own formatters, you can check back here after Kibana 4.2 has been released to be notified of any API changes.