1# Script Debugger <a id="script-debugger"></a>
2
3You can run the Icinga 2 daemon with the `-X` (`--script-debugger`)
4parameter to enable the script debugger:
5
6```bash
7icinga2 daemon -X
8```
9
10When an exception occurs or the [debugger](17-language-reference.md#breakpoints)
11keyword is encountered in a user script, Icinga 2 launches a console that
12allows the user to debug the script.
13
14You can also attach the script debugger to the [configuration validation](11-cli-commands.md#config-validation):
15
16```bash
17icinga2 daemon -C -X
18```
19
20Here is a list of common errors which can be diagnosed with the script debugger:
21
22* Configuration errors e.g. [apply rules](03-monitoring-basics.md#using-apply)
23* Errors in user-defined [functions](17-language-reference.md#functions)
24
25## Debugging Configuration Errors <a id="script-debugger-config-errors"></a>
26
27The following example illustrates the problem of a service [apply rule](03-monitoring-basics.md#using-apply-for)
28which expects a dictionary value for `config`, but the host custom variable only
29provides a string value:
30
31```
32object Host "script-debugger-host" {
33  check_command = "icinga"
34
35  vars.http_vhosts["example.org"] = "192.168.1.100" // a string value
36}
37
38apply Service for (http_vhost => config in host.vars.http_vhosts) {
39  import "generic-service"
40
41  vars += config // expects a dictionary
42
43  check_command = "http"
44}
45```
46
47The error message on config validation will warn about the wrong value type,
48but does not provide any context which objects are affected.
49
50Enable the script debugger and run the config validation:
51
52```
53# icinga2 daemon -C -X
54
55Breakpoint encountered in /etc/icinga2/conf.d/services.conf: 59:67-65:1
56Exception: Error: Error while evaluating expression: Cannot convert value of type 'String' to an object.
57Location:
58/etc/icinga2/conf.d/services.conf(62):   check_command = "http"
59/etc/icinga2/conf.d/services.conf(63):
60/etc/icinga2/conf.d/services.conf(64):   vars += config
61                                         ^^^^^^^^^^^^^^
62/etc/icinga2/conf.d/services.conf(65): }
63/etc/icinga2/conf.d/services.conf(66):
64You can inspect expressions (such as variables) by entering them at the prompt.
65To leave the debugger and continue the program use "$continue".
66<1> =>
67```
68
69You can print the variables `vars` and `config` to get an idea about
70their values:
71
72```
73<1> => vars
74null
75<2> => config
76"192.168.1.100"
77<3> =>
78```
79
80The `vars` attribute has to be a dictionary. Trying to set this attribute to a string caused
81the error in our configuration example.
82
83In order to determine the name of the host where the value of the `config` variable came from
84you can inspect attributes of the service object:
85
86```
87<3> => host_name
88"script-debugger-host-01"
89<4> => name
90"http"
91```
92
93Additionally you can view the service object attributes by printing the value of `this`.
94
95## Using Breakpoints <a id="script-debugger-breakpoints"></a>
96
97In order to halt execution in a script you can use the `debugger` keyword:
98
99```
100object Host "script-debugger-host-02" {
101  check_command = "dummy"
102  check_interval = 5s
103
104  vars.dummy_text = {{
105    var text = "Hello from " + macro("$name$")
106    debugger
107    return text
108  }}
109}
110```
111
112Icinga 2 will spawn a debugger console every time the function is executed:
113
114```
115# icinga2 daemon -X
116...
117Breakpoint encountered in /etc/icinga2/tests/script-debugger.conf: 7:5-7:12
118You can inspect expressions (such as variables) by entering them at the prompt.
119To leave the debugger and continue the program use "$continue".
120<1> => text
121"Hello from script-debugger-host-02"
122<2> => $continue
123```
124
125## Debugging API Filters <a id="script-debugger-api-filters"></a>
126
127Queries against the [Icinga 2 REST API](12-icinga2-api.md#icinga2-api) can use
128filters, just like available in `assign where` expressions. If these filters cause
129an internal error, they return an empty result to the caller.
130
131In order to analyse these server-side errors, you can use the script debugger.
132
133The following example tries filter for all host objects where the custom variable
134`os` is set. There are various possibilities to check that, one of them would be
135`host.vars.os != ""`. Another idea is to use the [contains](18-library-reference.md#dictionary-contains) method on the custom
136attribute dictionary like this: `host.vars.contains("os")`.
137
138```bash
139curl -k -s -u root:icinga -H 'Accept: application/json' -H 'X-HTTP-Method-Override: GET' \
140 -X POST 'https://localhost:5665/v1/objects/services' \
141 -d '{ "filter": "host.vars.contains(\"os\")", "attrs": [ "__name" ], "joins": [ "host.name", "host.vars" ], "pretty": true }'
142```
143
144This will fail on all hosts which don't have any custom variable specified.
145
146```
147# icinga2 daemon -X
148
149Breakpoint encountered.
150Exception: Error: Argument is not a callable object.
151Location: in <API query>: 1:0-1:23
152You can inspect expressions (such as variables) by entering them at the prompt.
153To leave the debugger and continue the program use "$continue".
154
155<1> => this.host
156
157...
158
159    	vars = null
160
161<2> => $continue
162```
163
164By definition, a type method can only be invoked on an actual object.
165
166In order to stay safe, add more checks to the API filter:
167
168- `host.vars && host.vars.contains("os")` or
169- `host.vars && typeof(host.vars) == Dictionary && host.vars.contains("os")`
170
171Example:
172
173```bash
174curl -k -s -u root:icinga -H 'Accept: application/json' -H 'X-HTTP-Method-Override: GET' \
175 -X POST 'https://localhost:5665/v1/objects/services' \
176 -d '{ "filter": "host.vars && typeof(host.vars) == Dictionary && host.vars.contains(\"os\")", "attrs": [ "__name" ], "joins": [ "host.name", "host.vars" ], "pretty": true }'
177```
178