Skip to content

Commit 3bb7fce

Browse files
committed
Improve logging messages for Icinga client
It will now warn if an endpoint is not reachable and received data is logged if it could not be JSON unmarshalled
1 parent 9feebc5 commit 3bb7fce

3 files changed

Lines changed: 40 additions & 6 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ Flags:
6969
--plugin-output-annotations=message,... List of Annotation names to be used to set the plugin output for the Icinga service ($ALERTMANAGER_ICINGA_BRIDGE_PLUGINOUTPUT_ANNOTATIONS)
7070
--checks-interval=12h Interval (in seconds) to be used for Icinga check_interval and retry_interval ($ALERTMANAGER_ICINGA_BRIDGE_SERVICE_CHECKS_INTERVAL)
7171
--keep-for=168h How long to keep created alerts around after they have been resolved ($ALERTMANAGER_ICINGA_BRIDGE_KEEP_FOR)
72-
--static-service-vars=KEY=VALUE;... Custom variable to be set for craeted Icinga services (variable=value, can be repeated) ($ALERTMANAGER_ICINGA_BRIDGE_STATIC_SERVICE_VAR)
72+
--static-service-vars=KEY=VALUE;... Custom variable to be set for created Icinga services (variable=value, can be repeated) ($ALERTMANAGER_ICINGA_BRIDGE_STATIC_SERVICE_VAR)
7373
```
7474

7575
Most flags can be set with environment variables, refer to the help to see which flags.

internal/icinga2/icinga.go

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"net"
1515
"net/http"
1616
"net/url"
17+
"strings"
1718
"time"
1819

1920
"github.com/NETWAYS/alertmanager-icinga-bridge/internal/config"
@@ -71,7 +72,7 @@ func NewClient(config *config.Config, logger *slog.Logger) *Client {
7172

7273
// Do is a small wrapper that tries the given request against all given Icinga API endpoints
7374
func (c *Client) Do(req *http.Request, path string) (*http.Response, error) {
74-
lastErr := ErrNoEndpointReachable
75+
var endpointErrors strings.Builder
7576

7677
for _, base := range c.IcingaURL {
7778
req.URL, _ = req.URL.Parse(base + path)
@@ -87,23 +88,24 @@ func (c *Client) Do(req *http.Request, path string) (*http.Response, error) {
8788
resp, err := c.httpClient.Do(req)
8889

8990
if err != nil {
90-
// Got an error while trying to reach the endpoint, trying the next
91-
lastErr = err
91+
c.logger.Warn(fmt.Sprintf("Icinga API not reachable at %s. Trying next endpoint", req.URL), "component", "icinga", "error", err.Error())
92+
fmt.Fprintf(&endpointErrors, " error: '%s' from '%s'", req.URL, err.Error())
9293

94+
// Trying the next endpoint
9395
continue
9496
}
9597

9698
if resp.StatusCode >= 500 {
9799
resp.Body.Close()
98-
lastErr = fmt.Errorf("error %d from %s", resp.StatusCode, req.URL)
100+
fmt.Fprintf(&endpointErrors, " error: '%d' from '%s'", resp.StatusCode, req.URL)
99101

100102
continue
101103
}
102104

103105
return resp, nil
104106
}
105107

106-
return nil, fmt.Errorf("all endpoints failed, last error: %w", lastErr)
108+
return nil, fmt.Errorf("failed to call one of the configured endpoints. %s", endpointErrors.String())
107109
}
108110

109111
// ProcessCheckResult handles a process-check-result for a given service
@@ -173,6 +175,7 @@ func (c *Client) GetHost(ctx context.Context, name string) (Host, error) {
173175
errDecode := json.Unmarshal(bodyBytes, &result)
174176

175177
if errDecode != nil {
178+
c.logger.Debug("Response body: " + string(bodyBytes))
176179
return Host{}, fmt.Errorf("could not unmarshal JSON: %w", errDecode)
177180
}
178181

@@ -216,6 +219,7 @@ func (c *Client) GetService(ctx context.Context, name string) (Service, error) {
216219
errDecode := json.Unmarshal(bodyBytes, &result)
217220

218221
if errDecode != nil {
222+
c.logger.Debug("Response body: " + string(bodyBytes))
219223
return Service{}, fmt.Errorf("could not unmarshal JSON: %w", errDecode)
220224
}
221225

@@ -265,6 +269,7 @@ func (c *Client) GetServices(ctx context.Context, filter QueryFilter) ([]Service
265269
errDecode := json.Unmarshal(bodyBytes, &result)
266270

267271
if errDecode != nil {
272+
c.logger.Debug("Response body: " + string(bodyBytes))
268273
return []Service{}, fmt.Errorf("could not unmarshal JSON: %w", errDecode)
269274
}
270275

internal/icinga2/icinga_test.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
package icinga2
44

55
import (
6+
"bytes"
67
"context"
78
"encoding/json"
89
"io"
@@ -11,6 +12,7 @@ import (
1112
"net/http/httptest"
1213
"os"
1314
"reflect"
15+
"strings"
1416
"testing"
1517

1618
"github.com/NETWAYS/alertmanager-icinga-bridge/internal/config"
@@ -58,6 +60,33 @@ func TestServiceFullName(t *testing.T) {
5860
}
5961
}
6062

63+
func TestGetHost_WithInvalidReponse(t *testing.T) {
64+
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
65+
w.WriteHeader(http.StatusOK)
66+
w.Write([]byte(`<invalid JSON>`))
67+
}))
68+
69+
defer server.Close()
70+
71+
var buf bytes.Buffer
72+
logger := slog.New(slog.NewTextHandler(&buf, &slog.HandlerOptions{Level: slog.LevelDebug}))
73+
74+
client := NewClient(testConfig(server.URL), logger)
75+
76+
_, err := client.GetHost(context.Background(), "unittest")
77+
78+
if err == nil {
79+
t.Fatalf("expected error, got nil")
80+
}
81+
82+
actual := buf.String()
83+
expected := "Response body: <invalid JSON>"
84+
85+
if !strings.Contains(actual, expected) {
86+
t.Fatalf("expected %v, got %v", expected, actual)
87+
}
88+
}
89+
6190
func TestGetHost(t *testing.T) {
6291
datasetHost := "testdata/host.json"
6392

0 commit comments

Comments
 (0)