Skip to content

Customization

Iván SZKIBA edited this page Dec 4, 2023 · 9 revisions

This page is about configuration customization.

The long-term goal is to eliminate the need for customization. Many features have already been created that partially make customization unnecessary (foldable sections, gRPC, browser, WebSockets charts). Until the development reaches this long-term goal, it is possible to customize the dashboard configuration.

Warning

The customization of the configuration is not an official dashboard feature, use at your own risk. It is possible that the customization changes in a way that is not backward compatible.

Example

.dashboard.js

function customize(config, { tab }) {
  tab("Custom", ({ section }) => {
    section("Sample Section", ({ panel }) => {
      panel("Sample Panel", ({ panel, serie }) => {
        panel.summary = "Example full width panel";
        panel.fullWidth = true;

        serie("http_reqs[?!tags && rate]", "Request Rate");
        serie("http_req_duration[?!tags && p95]", "Request Duration p(95)");
        serie("http_req_failed[?!tags && rate ]", "Request Failed");
      });
    });
  });
}

// --- Helpers ---

export default function (config) {
  const builder = new Builder(config);

  customize(config, { tab: (...args) => builder.tab(...args) });

  return config;
}

class Builder {
  constructor(config) {
    this.config = config;
  }

  tab(...args) {
    const [title, summary, self, fn] = getargs(args, { sections: [] });

    self.title = title;
    self.summary = summary;

    if (!self.id) self.id = `tab-${this.config.tabs.length}`;

    this.currentTab = self;

    fn({ tab: self, section: (...args) => this.section(...args) });

    this.config.tabs.push(self);

    delete this.currentTab;
  }

  section(...args) {
    const [title, summary, self, fn] = getargs(args, { panels: [] });

    self.title = title;
    self.summary = summary;

    if (!self.id) self.id = `${this.currentTab.id}.section-${this.currentTab.sections.length}`;

    this.currentSection = self;

    fn({ section: self, panel: (...args) => this.panel(...args) });

    this.currentTab.sections.push(self);

    delete this.currentSection;
  }

  panel(...args) {
    const [title, kind, self, fn] = getargs(args, { series: [] });

    self.title = title;

    if (kind) self.kind = kind;

    if (!self.id) self.id = `${this.currentSection.id}.panel-${this.currentSection.panels.length}`;

    this.currentPanel = self;

    fn({ panel: self, serie: (...args) => this.serie(...args) });

    if (!self.kind) self.kind = "chart";

    this.currentSection.panels.push(self);

    delete this.currentPanel;
  }

  serie(...args) {
    const [query, legend, self] = getargs(args, {});

    self.query = query;
    self.legend = legend;

    if (self.query == undefined) self.query = self.legend;

    this.currentPanel.series.push(self);
  }
}

function getargs(args, obj) {
  const ret = [];
  let idx = 0;

  ret.push(typeof args[idx] == "string" ? args[idx++] : undefined);
  ret.push(typeof args[idx] == "string" ? args[idx++] : undefined);
  ret.push(typeof args[idx] == "object" ? Object.assign(obj, args[idx++]) : obj);
  ret.push(typeof args[idx] == "function" ? args[idx++] : () => {});

  return ret;
}
Clone this wiki locally