Skip to content

Commit a115e6d

Browse files
committed
[jaeger] Add Panels for Service Performance Monitoring
It is now possible to use the new Service Performance Monitoring feature of the Jaeger plugin within dashboards. It is possible to add panels for the service latency, error rate and request rate and a panel for the metrics of the operations of a service. It is now also possible to select the span kinds which should be included in the different metrics. By default we always include all span kinds.
1 parent f810912 commit a115e6d

16 files changed

+304
-50
lines changed
Loading

docs/plugins/azure.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ The `*` value is a special value, which allows access to all resources, resource
171171
- `verbs`: `delete`, `get`, `post` and `put`
172172

173173
!!! note
174-
You have to set the `permissionsEnabled` property in the configuration to `true` and you must enable [authentication](../getting-started/configuration/authentication.md) for kobs to use this feature.
174+
You have to set the `permissionsEnabled` property in the configuration to `true` and you must enable [authentication](../getting-started/configuration/auth.md) for kobs to use this feature.
175175

176176
### Metrics
177177

docs/plugins/jaeger.md

+66-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@ The following options can be used for a panel with the Jaeger plugin:
5252
| Field | Type | Description | Required |
5353
| ----- | ---- | ----------- | -------- |
5454
| showChart | boolean | If this is `true` the chart with the traces will be shown. | No |
55-
| queries | [[]Query](#query) | A list of Jaeger queries, which can be selected by the user. | Yes |
55+
| queries | [[]Query](#query) | A list of Jaeger queries, which can be selected by the user. | No |
56+
| metrics | [Metrics](#metrics) | The configuration to show the metrics for the Service Performance Monitoring. | No |
5657

5758
### Query
5859

@@ -66,6 +67,14 @@ The following options can be used for a panel with the Jaeger plugin:
6667
| operation | string | An optional operation to retrieve traces for. | No |
6768
| tags | string | Tags, which the traces must be contain. | No |
6869

70+
### Metrics
71+
72+
| Field | Type | Description | Required |
73+
| ----- | ---- | ----------- | -------- |
74+
| type | string | The metrics type which should be displayed. Must be one of the following: `servicelatency`, `serviceerrors`, `servicecalls` or `operations`. | Yes |
75+
| service | string | The service for which the selected metrics should be displayed. | Yes |
76+
| spanKinds | string | A list of span kinds for which the selected metrics should be displayed. By default it includes all span kinds: `unspecified`, `internal`, `server`, `client`, `producer` and `consumer`. | No |
77+
6978
## Notification Options
7079

7180
!!! note
@@ -107,3 +116,59 @@ spec:
107116
```
108117

109118
![Dashboard](assets/jaeger-dashboard.png)
119+
120+
```yaml
121+
---
122+
apiVersion: kobs.io/v1
123+
kind: Application
124+
metadata:
125+
name: satellite
126+
namespace: kobs
127+
spec:
128+
dashboards:
129+
- title: Service Performance Monitoring
130+
inline:
131+
rows:
132+
- size: 3
133+
panels:
134+
- title: Latency (ms)
135+
colSpan: 4
136+
plugin:
137+
name: jaeger
138+
type: jaeger
139+
options:
140+
metrics:
141+
type: servicelatency
142+
service: satellite
143+
- title: Errors (%)
144+
colSpan: 4
145+
plugin:
146+
name: jaeger
147+
type: jaeger
148+
options:
149+
metrics:
150+
type: serviceerrors
151+
service: satellite
152+
- title: Request Rate (req/s)
153+
colSpan: 4
154+
plugin:
155+
name: jaeger
156+
type: jaeger
157+
options:
158+
metrics:
159+
type: servicecalls
160+
service: satellite
161+
- size: -1
162+
panels:
163+
- title: Operations
164+
colSpan: 4
165+
plugin:
166+
name: jaeger
167+
type: jaeger
168+
options:
169+
metrics:
170+
type: operations
171+
service: satellite
172+
```
173+
174+
![Service Performance Monitoring](assets/jaeger-service-performance-monitoring.png)

plugins/plugin-jaeger/cmd/jaeger.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -158,14 +158,15 @@ func (router *Router) getMetrics(w http.ResponseWriter, r *http.Request) {
158158
name := r.Header.Get("x-kobs-plugin")
159159
metric := r.URL.Query().Get("metric")
160160
service := r.URL.Query().Get("service")
161+
spanKinds := r.URL.Query()["spanKind"]
161162
groupByOperation := r.URL.Query().Get("groupByOperation")
162163
quantile := r.URL.Query().Get("quantile")
163164
ratePer := r.URL.Query().Get("ratePer")
164165
step := r.URL.Query().Get("step")
165166
timeEnd := r.URL.Query().Get("timeEnd")
166167
timeStart := r.URL.Query().Get("timeStart")
167168

168-
log.Debug(r.Context(), "Get metrics parameters", zap.String("name", name), zap.String("metric", metric), zap.String("service", service), zap.String("groupByOperation", groupByOperation), zap.String("quantile", quantile), zap.String("ratePer", ratePer), zap.String("step", step), zap.String("timeEnd", timeEnd), zap.String("timeStart", timeStart))
169+
log.Debug(r.Context(), "Get metrics parameters", zap.String("name", name), zap.String("metric", metric), zap.String("service", service), zap.Strings("spanKinds", spanKinds), zap.String("groupByOperation", groupByOperation), zap.String("quantile", quantile), zap.String("ratePer", ratePer), zap.String("step", step), zap.String("timeEnd", timeEnd), zap.String("timeStart", timeStart))
169170

170171
i := router.getInstance(name)
171172
if i == nil {
@@ -188,7 +189,7 @@ func (router *Router) getMetrics(w http.ResponseWriter, r *http.Request) {
188189
return
189190
}
190191

191-
body, err := i.GetMetrics(r.Context(), metric, service, groupByOperation, quantile, ratePer, step, parsedTimeStart, parsedTimeEnd)
192+
body, err := i.GetMetrics(r.Context(), metric, service, groupByOperation, quantile, ratePer, step, spanKinds, parsedTimeStart, parsedTimeEnd)
192193
if err != nil {
193194
log.Error(r.Context(), "Could not get metrics", zap.Error(err))
194195
errresponse.Render(w, r, err, http.StatusInternalServerError, "Could not get metrics")

plugins/plugin-jaeger/pkg/instance/instance.go

+8-3
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ type Instance interface {
3838
GetOperations(ctx context.Context, service string) (map[string]any, error)
3939
GetTraces(ctx context.Context, limit, maxDuration, minDuration, operation, service, tags string, timeStart, timeEnd int64) (map[string]any, error)
4040
GetTrace(ctx context.Context, traceID string) (map[string]any, error)
41-
GetMetrics(ctx context.Context, metric, service, groupByOperation, quantile, ratePer, step string, timeStart, timeEnd int64) (map[string]any, error)
41+
GetMetrics(ctx context.Context, metric, service, groupByOperation, quantile, ratePer, step string, spanKinds []string, timeStart, timeEnd int64) (map[string]any, error)
4242
}
4343

4444
type instance struct {
@@ -112,12 +112,17 @@ func (i *instance) GetTrace(ctx context.Context, traceID string) (map[string]any
112112
return i.doRequest(ctx, fmt.Sprintf("/api/traces/%s", traceID))
113113
}
114114

115-
func (i *instance) GetMetrics(ctx context.Context, metric, service, groupByOperation, quantile, ratePer, step string, timeStart, timeEnd int64) (map[string]any, error) {
115+
func (i *instance) GetMetrics(ctx context.Context, metric, service, groupByOperation, quantile, ratePer, step string, spanKinds []string, timeStart, timeEnd int64) (map[string]any, error) {
116116
timeStart = timeStart * 1000
117117
timeEnd = timeEnd * 1000
118118
lookback := timeEnd - timeStart
119119

120-
return i.doRequest(ctx, fmt.Sprintf("/api/metrics/%s?service=%s&endTs=%d&lookback=%d&groupByOperation=%s&quantile=%s&ratePer=%s&step=%s&spanKind=unspecified&spanKind=internal&spanKind=server&spanKind=client&spanKind=producer&spanKind=consumer", metric, service, timeEnd, lookback, groupByOperation, quantile, ratePer, step))
120+
var spanKindsParameters string
121+
for _, spanKind := range spanKinds {
122+
spanKindsParameters = fmt.Sprintf("%s&spanKind=%s", spanKindsParameters, spanKind)
123+
}
124+
125+
return i.doRequest(ctx, fmt.Sprintf("/api/metrics/%s?service=%s&endTs=%d&lookback=%d&groupByOperation=%s&quantile=%s&ratePer=%s&step=%s%s", metric, service, timeEnd, lookback, groupByOperation, quantile, ratePer, step, spanKindsParameters))
121126
}
122127

123128
// New returns a new Jaeger instance for the given configuration.

plugins/plugin-jaeger/pkg/instance/instance_mock.go

+7-7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

plugins/plugin-jaeger/src/components/page/Monitor.tsx

+10-4
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,12 @@ const Monitor: React.FunctionComponent<IMonitorProps> = ({ instance }: IMonitorP
2424
const [details, setDetails] = useState<React.ReactNode>(undefined);
2525

2626
const changeOptions = (opts: IMonitorOptions): void => {
27+
const spanKinds = opts.spanKinds.map((spanKind) => `&spanKind=${spanKind}`);
28+
2729
navigate(
28-
`${location.pathname}?service=${encodeURIComponent(opts.service)}&time=${opts.times.time}&timeEnd=${
29-
opts.times.timeEnd
30-
}&timeStart=${opts.times.timeStart}`,
30+
`${location.pathname}?service=${encodeURIComponent(opts.service)}${
31+
spanKinds.length > 0 ? spanKinds.join('') : ''
32+
}&time=${opts.times.time}&timeEnd=${opts.times.timeEnd}&timeStart=${opts.times.timeStart}`,
3133
);
3234
};
3335

@@ -58,13 +60,14 @@ const Monitor: React.FunctionComponent<IMonitorProps> = ({ instance }: IMonitorP
5860
toolbarContent={<MonitorToolbar instance={instance} options={options} setOptions={changeOptions} />}
5961
panelContent={details}
6062
>
61-
{options.service ? (
63+
{options.service && options.spanKinds.length > 0 ? (
6264
<Grid hasGutter={true}>
6365
<GridItem style={{ height: '300px' }} sm={12} md={12} lg={4} xl={4} xl2={4}>
6466
<MonitorServiceLatency
6567
title="Latency (ms)"
6668
instance={instance}
6769
service={options.service}
70+
spanKinds={options.spanKinds}
6871
times={options.times}
6972
/>
7073
</GridItem>
@@ -73,6 +76,7 @@ const Monitor: React.FunctionComponent<IMonitorProps> = ({ instance }: IMonitorP
7376
title="Error Rate (%)"
7477
instance={instance}
7578
service={options.service}
79+
spanKinds={options.spanKinds}
7680
times={options.times}
7781
/>
7882
</GridItem>
@@ -81,6 +85,7 @@ const Monitor: React.FunctionComponent<IMonitorProps> = ({ instance }: IMonitorP
8185
title="Request Rate (req/s)"
8286
instance={instance}
8387
service={options.service}
88+
spanKinds={options.spanKinds}
8489
times={options.times}
8590
/>
8691
</GridItem>
@@ -89,6 +94,7 @@ const Monitor: React.FunctionComponent<IMonitorProps> = ({ instance }: IMonitorP
8994
title="Operations"
9095
instance={instance}
9196
service={options.service}
97+
spanKinds={options.spanKinds}
9298
times={options.times}
9399
setDetails={setDetails}
94100
/>

plugins/plugin-jaeger/src/components/page/MonitorToolbar.tsx

+22
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React, { useState } from 'react';
22

33
import { IOptionsAdditionalFields, IPluginInstance, ITimes, Options, Toolbar, ToolbarItem } from '@kobsio/shared';
44
import { IMonitorOptions } from '../../utils/interfaces';
5+
import MonitorToolbarSpanKinds from './MonitorToolbarSpanKinds';
56
import TracesToolbarServices from './TracesToolbarServices';
67

78
interface IMonitorToolbarProps {
@@ -16,10 +17,28 @@ const MonitorToolbar: React.FunctionComponent<IMonitorToolbarProps> = ({
1617
setOptions,
1718
}: IMonitorToolbarProps) => {
1819
const [service, setService] = useState<string>(options.service);
20+
const [spanKinds, setSpanKinds] = useState<string[]>(options.spanKinds);
21+
22+
const selectSpanKind = (spanKind: string): void => {
23+
if (spanKind === '') {
24+
setSpanKinds([]);
25+
} else {
26+
if (spanKind) {
27+
if (spanKinds.includes(spanKind)) {
28+
setSpanKinds(spanKinds.filter((item) => item !== spanKind));
29+
} else {
30+
setSpanKinds([...spanKinds, spanKind]);
31+
}
32+
} else {
33+
setSpanKinds([spanKind]);
34+
}
35+
}
36+
};
1937

2038
const changeOptions = (times: ITimes, additionalFields: IOptionsAdditionalFields[] | undefined): void => {
2139
setOptions({
2240
service: service,
41+
spanKinds: spanKinds,
2342
times: times,
2443
});
2544
};
@@ -29,6 +48,9 @@ const MonitorToolbar: React.FunctionComponent<IMonitorToolbarProps> = ({
2948
<ToolbarItem grow={true}>
3049
<TracesToolbarServices instance={instance} service={service} setService={(value): void => setService(value)} />
3150
</ToolbarItem>
51+
<ToolbarItem>
52+
<MonitorToolbarSpanKinds spanKinds={spanKinds} setSpanKind={(value): void => selectSpanKind(value)} />
53+
</ToolbarItem>
3254

3355
<Options times={options.times} showOptions={true} showSearchButton={true} setOptions={changeOptions} />
3456
</Toolbar>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import React, { useState } from 'react';
2+
import { Select, SelectOption, SelectVariant } from '@patternfly/react-core';
3+
4+
interface IMonitorToolbarSpanKindsProps {
5+
spanKinds: string[];
6+
setSpanKind: (spanKind: string) => void;
7+
}
8+
9+
const MonitorToolbarSpanKinds: React.FunctionComponent<IMonitorToolbarSpanKindsProps> = ({
10+
spanKinds,
11+
setSpanKind,
12+
}: IMonitorToolbarSpanKindsProps) => {
13+
const [show, setShow] = useState<boolean>(false);
14+
15+
return (
16+
<Select
17+
variant={SelectVariant.checkbox}
18+
typeAheadAriaLabel="Span Kinds"
19+
placeholderText="Span Kinds"
20+
onToggle={(): void => setShow(!show)}
21+
onSelect={(e, value): void => setSpanKind(value as string)}
22+
selections={spanKinds}
23+
isOpen={show}
24+
maxHeight="50vh"
25+
>
26+
{['unspecified', 'internal', 'server', 'client', 'producer', 'consumer'].map((spanKind) => (
27+
<SelectOption key={spanKind} value={spanKind} />
28+
))}
29+
</Select>
30+
);
31+
};
32+
33+
export default MonitorToolbarSpanKinds;

plugins/plugin-jaeger/src/components/panel/MonitorOperations.tsx

+4-2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ interface IMonitorOperationsProps {
1010
description?: string;
1111
instance: IPluginInstance;
1212
service: string;
13+
spanKinds: string[];
1314
times: ITimes;
1415
setDetails?: (details: React.ReactNode) => void;
1516
}
@@ -18,11 +19,12 @@ const MonitorOperations: React.FunctionComponent<IMonitorOperationsProps> = ({
1819
title,
1920
description,
2021
instance,
21-
times,
2222
service,
23+
spanKinds,
24+
times,
2325
setDetails,
2426
}: IMonitorOperationsProps) => {
25-
const metrics = useGetOperationMetrics(instance, service, times);
27+
const metrics = useGetOperationMetrics(instance, service, spanKinds, times);
2628

2729
if (
2830
metrics[0].isLoading ||

plugins/plugin-jaeger/src/components/panel/MonitorServiceCalls.tsx

+9-3
Original file line numberDiff line numberDiff line change
@@ -12,22 +12,28 @@ interface IMonitorServiceCallsProps {
1212
description?: string;
1313
instance: IPluginInstance;
1414
service: string;
15+
spanKinds: string[];
1516
times: ITimes;
1617
}
1718

1819
const MonitorServiceCalls: React.FunctionComponent<IMonitorServiceCallsProps> = ({
1920
title,
2021
description,
2122
instance,
22-
times,
2323
service,
24+
spanKinds,
25+
times,
2426
}: IMonitorServiceCallsProps) => {
2527
const { isError, isLoading, error, data } = useQuery<IChartData[], Error>(
26-
['jaeger/metrics/calls/service', instance, service, times],
28+
['jaeger/metrics/calls/service', instance, service, spanKinds, times],
2729
async () => {
2830
try {
31+
const sk = spanKinds.map((spanKind) => `&spanKind=${spanKind}`);
32+
2933
const response = await fetch(
30-
`/api/plugins/jaeger/metrics?metric=calls&service=${service}&groupByOperation=false&ratePer=600000&step=60000&timeStart=${times.timeStart}&timeEnd=${times.timeEnd}`,
34+
`/api/plugins/jaeger/metrics?metric=calls&service=${service}${
35+
sk.length > 0 ? sk.join('') : ''
36+
}&groupByOperation=false&ratePer=600000&step=60000&timeStart=${times.timeStart}&timeEnd=${times.timeEnd}`,
3137
{
3238
headers: {
3339
// eslint-disable-next-line @typescript-eslint/naming-convention

0 commit comments

Comments
 (0)