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