I am using the example from the Splunk Dashboard Examples for Table Cell Highlighting, I'm using Splunk 7.0.2. I am creating a chart where only one of the column names is known ahead of time, "testcase". I need to color cells for every cell except those in the testcase column. Per the example, it looks as though the canRender function returns true for any cell which requires rendering, i.e. for any cell which may need color added. I have tried coding the canRender function to return true if cell.field for any field other than the testcase field, but in all my attempts, the render function is still invoked for every cell, including the testcase field. Here are two examples of what I have tried:
canRender: function(cell) {
console.log("canRender: cell.field=" + cell.field);
// Enable this custom cell renderer for any field except the test case field
var testpattern = new RegExp ('test', 'i');
return _(!testpattern.test(cell.field));
},
I have also tried:
canRender: function(cell) {
console.log("canRender: cell.field=" + cell.field);
// Enable this custom cell renderer for any field except the test case field
return _(!(['testcase']).contains(cell.field));
},
I have worked around this by putting code in the render function to add the cell.value as a string for the testcase field, but if I understand how this is supposed to work, then I should not have to do that.
@leesahic, Please try the following approach where I am filtering only the required fields using the following JavaScript and then pass on the filtered column list as array of string. In the example I have used _time
as the filed not be included for custom cell rendering.
myResults.on("data", function(){
allFields=myResults.data().fields;
filteredFields=allFields.filter(filterFields);
console.log("Filtered Fields:",filteredFields);
});
function filterFields(field) {
return field !== "_time";
}
Following is the Simple XML Dashboard code:
<form script="table_cell_render_all_cells.js">
<label>Custom Cell Render to all cells except _time</label>
<fieldset submitButton="false">
<input type="time" token="tokTime" searchWhenChanged="true">
<label>Select Time</label>
<default>
<earliest>-24h@h</earliest>
<latest>now</latest>
</default>
</input>
</fieldset>
<row>
<panel depends="$alaysHideCSS$">
<html>
<style>
#highlight td {
background-color: #c1ffc3 !important;
}
*/
#highlight td.range-low {
color: #C0D9D9;
}
#highlight td.range-elevated {
background-color: #ffc57a !important;
font-weight: bold;
}
#highlight td.range-severe {
background-color: #d59392 !important;
font-weight: bold;
}
</style>
</html>
</panel>
<panel>
<table id="highlight">
<search id="myTableSearch">
<query>index=_internal sourcetype=splunkd
| timechart count by component useother=f usenull=f</query>
<earliest>$tokTime.earliest$</earliest>
<latest>$tokTime.latest$</latest>
<sampleRatio>1</sampleRatio>
</search>
<option name="count">20</option>
<option name="dataOverlayMode">none</option>
<option name="drilldown">none</option>
<option name="percentagesRow">false</option>
<option name="rowNumbers">false</option>
<option name="totalsRow">false</option>
<option name="wrap">true</option>
</table>
</panel>
</row>
</form>
Following is the JavaScript code for table_cell_render_all_cells.js
require([
"underscore",
"jquery",
"splunkjs/mvc",
"splunkjs/mvc/searchmanager",
"splunkjs/mvc/tableview",
"splunkjs/mvc/simplexml/ready!"
], function(_, $, mvc, SearchManager, TableView) {
var mySearch = splunkjs.mvc.Components.getInstance("myTableSearch");
var myResults = mySearch.data("results", {count:0});
var allFields,filteredFields;
myResults.on("data", function(){
allFields=myResults.data().fields;
filteredFields=allFields.filter(filterFields);
console.log("Filtered Fields:",filteredFields);
});
function filterFields(field) {
return field !== "_time";
}
// Row Coloring Example with custom, client-side range interpretation
var CustomRangeRenderer = TableView.BaseCellRenderer.extend({
canRender: function(cell) {
// Enable this custom cell renderer for all fields except _time
return _(filteredFields).contains(cell.field);
},
render: function($td, cell) {
// Add a class to the cell based on the returned value
var value = parseInt(cell.value);
// Apply interpretation based on count of errors per _time for each field
if(cell.field !== "_time"){
if (value >= 500) {
$td.addClass("range-cell").addClass("range-severe");
}
else if (value >= 200 && value < 500) {
$td.addClass("range-cell").addClass("range-elevated");
}
else if (value < 200) {
$td.addClass("range-cell").addClass("range-low");
}
// Update the cell content
$td.text(value).addClass("numeric");
}
}
});
mvc.Components.get("highlight").getVisualization(function(tableView) {
// Add custom cell renderer, the table will re-render automatically.
tableView.addCellRenderer(new CustomRangeRenderer());
});
});
@leesahic, Please try the following approach where I am filtering only the required fields using the following JavaScript and then pass on the filtered column list as array of string. In the example I have used _time
as the filed not be included for custom cell rendering.
myResults.on("data", function(){
allFields=myResults.data().fields;
filteredFields=allFields.filter(filterFields);
console.log("Filtered Fields:",filteredFields);
});
function filterFields(field) {
return field !== "_time";
}
Following is the Simple XML Dashboard code:
<form script="table_cell_render_all_cells.js">
<label>Custom Cell Render to all cells except _time</label>
<fieldset submitButton="false">
<input type="time" token="tokTime" searchWhenChanged="true">
<label>Select Time</label>
<default>
<earliest>-24h@h</earliest>
<latest>now</latest>
</default>
</input>
</fieldset>
<row>
<panel depends="$alaysHideCSS$">
<html>
<style>
#highlight td {
background-color: #c1ffc3 !important;
}
*/
#highlight td.range-low {
color: #C0D9D9;
}
#highlight td.range-elevated {
background-color: #ffc57a !important;
font-weight: bold;
}
#highlight td.range-severe {
background-color: #d59392 !important;
font-weight: bold;
}
</style>
</html>
</panel>
<panel>
<table id="highlight">
<search id="myTableSearch">
<query>index=_internal sourcetype=splunkd
| timechart count by component useother=f usenull=f</query>
<earliest>$tokTime.earliest$</earliest>
<latest>$tokTime.latest$</latest>
<sampleRatio>1</sampleRatio>
</search>
<option name="count">20</option>
<option name="dataOverlayMode">none</option>
<option name="drilldown">none</option>
<option name="percentagesRow">false</option>
<option name="rowNumbers">false</option>
<option name="totalsRow">false</option>
<option name="wrap">true</option>
</table>
</panel>
</row>
</form>
Following is the JavaScript code for table_cell_render_all_cells.js
require([
"underscore",
"jquery",
"splunkjs/mvc",
"splunkjs/mvc/searchmanager",
"splunkjs/mvc/tableview",
"splunkjs/mvc/simplexml/ready!"
], function(_, $, mvc, SearchManager, TableView) {
var mySearch = splunkjs.mvc.Components.getInstance("myTableSearch");
var myResults = mySearch.data("results", {count:0});
var allFields,filteredFields;
myResults.on("data", function(){
allFields=myResults.data().fields;
filteredFields=allFields.filter(filterFields);
console.log("Filtered Fields:",filteredFields);
});
function filterFields(field) {
return field !== "_time";
}
// Row Coloring Example with custom, client-side range interpretation
var CustomRangeRenderer = TableView.BaseCellRenderer.extend({
canRender: function(cell) {
// Enable this custom cell renderer for all fields except _time
return _(filteredFields).contains(cell.field);
},
render: function($td, cell) {
// Add a class to the cell based on the returned value
var value = parseInt(cell.value);
// Apply interpretation based on count of errors per _time for each field
if(cell.field !== "_time"){
if (value >= 500) {
$td.addClass("range-cell").addClass("range-severe");
}
else if (value >= 200 && value < 500) {
$td.addClass("range-cell").addClass("range-elevated");
}
else if (value < 200) {
$td.addClass("range-cell").addClass("range-low");
}
// Update the cell content
$td.text(value).addClass("numeric");
}
}
});
mvc.Components.get("highlight").getVisualization(function(tableView) {
// Add custom cell renderer, the table will re-render automatically.
tableView.addCellRenderer(new CustomRangeRenderer());
});
});
Can you explain to me how this is different from what my code does:
@leesahic, myResults.on("data", function()
has been used to get all field names and then filter _time. Check out Console Logs. Then canRender gets all fields except _time.
_time is green because default Background Color has been overridden as green through CSS override.
#highlight td {
background-color: #c1ffc3 !important;
}
You can remove this as all remaining rows except time will fall into either one of the three categories i.e. low, elevated or severe. Hence, background color will always be applied for remaining fields.
Sorry for the delay in my response. Please try out and confirm!
@leesahic please confirm whether the solution worked for you or not.
@leesahic, what are the fields other than test case? Do you know how many fields you will have if not their names? Do you intend to color specific cells for each field or do you need to color each row based on cell value?
I need to color all fields except the testcase field. I do not know their names or how many there will be for any specific query.
@leesahic, can you add some sample for these fields other than testcase? Also what is the query generating these fields? If you don't know how many there can be is there still any max value like 10, 100, 1000, 10000 or more?
There would typically be more than 10 and less than 100 columns. Other than testcase, the columns are labelled by kitname, so for example, the columns returned would be testcase, KitA, KitB, KitC, ... where kitname is dynamic. This query is counting the number of test failures by testcase for each kitname. The colorization I have implemented colors any kit failure count > 0 as red, otherwise green.
Query is as follows:
index="owr-build" sourcetype="owr:precheck" test-case{@name}="$testcase_token$"
| rename test-case{@result} as result
| rename test-case{@name} as testcase
| eval testcase=lower(testcase)
| eval resultval=case(result="Failure", 1, 1=1, 0)
| join id [search index=owr-build sourcetype=owr:build buildduration=* status!=incomplete platformfamily="$platformfamily_token$" platformconfig="$platformconfig_token$" ]
| replace "" with "Unknown" in platformfamily
| extract_test_case_ingredient_and_test(test-case{@name})
| dedup kitname testcase resultval
| chart count(resultval) over testcase by kitname limit=0
@leesahic, if it was limited countable fields I would have recommended the following approach using Simple XML chart configuration with Dynamic fields with tokens.
https://answers.splunk.com/answers/615411/how-to-format-table-range-per-week-automatically.html
But definitely your case is complicated. Let me see if I can find some time to figure out something for your issue.
Perhaps there is something that I don't understand. I see from debug output that canRender is called for every column field, which is expected. Per the example from Splunk Dashboard Examples for Table Cell Highlighting, it returns true if the field should be rendered, otherwise false.
My code returns true for any field that is not the testcase field.
However, I had to add code in the render function to update the testcase field value; without this the cell ends up blank. Here is the complete working code:
require([
'underscore',
'jquery',
'splunkjs/mvc',
'splunkjs/mvc/tableview',
'splunkjs/mvc/simplexml/ready!'
], function(_, $, mvc, TableView) {
// Row Coloring Example with custom, client-side range interpretation
var CustomRangeRenderer = TableView.BaseCellRenderer.extend({
canRender: function(cell) {
console.log("canRender: cell.field=" + cell.field);
// Enable this custom cell renderer for any field except the test case field
var testpattern = new RegExp ('test', 'i');
return _(!testpattern.test(cell.field));
},
render: function($td, cell) {
// Add a class to the cell based on the returned value
var testpattern = new RegExp ('test', 'i');
if (testpattern.test(cell.field))
{
// Update the cell content
$td.text(cell.value).addClass('string');
}
else {
var value = parseInt(cell.value);
if (value >= 1) {
$td.addClass('range-cell').addClass('range-fail');
}
else {
$td.addClass('range-cell').addClass('range-pass');
}
// Update the cell content
$td.text(' ').addClass('string');
}
}
});
mvc.Components.get('highlight').getVisualization(function(tableView) {
// Add custom cell renderer, the table will re-render automatically.
tableView.addCellRenderer(new CustomRangeRenderer());
});
});