diff --git a/go.mod b/go.mod
index 66def3213e37c475bb9967f3b12a3ba8e3573895..c215a3ec313d0f5440d707709baeb136bb44a78a 100644
--- a/go.mod
+++ b/go.mod
@@ -1,3 +1,5 @@
 module git.fsmpi.rwth-aachen.de/thomas/go-rwthdns
 
 go 1.18
+
+require github.com/go-logr/logr v1.2.3
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000000000000000000000000000000000000..6da913857d049bc485b993e3e1e862acb2da182a
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,2 @@
+github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
+github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
diff --git a/rwthdns.go b/rwthdns.go
index 4c9af7ac47e33744f679aeaa673ea3d46ad1fbfb..ea632338bccd99b788751328a14a71bc795ebb92 100644
--- a/rwthdns.go
+++ b/rwthdns.go
@@ -8,6 +8,8 @@ import (
 	"net/url"
 	"strings"
 	"time"
+
+	"github.com/go-logr/logr"
 )
 
 const ApiBase = "https://noc-portal.rz.rwth-aachen.de/dns-admin/api/v1/"
@@ -76,16 +78,29 @@ type Record struct {
 type Client struct {
 	ApiToken string
 	Client   *http.Client
+	Log      *logr.Logger
+}
+
+func (c *Client) log() logr.Logger {
+	if c.Log != nil {
+		return c.Log.WithName("rwthdns")
+	}
+	return logr.Discard()
 }
 
 func (c *Client) do(method string, endpoint string, params url.Values) (*http.Response, error) {
+	logger := c.log().WithValues("function", "do")
 	if c.Client == nil {
 		c.Client = http.DefaultClient
 	}
 
+	logger.V(6).Info("arguments", "method", method, "endpoint", endpoint, "params", params)
+
 	r, err := http.NewRequest(method, ApiBase+endpoint,
 		strings.NewReader(params.Encode()))
 	if err != nil {
+		logger.Error(err, "NewRequest", "method", method, "url", ApiBase+endpoint,
+			"params", params.Encode())
 		return nil, err
 	}
 	r.Header.Set("Content-Type", "application/x-www-form-urlencoded")
@@ -96,6 +111,8 @@ func (c *Client) do(method string, endpoint string, params url.Values) (*http.Re
 }
 
 func (c *Client) ListZones(search *string) ([]Zone, error) {
+	logger := c.log().WithValues("function", "ListZones")
+	logger.V(6).Info("arguments", "search", search)
 	q := url.Values{}
 	if search != nil {
 		q.Set("search", *search)
@@ -103,16 +120,32 @@ func (c *Client) ListZones(search *string) ([]Zone, error) {
 
 	res, err := c.do("GET", "list_zones", q)
 	if err != nil {
+		logger.Error(err, "(*Client).do()",
+			"method", "GET", "endpoint", "list_zones", "params", q)
 		return nil, err
 	}
 
-	dec := json.NewDecoder(res.Body)
+	b, err := io.ReadAll(res.Body)
+	if err != nil {
+		logger.Error(err, "io.ReadAll(res.Body)")
+		return nil, err
+	}
+	bs := string(b)
+	logger.V(6).Info("response", "content", bs, "response", responseInfo(res))
+
+	dec := json.NewDecoder(strings.NewReader(bs))
 	var zones []Zone
 	err = dec.Decode(&zones)
+	if err != nil {
+		logger.Error(err, "json.Decoder.Decode()", "zones", zones, "content", bs)
+		err = fmt.Errorf("Decode: %w (%s)", err, bs)
+	}
 	return zones, err
 }
 
 func (c *Client) ListRecords(zone *int, search *string) ([]Record, error) {
+	logger := c.log().WithValues("function", "ListRecords")
+	logger.V(6).Info("arguments", "zone", zone, "search", search)
 	q := url.Values{}
 	if zone != nil {
 		q.Add("zone_id", fmt.Sprintf("%d", *zone))
@@ -123,50 +156,76 @@ func (c *Client) ListRecords(zone *int, search *string) ([]Record, error) {
 
 	res, err := c.do("GET", "list_records", q)
 	if err != nil {
+		logger.Error(err, "(*Client).do()",
+			"method", "GET", "endpoint", "list_records", "params", q)
 		return nil, err
 	}
 
-	dec := json.NewDecoder(res.Body)
+	b, err := io.ReadAll(res.Body)
+	if err != nil {
+		logger.Error(err, "io.ReadAll(res.Body)")
+		return nil, err
+	}
+	bs := string(b)
+	logger.V(6).Info("response", "content", bs, "response", responseInfo(res))
+
+	dec := json.NewDecoder(strings.NewReader(bs))
 	var records []Record
 	err = dec.Decode(&records)
+	if err != nil {
+		logger.Error(err, "json.Decoder.Decode()", "records", records, "content", bs)
+		err = fmt.Errorf("Decode: %w (%s)", err, bs)
+	}
 	return records, err
 }
 
 func (c *Client) DeployZone(zone int) (Zone, error) {
+	logger := c.log().WithValues("function", "DeployZone")
+	logger.V(6).Info("arguments", "zone", zone)
 	q := url.Values{}
 	q.Set("zone_id", fmt.Sprintf("%d", zone))
 
 	res, err := c.do("POST", "deploy_zone", q)
 	if err != nil {
+		logger.Error(err, "(*Client).do()",
+			"method", "POST", "endpoint", "deploy_zone", "params", q)
 		return Zone{}, err
 	}
 
 	b, err := io.ReadAll(res.Body)
 	if err != nil {
+		logger.Error(err, "io.ReadAll(res.Body)")
 		return Zone{}, err
 	}
 	bs := string(b)
+	logger.V(6).Info("response", "content", bs, "response", responseInfo(res))
 
 	dec := json.NewDecoder(strings.NewReader(bs))
 	var z Zone
 	err = dec.Decode(&z)
 	if err != nil {
+		logger.Error(err, "json.Decoder.Decode()", "zone", z, "content", bs)
 		err = fmt.Errorf("Decode: %w (%s)", err, bs)
 	}
 	return z, err
 }
 
 func (c *Client) CreateRecord(content string) (Record, error) {
+	logger := c.log().WithValues("function", "CreateRecord")
+	logger.V(6).Info("arguments", "content", content)
 	q := url.Values{}
 	q.Set("record_content", content)
 
 	res, err := c.do("POST", "create_record", q)
 	if err != nil {
+		logger.Error(err, "(*Client).do()",
+			"method", "POST", "endpoint", "create_record", "params", q)
 		return Record{}, err
 	}
 
 	b, err := io.ReadAll(res.Body)
 	if err != nil {
+		logger.Error(err, "io.ReadAll(res.Body)")
 		return Record{}, err
 	}
 	bs := string(b)
@@ -175,53 +234,111 @@ func (c *Client) CreateRecord(content string) (Record, error) {
 	var r Record
 	err = dec.Decode(&r)
 	if err != nil {
+		logger.Error(err, "json.Decoder.Decode()", "record", r, "content", bs)
 		err = fmt.Errorf("Decode: %w (%s)", err, bs)
 	}
 	return r, err
 }
 
 func (c *Client) UpdateRecord(record int, content string) (Record, error) {
+	logger := c.log().WithValues("function", "UpdateRecord")
+	logger.V(6).Info("arguments", "record", record, "content", content)
 	q := url.Values{}
 	q.Set("record_id", fmt.Sprintf("%d", record))
 	q.Set("record_content", content)
 
 	res, err := c.do("POST", "update_record", q)
 	if err != nil {
+		logger.Error(err, "(*Client).do()",
+			"method", "POST", "endpoint", "update_record", "params", q)
+		return Record{}, err
+	}
+
+	b, err := io.ReadAll(res.Body)
+	if err != nil {
+		logger.Error(err, "io.ReadAll(res.Body)")
 		return Record{}, err
 	}
+	bs := string(b)
+	logger.V(6).Info("response", "content", bs, "response", responseInfo(res))
 
-	dec := json.NewDecoder(res.Body)
+	dec := json.NewDecoder(strings.NewReader(bs))
 	var r Record
 	err = dec.Decode(&r)
+	if err != nil {
+		logger.Error(err, "json.Decoder.Decode()", "record", r, "content", bs)
+		err = fmt.Errorf("Decode: %w (%s)", err, bs)
+	}
 	return r, err
 }
 
 func (c *Client) DestroyRecord(record int) (Record, error) {
+	logger := c.log().WithValues("function", "DestroyRecord")
+	logger.V(6).Info("arguments", "record", record)
 	q := url.Values{}
 	q.Set("record_id", fmt.Sprintf("%d", record))
 
 	res, err := c.do("DELETE", "destroy_record", q)
 	if err != nil {
+		logger.Error(err, "(*Client).do()",
+			"method", "DELETE", "endpoint", "destroy_record", "params", q)
 		return Record{}, err
 	}
 
-	dec := json.NewDecoder(res.Body)
+	b, err := io.ReadAll(res.Body)
+	if err != nil {
+		logger.Error(err, "io.ReadAll(res.Body)")
+		return Record{}, err
+	}
+	bs := string(b)
+	logger.V(6).Info("response", "content", bs, "response", responseInfo(res))
+
+	dec := json.NewDecoder(strings.NewReader(bs))
 	var r Record
 	err = dec.Decode(&r)
+	if err != nil {
+		logger.Error(err, "json.Decoder.Decode()", "record", r, "content", bs)
+		err = fmt.Errorf("Decode: %w (%s)", err, bs)
+	}
 	return r, err
 }
 
 func (c *Client) RestoreRecord(record int) (Record, error) {
+	logger := c.log().WithValues("function", "RestoreRecord")
+	logger.V(6).Info("arguments", "record", record)
 	q := url.Values{}
 	q.Set("record_id", fmt.Sprintf("%d", record))
 
 	res, err := c.do("POST", "restore_record", q)
 	if err != nil {
+		logger.Error(err, "(*Client).do()",
+			"method", "POST", "endpoint", "restore_record", "params", q)
 		return Record{}, err
 	}
 
-	dec := json.NewDecoder(res.Body)
+	b, err := io.ReadAll(res.Body)
+	if err != nil {
+		logger.Error(err, "io.ReadAll(res.Body)")
+		return Record{}, err
+	}
+	bs := string(b)
+	logger.V(6).Info("response", "content", bs, "response", responseInfo(res))
+
+	dec := json.NewDecoder(strings.NewReader(bs))
 	var r Record
 	err = dec.Decode(&r)
+	if err != nil {
+		logger.Error(err, "json.Decoder.Decode()", "record", r, "content", bs)
+		err = fmt.Errorf("Decode: %w (%s)", err, bs)
+	}
 	return r, err
 }
+
+func responseInfo(res *http.Response) map[string]interface{} {
+	r := make(map[string]interface{})
+	r["status"] = res.Status
+	r["proto"] = res.Proto
+	r["headers"] = res.Header
+
+	return r
+}