Skip to content

Commit 6281873

Browse files
v-zhuravlevDasomeonergeyer
authored
Add helloworld observlib (#1084)
* Add helloworld observlib * Update README * Add structure * Update readme * Update helloworld-observ-lib/alerts.libsonnet Co-authored-by: Emily <[email protected]> * Fmt annotations * Update ref to commonlib * Update refs * Install deps in observ-libs dirs as well * Fix lint for observ libs * Fix linter * Fix typo 'currect' -> 'current' * Add inline wrapPanels comment * Update README. * Update helloworld-observ-lib/README.md Co-authored-by: Ryan Geyer <[email protected]> --------- Co-authored-by: Emily <[email protected]> Co-authored-by: Ryan Geyer <[email protected]>
1 parent b1d8125 commit 6281873

14 files changed

+771
-1
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ lint-fmt:
2424

2525
lint-mixins:
2626
@RESULT=0; \
27-
for d in $$(find . -name '*-mixin' -a -type d -print); do \
27+
for d in $$(find . -maxdepth 1 -regex '.*-mixin\|.*-lib' -a -type d -print); do \
2828
if [ -e "$$d/jsonnetfile.json" ]; then \
2929
echo "Installing dependencies for $$d"; \
3030
pushd "$$d" >/dev/null && jb install && popd >/dev/null; \

helloworld-observ-lib/.lint

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
exclusions:
2+
panel-units-rule:
3+
reason: "Table doesn't require units."
4+
entities:
5+
- panel: Table 1
6+
panel-datasource-rule:
7+
reason: "grafonnet created panels use --Mixed-- datasource by default."
8+
template-instance-rule:
9+
reason: "instanceLabels must be single-select for single instance dashboards."

helloworld-observ-lib/Makefile

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
JSONNET_FMT := jsonnetfmt -n 2 --max-blank-lines 1 --string-style s --comment-style s
2+
3+
.PHONY: all
4+
all: build dashboards_out prometheus_alerts.yaml
5+
6+
vendor: jsonnetfile.json
7+
jb install
8+
9+
.PHONY: build
10+
build: vendor
11+
12+
.PHONY: fmt
13+
fmt:
14+
find . -name 'vendor' -prune -o -name '*.libsonnet' -print -o -name '*.jsonnet' -print | \
15+
xargs -n 1 -- $(JSONNET_FMT) -i
16+
17+
.PHONY: lint
18+
lint: build
19+
find . -name 'vendor' -prune -o -name '*.libsonnet' -print -o -name '*.jsonnet' -print | \
20+
while read f; do \
21+
$(JSONNET_FMT) "$$f" | diff -u "$$f" -; \
22+
done
23+
mixtool lint mixin.libsonnet
24+
25+
dashboards_out: mixin.libsonnet config.libsonnet $(wildcard dashboards/*)
26+
@mkdir -p dashboards_out
27+
jsonnet -J vendor -m dashboards_out lib/dashboards.jsonnet
28+
29+
prometheus_alerts.yaml: mixin.libsonnet lib/alerts.jsonnet alerts/*.libsonnet
30+
jsonnet -J vendor -S lib/alerts.jsonnet > $@
31+
32+
.PHONY: clean
33+
clean:
34+
rm -rf dashboards_out prometheus_alerts.yaml

helloworld-observ-lib/README.md

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
# Hello world observ lib
2+
3+
This lib can be used as a starter template of modular observ lib. Modular observ libs are highly reusable observability package containing grafana dashboards and prometheus alerts/rules. They can be used in as a replacement or in conjuction to monitoring-mixins format.
4+
5+
6+
## Import
7+
8+
```sh
9+
jb init
10+
jb install https://github.com/grafana/jsonnet-libs/helloworld-observ-lib
11+
```
12+
13+
## Modular observability lib format
14+
15+
```jsonnet
16+
17+
{
18+
config: {
19+
//common options
20+
},
21+
22+
grafana: {
23+
24+
// grafana templated variables to reuse across mutltiple dashboards
25+
variables: {
26+
datasources: {
27+
prometheus: {},
28+
loki: {},
29+
},
30+
multiInstance: [],
31+
singleInstace: [],
32+
queriesSelector: "",
33+
},
34+
35+
// grafana targets (queries) to attach to panels
36+
targets: {
37+
target1: <target1>,
38+
target3: <target2>,
39+
...
40+
targetN: <targetN>,
41+
},
42+
43+
// grafana panels
44+
panels: {
45+
panel1: <panel1>,
46+
panel2: <panel2>,
47+
...
48+
panelN: <panelN>,
49+
},
50+
51+
// grafana dashboards
52+
dashboards: {
53+
dashboard1: <dashboard1>,
54+
dashboard2: <dashboard2>,
55+
...
56+
dashboardN: <dashboardN>,
57+
},
58+
59+
// grafana annotations
60+
annotations: {
61+
annotation1: <annotation1>,
62+
...
63+
},
64+
65+
// grafana dashboard links
66+
links: {},
67+
},
68+
69+
prometheus: {
70+
alerts: {},
71+
rules: {},
72+
},
73+
}
74+
75+
```
76+
77+
## Pros of using modular observabilty format
78+
79+
- Uses (jsonnet)[https://jsonnet.org/learning/tutorial.html], jsonnet-bundler, and grafonnet[https://github.com/grafana/grafonnet]
80+
- Highly customizable and flexible:
81+
82+
Any object like `panel`, `target` (query) can be easily referenced by key and then overriden before output of the lib is provided by using jsonnet (patching)[https://tanka.dev/tutorial/environments#patching] technique:
83+
84+
```jsonnet
85+
local helloworldlib = import './main.libsonnet';
86+
87+
local helloworld =
88+
helloworldlib.new(
89+
filteringSelector='job="integrations/helloworld"',
90+
uid='myhelloworld',
91+
groupLabels=['environment', 'cluster'],
92+
instanceLabels=['host'],
93+
)
94+
+
95+
{
96+
grafana+: {
97+
panels+: {
98+
panel1+:
99+
g.panel.timeSeries.withDescription("My new description for panel1")
100+
}
101+
}
102+
};
103+
```
104+
105+
- Due to high decomposition level, not only dashboards but single panels can be imported ('cherry-picked') from the library to be used in other dashboards
106+
- Format introduces mandatory arguments that each library should have: `filteringSelector`, `instanceLabels`, `groupLabels`, `uid`. Proper use of those parameters ensures library can be used to instantiate multiple copies of the observability package in the same enviroment without `ids` conflicts or timeSeries overlapping.
107+
108+
## Examples
109+
110+
### Example 1: Monitoring-Mixin example
111+
112+
You can use lib to fill in [monitoring-mixin](https://monitoring.mixins.dev/) structure:
113+
114+
```jsonnet
115+
// mixin.libsonnet file
116+
117+
local helloworldlib = import 'helloworld-observ-lib/main.libsonnet';
118+
119+
local helloworld =
120+
helloworldlib.new(
121+
filteringSelector='job="integrations/helloworld"',
122+
uid='myhelloworld',
123+
groupLabels=['environment', 'cluster'],
124+
instanceLabels=['host'],
125+
);
126+
127+
// populate monitoring-mixin:
128+
{
129+
grafanaDashboards+:: helloworld.grafana.dashboards,
130+
prometheusAlerts+:: helloworld.prometheus.alerts,
131+
prometheusRules+:: helloworld.prometheus.recordingRules,
132+
}
133+
```
134+
135+
### Example 2: Changing specific panel before rendering dashboards
136+
137+
We can point to any object (i.e grafana.panels.panel1) and modify it by using (jsonnnet mixins)[https://jsonnet.org/learning/tutorial.html].
138+
139+
For example, let's modify panel's default draw style to bars by mutating it with (grafonnet)[https://grafana.github.io/grafonnet/API/panel/timeSeries/index.html#fn-fieldconfigdefaultscustomwithdrawstyle]
140+
141+
```
142+
local g = import './g.libsonnet';
143+
local helloworldlib = import 'helloworld-observ-lib/main.libsonnet';
144+
145+
local helloworld =
146+
helloworldlib.new(
147+
filteringSelector='job="integrations/helloworld"',
148+
uid='myhelloworld',
149+
groupLabels=['environment', 'cluster'],
150+
instanceLabels=['host'],
151+
)
152+
+ {
153+
grafana+: {
154+
panels+: {
155+
networkSockstatAll+:
156+
+ g.panel.timeSeries.fieldConfig.defaults.custom.withDrawStyle('bars')
157+
}
158+
}
159+
};
160+
161+
// populate monitoring-mixin:
162+
{
163+
grafanaDashboards+:: helloworld.grafana.dashboards,
164+
prometheusAlerts+:: helloworld.prometheus.alerts,
165+
prometheusRules+:: helloworld.prometheus.recordingRules,
166+
}
167+
168+
```
169+
170+
### Example 3: Optional logs collection
171+
172+
Grafana Loki datasource is used to populate logs dashboard and also for quering annotations.
173+
174+
To opt-out, you can set `enableLokiLogs: false` in config:
175+
176+
```
177+
local helloworldlib = import 'helloworld-observ-lib/main.libsonnet';
178+
179+
local helloworld =
180+
helloworldlib.new(
181+
filteringSelector='job="integrations/helloworld"',
182+
uid='myhelloworld',
183+
groupLabels=['environment', 'cluster'],
184+
instanceLabels=['host'],
185+
)
186+
+ helloworldlib.withConfigMixin(
187+
{
188+
// disable loki logs
189+
enableLokiLogs: false,
190+
}
191+
);
192+
193+
// populate monitoring-mixin:
194+
{
195+
grafanaDashboards+:: helloworld.grafana.dashboards,
196+
prometheusAlerts+:: helloworld.prometheus.alerts,
197+
prometheusRules+:: helloworld.prometheus.recordingRules,
198+
}
199+
```
200+
201+
## Recommended dev environment
202+
203+
To speed up developing observability libs as-code, we recommend to work in the following dev enviroment:
204+
205+
- Setup Vscode with [Jsonnet Language Server][https://marketplace.visualstudio.com/items?itemName=Grafana.vscode-jsonnet]
206+
- Setup format on save in vscode to lint jsonnet automatically.
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
{
2+
new(this): {
3+
4+
groups: [
5+
{
6+
name: 'helloworld-alerts-' + this.config.uid,
7+
rules: [
8+
{
9+
alert: 'HelloWorldAlert1',
10+
expr: |||
11+
100 - (avg without (mode, core) (rate(metric1{%(filteringSelector)s}[5m])) * 100) > %(alertsThreshold1)s
12+
||| % this.config,
13+
'for': '5m',
14+
labels: {
15+
severity: 'warning',
16+
},
17+
annotations: {
18+
summary: 'Something bad happened.',
19+
description: |||
20+
Something bad happened on {{ $labels.instance }} and now above %(alertsThreshold1)s%%. The current value is {{ $value | printf "%%.2f" }}.
21+
||| % this.config,
22+
},
23+
},
24+
{
25+
alert: 'HelloWorldAlert2',
26+
expr: |||
27+
100 - ((metric2{%(filteringSelector)s}
28+
/
29+
metric3{%(filteringSelector)s}) * 100) > %(alertsThreshold2)s
30+
||| % this.config,
31+
'for': '5m',
32+
labels: {
33+
severity: 'critical',
34+
},
35+
annotations: {
36+
summary: 'Something else detected.',
37+
description: |||
38+
Something else detected on {{ $labels.instance }}, is above %(alertsThreshold2)s%%. The current value is {{ $value | printf "%%.2f" }}.
39+
||| % this.config,
40+
},
41+
},
42+
],
43+
},
44+
],
45+
},
46+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
local commonlib = import 'common-lib/common/main.libsonnet';
2+
3+
{
4+
new(this):
5+
local grafana = this.grafana;
6+
local instanceLabels = this.config.instanceLabels;
7+
local groupLabels = this.config.groupLabels;
8+
{
9+
reboot:
10+
commonlib.annotations.reboot.new(
11+
title='Reboot',
12+
target=this.grafana.targets.uptime1,
13+
instanceLabels=std.join(',', instanceLabels),
14+
)
15+
+ commonlib.annotations.base.withTagKeys(std.join(',', groupLabels + instanceLabels)),
16+
}
17+
+
18+
if
19+
this.config.enableLokiLogs
20+
// add logs-based annotations only if loki enabled
21+
then
22+
{
23+
criticalEvents:
24+
commonlib.annotations.base.new(
25+
title='Interesting system event from logs',
26+
target=this.grafana.targets.lokiQuery1,
27+
)
28+
+ commonlib.annotations.base.withTagKeys(std.join(',', groupLabels + instanceLabels + ['level']))
29+
+ commonlib.annotations.base.withTextFormat('{{message}}'),
30+
}
31+
else
32+
{},
33+
34+
}

0 commit comments

Comments
 (0)