diff --git a/app/assets/stylesheets/graylog2.less b/app/assets/stylesheets/graylog2.less index b8c1ab39d539..a5ef32549601 100644 --- a/app/assets/stylesheets/graylog2.less +++ b/app/assets/stylesheets/graylog2.less @@ -416,20 +416,11 @@ a.fields-set-chooser { color: #26ADE4; } -.terms-msg-modal .modal-header { - font-size: 11px; -} - -.terms-msg-modal .modal-body li { - float: left; +.message-terms { margin-right: 8px; font-family: monospace; } -.terms-msg-modal .modal-body .as-list li { - float: none !important; -} - .support-sources ul { margin-top: 5px; } @@ -2945,10 +2936,10 @@ table.messages tr.message-detail-row dl.message-details-fields dd { border-bottom: 1px solid #ececec; } -table.messages tr.message-detail-row dl.message-details dd ul { - list-style-type: disc; - padding-left: 25px; -} +//table.messages tr.message-detail-row dl.message-details dd ul { +// list-style-type: disc; +// padding-left: 25px; +//} table.messages tr.message-detail-row dl.message-details dd ul li { margin-top: 3px; diff --git a/app/controllers/MessagesController.java b/app/controllers/MessagesController.java index 27f8516e4576..784fc25e37c9 100644 --- a/app/controllers/MessagesController.java +++ b/app/controllers/MessagesController.java @@ -150,12 +150,12 @@ public Result analyze(String index, String id, String field) { try { MessageResult message = messagesService.getMessage(index, id); - String analyzeField = (String) message.getFilteredFields().get(field); - if (analyzeField == null || analyzeField.isEmpty()) { + Object analyzeField = message.getFilteredFields().get(field); + if (analyzeField == null || (analyzeField instanceof String) && ((String)analyzeField).isEmpty()) { return status(404, "Message does not have requested field " + field); } - - MessageAnalyzeResult result = messagesService.analyze(index, analyzeField); + final String stringifiedValue = String.valueOf(analyzeField); + MessageAnalyzeResult result = messagesService.analyze(index, stringifiedValue); return ok(Json.toJson(result.getTokens())); } catch (IOException e) { return status(500, views.html.errors.error.render(ApiClient.ERROR_MSG_IO, e, request())); diff --git a/app/views/partials/navbar.scala.html b/app/views/partials/navbar.scala.html index 06a0f091eb19..1f1bf1549544 100644 --- a/app/views/partials/navbar.scala.html +++ b/app/views/partials/navbar.scala.html @@ -26,6 +26,7 @@ routes.javascript.SystemController.index, routes.javascript.NodesController.nodes, routes.javascript.NodesController.node, + routes.javascript.MessagesController.analyze, routes.javascript.MessagesController.show, routes.javascript.InputsController.index, routes.javascript.OutputsController.index, diff --git a/javascript/src/components/search/MessageDetail.jsx b/javascript/src/components/search/MessageDetail.jsx index f074e9437de0..266c09780fd0 100644 --- a/javascript/src/components/search/MessageDetail.jsx +++ b/javascript/src/components/search/MessageDetail.jsx @@ -9,14 +9,19 @@ var Row = require('react-bootstrap').Row; var Col = require('react-bootstrap').Col; var OverlayTrigger = require('react-bootstrap').OverlayTrigger; var Tooltip = require('react-bootstrap').Tooltip; +var SplitButton = require('react-bootstrap').SplitButton; +var MenuItem = require('react-bootstrap').MenuItem; +var Alert = require('react-bootstrap').Alert; var ReactZeroClipboard = require('react-zeroclipboard'); var Immutable = require('immutable'); +var MessagesStore = require('../../stores/messages/MessagesStore'); var MessageDetail = React.createClass({ getInitialState() { return { - scrollWarn: false + scrollWarn: false, + messageTerms: Immutable.Map() }; }, componentDidMount() { @@ -40,20 +45,42 @@ var MessageDetail = React.createClass({ : stopped node; }, + _loadTerms(field) { + return () => { + var promise = MessagesStore.fieldTerms(this.props.message.index, this.props.message.id, field); + promise.done((terms) => this._onTermsLoaded(field, terms)) + } + }, + _onTermsLoaded(field, terms) { + var map = Immutable.Map().set(field, terms); + this.setState({messageTerms: map}); + }, render() { var messageUrl = jsRoutes.controllers.MessagesController.show(this.props.message.index, this.props.message.id).url; var fields = []; var formattedFields = Immutable.Map(this.props.message['formatted_fields']); + var idx = 0; formattedFields.forEach((value, key) => { + idx++; + // this is to work around the fact that the container has an overflow-y: auto. well :grumpy: + var shouldDropup = !!(formattedFields.size > 4 && idx >= formattedFields.size - 1); + var showTerms = this.state.messageTerms.has(key); + var terms = showTerms ? Immutable.fromJS(this.state.messageTerms.get(key)) : Immutable.List(); + var termsMarkup = []; + terms.forEach((term, idx) => termsMarkup.push({term})); fields.push(
{key}
); fields.push(
- - + } key={1}> + Create extractor for field {key} + Show terms of {key} +
{value} + {showTerms &&
} + {showTerms && this.setState({messageTerms: Immutable.Map()})}>Field terms: {termsMarkup}}
); }); @@ -87,8 +114,6 @@ var MessageDetail = React.createClass({ - -

{this.props.message.id}

@@ -118,7 +143,7 @@ var MessageDetail = React.createClass({ -
+
{this.state.scrollWarn ? Scroll field list for more details... : null}
{fields} diff --git a/javascript/src/stores/messages/MessagesStore.ts b/javascript/src/stores/messages/MessagesStore.ts index 608d05a948ce..804ad124e9e9 100644 --- a/javascript/src/stores/messages/MessagesStore.ts +++ b/javascript/src/stores/messages/MessagesStore.ts @@ -3,6 +3,7 @@ 'use strict'; declare var $: any; +declare var jsRoutes: any; import UserNotification = require("../../util/UserNotification"); import URLUtils = require("../../util/URLUtils"); @@ -30,6 +31,16 @@ var MessagesStore = { "Could not load message information"); }); return promise; + }, + + fieldTerms(index: string, messageId: string, field: string): JQueryPromise> { + var url = jsRoutes.controllers.MessagesController.analyze(index, messageId, field).url; + var promise = $.getJSON(url); + promise.fail((jqXHR, textStatus, errorThrown) => { + UserNotification.error("Loading field terms failed with status: " + errorThrown, + "Could not load field terms."); + }); + return promise; } };