Commit 9c9d6c15 by Torkel Ödegaard

Merge branch 'master' into explore-logs-styling-alt2

parents 5ac3b5a5 093c9425
...@@ -170,16 +170,25 @@ export function filterLogLevels(logs: LogsModel, hiddenLogLevels: Set<LogLevel>) ...@@ -170,16 +170,25 @@ export function filterLogLevels(logs: LogsModel, hiddenLogLevels: Set<LogLevel>)
} }
export function makeSeriesForLogs(rows: LogRow[], intervalMs: number): TimeSeries[] { export function makeSeriesForLogs(rows: LogRow[], intervalMs: number): TimeSeries[] {
// currently interval is rangeMs / resolution, which is too low for showing series as bars.
// need at least 10px per bucket, so we multiply interval by 10. Should be solved higher up the chain
// when executing queries & interval calculated and not here but this is a temporary fix.
// intervalMs = intervalMs * 10;
// Graph time series by log level // Graph time series by log level
const seriesByLevel = {}; const seriesByLevel = {};
rows.forEach(row => { const bucketSize = intervalMs * 10;
for (const row of rows) {
if (!seriesByLevel[row.logLevel]) { if (!seriesByLevel[row.logLevel]) {
seriesByLevel[row.logLevel] = { lastTs: null, datapoints: [], alias: row.logLevel }; seriesByLevel[row.logLevel] = { lastTs: null, datapoints: [], alias: row.logLevel };
} }
const levelSeries = seriesByLevel[row.logLevel]; const levelSeries = seriesByLevel[row.logLevel];
// Bucket to nearest minute // Bucket to nearest minute
const time = Math.round(row.timeEpochMs / intervalMs / 10) * intervalMs * 10; const time = Math.round(row.timeEpochMs / bucketSize) * bucketSize;
// Entry for time // Entry for time
if (time === levelSeries.lastTs) { if (time === levelSeries.lastTs) {
levelSeries.datapoints[levelSeries.datapoints.length - 1][0]++; levelSeries.datapoints[levelSeries.datapoints.length - 1][0]++;
...@@ -187,7 +196,7 @@ export function makeSeriesForLogs(rows: LogRow[], intervalMs: number): TimeSerie ...@@ -187,7 +196,7 @@ export function makeSeriesForLogs(rows: LogRow[], intervalMs: number): TimeSerie
levelSeries.datapoints.push([1, time]); levelSeries.datapoints.push([1, time]);
levelSeries.lastTs = time; levelSeries.lastTs = time;
} }
}); }
return Object.keys(seriesByLevel).reduce((acc, level) => { return Object.keys(seriesByLevel).reduce((acc, level) => {
if (seriesByLevel[level]) { if (seriesByLevel[level]) {
......
import _ from 'lodash'; import _ from 'lodash';
import { renderUrl } from 'app/core/utils/url'; import { renderUrl } from 'app/core/utils/url';
import { ExploreState, ExploreUrlState, HistoryItem, QueryTransaction } from 'app/types/explore';
import { DataQuery, RawTimeRange } from 'app/types/series';
import TableModel, { mergeTablesIntoModel } from 'app/core/table_model';
import kbn from 'app/core/utils/kbn'; import kbn from 'app/core/utils/kbn';
import store from 'app/core/store';
import colors from 'app/core/utils/colors'; import colors from 'app/core/utils/colors';
import TimeSeries from 'app/core/time_series2';
import { parse as parseDate } from 'app/core/utils/datemath'; import { parse as parseDate } from 'app/core/utils/datemath';
import store from 'app/core/store';
import TimeSeries from 'app/core/time_series2';
import TableModel, { mergeTablesIntoModel } from 'app/core/table_model';
import { ExploreState, ExploreUrlState, HistoryItem, QueryTransaction } from 'app/types/explore';
import { DataQuery, RawTimeRange, IntervalValues, DataSourceApi } from 'app/types/series';
export const DEFAULT_RANGE = { export const DEFAULT_RANGE = {
from: 'now-6h', from: 'now-6h',
...@@ -170,18 +170,16 @@ export function calculateResultsFromQueryTransactions( ...@@ -170,18 +170,16 @@ export function calculateResultsFromQueryTransactions(
}; };
} }
export function getIntervals( export function getIntervals(range: RawTimeRange, datasource: DataSourceApi, resolution: number): IntervalValues {
range: RawTimeRange,
datasource,
resolution: number
): { interval: string; intervalMs: number } {
if (!datasource || !resolution) { if (!datasource || !resolution) {
return { interval: '1s', intervalMs: 1000 }; return { interval: '1s', intervalMs: 1000 };
} }
const absoluteRange: RawTimeRange = { const absoluteRange: RawTimeRange = {
from: parseDate(range.from, false), from: parseDate(range.from, false),
to: parseDate(range.to, true), to: parseDate(range.to, true),
}; };
return kbn.calculateInterval(absoluteRange, resolution, datasource.interval); return kbn.calculateInterval(absoluteRange, resolution, datasource.interval);
} }
......
...@@ -24,6 +24,7 @@ const PREVIEW_LIMIT = 100; ...@@ -24,6 +24,7 @@ const PREVIEW_LIMIT = 100;
const graphOptions = { const graphOptions = {
series: { series: {
stack: true,
bars: { bars: {
show: true, show: true,
lineWidth: 5, lineWidth: 5,
......
...@@ -136,7 +136,8 @@ export default class LokiDatasource { ...@@ -136,7 +136,8 @@ export default class LokiDatasource {
} }
return { return {
status: 'error', status: 'error',
message: 'Data source connected, but no labels received. Verify that Loki is configured properly.', message:
'Data source connected, but no labels received. Verify that Loki and Promtail is configured properly.',
}; };
}) })
.catch(err => { .catch(err => {
......
...@@ -151,8 +151,7 @@ table_schema IN ( ...@@ -151,8 +151,7 @@ table_schema IN (
buildDatatypeQuery(column: string) { buildDatatypeQuery(column: string) {
let query = 'SELECT udt_name FROM information_schema.columns WHERE '; let query = 'SELECT udt_name FROM information_schema.columns WHERE ';
query += this.buildSchemaConstraint(); query += this.buildTableConstraint(this.target.table);
query += ' AND table_name = ' + this.quoteIdentAsLiteral(this.target.table);
query += ' AND column_name = ' + this.quoteIdentAsLiteral(column); query += ' AND column_name = ' + this.quoteIdentAsLiteral(column);
return query; return query;
} }
......
...@@ -19,6 +19,7 @@ import { ...@@ -19,6 +19,7 @@ import {
DataQuery, DataQuery,
DataQueryResponse, DataQueryResponse,
DataQueryOptions, DataQueryOptions,
IntervalValues,
} from './series'; } from './series';
import { PanelProps, PanelOptionsProps } from './panel'; import { PanelProps, PanelOptionsProps } from './panel';
import { PluginDashboard, PluginMeta, Plugin, PluginsState } from './plugins'; import { PluginDashboard, PluginMeta, Plugin, PluginsState } from './plugins';
...@@ -87,6 +88,7 @@ export { ...@@ -87,6 +88,7 @@ export {
AppNotificationTimeout, AppNotificationTimeout,
DashboardSearchHit, DashboardSearchHit,
UserState, UserState,
IntervalValues,
}; };
export interface StoreState { export interface StoreState {
......
...@@ -19,6 +19,11 @@ export interface TimeRange { ...@@ -19,6 +19,11 @@ export interface TimeRange {
raw: RawTimeRange; raw: RawTimeRange;
} }
export interface IntervalValues {
interval: string; // 10s,5m
intervalMs: number;
}
export type TimeSeriesValue = string | number | null; export type TimeSeriesValue = string | number | null;
export type TimeSeriesPoints = TimeSeriesValue[][]; export type TimeSeriesPoints = TimeSeriesValue[][];
...@@ -90,6 +95,11 @@ export interface DataQueryOptions { ...@@ -90,6 +95,11 @@ export interface DataQueryOptions {
export interface DataSourceApi { export interface DataSourceApi {
/** /**
* min interval range
*/
interval?: string;
/**
* Imports queries from a different datasource * Imports queries from a different datasource
*/ */
importQueries?(queries: DataQuery[], originMeta: PluginMeta): Promise<DataQuery[]>; importQueries?(queries: DataQuery[], originMeta: PluginMeta): Promise<DataQuery[]>;
...@@ -97,6 +107,14 @@ export interface DataSourceApi { ...@@ -97,6 +107,14 @@ export interface DataSourceApi {
* Initializes a datasource after instantiation * Initializes a datasource after instantiation
*/ */
init?: () => void; init?: () => void;
/**
* Main data query method
*/
query(options: DataQueryOptions): Promise<DataQueryResponse>; query(options: DataQueryOptions): Promise<DataQueryResponse>;
/**
* test data source
*/
testDatasource?: () => Promise<any>; testDatasource?: () => Promise<any>;
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment