diff --git a/go.mod b/go.mod
index 31b7140aa08af74b45636d7d44a92b84c0e27e42..8e94e41a7caa2c1df64206bfa08406884f803970 100644
--- a/go.mod
+++ b/go.mod
@@ -12,12 +12,14 @@ require (
 	github.com/gorilla/websocket v1.4.0
 	github.com/iotaledger/iota.go v1.0.0-beta.7
 	github.com/kr/pretty v0.1.0 // indirect
+	github.com/kr/text v0.1.0
 	github.com/labstack/echo v3.3.10+incompatible
 	github.com/labstack/gommon v0.2.9 // indirect
 	github.com/pkg/errors v0.8.1
 	github.com/rivo/tview v0.0.0-20190721135419-23dc8a0944e4
 	github.com/rivo/uniseg v0.1.0 // indirect
 	github.com/stretchr/testify v1.3.0
+	github.com/tonnerre/golang-text v0.0.0-20130925195846-048ed3d792f7 // indirect
 	golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4
 	golang.org/x/net v0.0.0-20190724013045-ca1201d0de80
 	golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e // indirect
diff --git a/go.sum b/go.sum
index 3190f6cf086e2f9237295e991a2a9f0de4aa3630..4321f8c8d27761874b2aaa0f37c2554835f26a75 100644
--- a/go.sum
+++ b/go.sum
@@ -100,6 +100,8 @@ github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoH
 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/tonnerre/golang-text v0.0.0-20130925195846-048ed3d792f7 h1:E4pdTAo1tqctAfTr2tkxcX+JkDeJP84bQfjC3ONwoXQ=
+github.com/tonnerre/golang-text v0.0.0-20130925195846-048ed3d792f7/go.mod h1:J5H/d3ZVtRUjmP4Zf87sPSIUSCGFXorzn3hQtdODORQ=
 github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
 github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
 github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
diff --git a/packages/ca/heartbeat/heartbeat.go b/packages/ca/heartbeat/heartbeat.go
index 6087afd891c0c00ad61cf71c9d03668ec2e68e6e..dd2d3da88ada299a51c409f4e1c8315b2a69cb10 100644
--- a/packages/ca/heartbeat/heartbeat.go
+++ b/packages/ca/heartbeat/heartbeat.go
@@ -3,6 +3,8 @@ package heartbeat
 import (
 	"sync"
 
+	"github.com/iotaledger/goshimmer/packages/stringify"
+
 	"github.com/iotaledger/goshimmer/packages/errors"
 	"github.com/iotaledger/goshimmer/packages/marshaling"
 
@@ -28,7 +30,7 @@ func NewHeartbeat() *Heartbeat {
 
 func (heartbeat *Heartbeat) GetNodeId() string {
 	heartbeat.nodeIdMutex.RLock()
-	defer heartbeat.nodeIdMutex.RLock()
+	defer heartbeat.nodeIdMutex.RUnlock()
 
 	return heartbeat.nodeId
 }
@@ -126,3 +128,12 @@ func (heartbeat *Heartbeat) MarshalBinary() ([]byte, errors.IdentifiableError) {
 func (heartbeat *Heartbeat) UnmarshalBinary(data []byte) (err errors.IdentifiableError) {
 	return marshaling.Unmarshal(heartbeat, data, &heartbeatProto.HeartBeat{})
 }
+
+func (heartbeat *Heartbeat) String() string {
+	return stringify.Struct("Heartbeat",
+		stringify.StructField("nodeId", heartbeat.nodeId),
+		stringify.StructField("mainStatement", heartbeat.mainStatement),
+		stringify.StructField("neighborStatements", heartbeat.neighborStatements),
+		stringify.StructField("signature", heartbeat.signature),
+	)
+}
diff --git a/packages/ca/heartbeat/opinion_statement.go b/packages/ca/heartbeat/opinion_statement.go
index 8071114d54ffc3b40ffcaf9f031afed4addd433d..95819a4843dd4085a311abe778f0795fc972b292 100644
--- a/packages/ca/heartbeat/opinion_statement.go
+++ b/packages/ca/heartbeat/opinion_statement.go
@@ -1,10 +1,10 @@
 package heartbeat
 
 import (
-	"encoding/hex"
-	"strconv"
 	"sync"
 
+	"github.com/iotaledger/goshimmer/packages/stringify"
+
 	"github.com/iotaledger/goshimmer/packages/errors"
 	"github.com/iotaledger/goshimmer/packages/marshaling"
 
@@ -157,11 +157,11 @@ func (opinionStatement *OpinionStatement) UnmarshalBinary(data []byte) (err erro
 }
 
 func (opinionStatement *OpinionStatement) String() string {
-	return "OpinionStatement {\n" +
-		"    previousStatementHash: 0x" + hex.EncodeToString(opinionStatement.previousStatementHash) + "\n" +
-		"    nodeId:                " + opinionStatement.nodeId + "\n" +
-		"    time:                  " + strconv.Itoa(int(opinionStatement.time)) + "\n" +
-		"    toggledTransactions:   [" + "" + "]\n" +
-		"    signature:             0x" + hex.EncodeToString(opinionStatement.signature) + "\n" +
-		"}"
+	return stringify.Struct("OpinionStatement",
+		stringify.StructField("previousStatementHash", opinionStatement.previousStatementHash),
+		stringify.StructField("nodeId", opinionStatement.nodeId),
+		stringify.StructField("time", opinionStatement.time),
+		stringify.StructField("toggledTransactions", opinionStatement.toggledTransactions),
+		stringify.StructField("signature", opinionStatement.signature),
+	)
 }
diff --git a/packages/ca/heartbeat/toggled_transaction.go b/packages/ca/heartbeat/toggled_transaction.go
index a6fb4bb97035c2aaf1e5675f1bce55a0f681a05e..8d81371c6578448aa5ff830ad8fe291f7bf70c42 100644
--- a/packages/ca/heartbeat/toggled_transaction.go
+++ b/packages/ca/heartbeat/toggled_transaction.go
@@ -3,6 +3,8 @@ package heartbeat
 import (
 	"sync"
 
+	"github.com/iotaledger/goshimmer/packages/stringify"
+
 	"github.com/iotaledger/goshimmer/packages/errors"
 	"github.com/iotaledger/goshimmer/packages/marshaling"
 
@@ -89,3 +91,11 @@ func (toggledTransaction *ToggledTransaction) MarshalBinary() ([]byte, errors.Id
 func (toggledTransaction *ToggledTransaction) UnmarshalBinary(data []byte) (err errors.IdentifiableError) {
 	return marshaling.Unmarshal(toggledTransaction, data, &heartbeatProto.ToggledTransaction{})
 }
+
+func (toggledTransaction *ToggledTransaction) String() string {
+	return stringify.Struct("ToggledTransaction",
+		stringify.StructField("transactionId", toggledTransaction.transactionId),
+		stringify.StructField("initialStatement", toggledTransaction.initialStatement),
+		stringify.StructField("finalStatement", toggledTransaction.finalStatement),
+	)
+}
diff --git a/packages/ca/heartbeat_manager_test.go b/packages/ca/heartbeat_manager_test.go
index 9c5bc1a59387e8feb15ab1aeaee7b061b891cc7d..42b82105018dc1cea245f5994aa36e9ccdf0397f 100644
--- a/packages/ca/heartbeat_manager_test.go
+++ b/packages/ca/heartbeat_manager_test.go
@@ -12,8 +12,12 @@ func TestHeartbeatManager_GenerateHeartbeat(t *testing.T) {
 	transactionId1 := make([]byte, 50)
 	rand.Read(transactionId1)
 
+	transactionId2 := make([]byte, 50)
+	rand.Read(transactionId2)
+
 	heartbeatManager := NewHeartbeatManager(identity.GenerateRandomIdentity())
 	heartbeatManager.SetInitialOpinion(transactionId1)
+	heartbeatManager.SetInitialOpinion(transactionId2)
 
 	result, err := heartbeatManager.GenerateHeartbeat()
 	if err != nil {
@@ -22,5 +26,5 @@ func TestHeartbeatManager_GenerateHeartbeat(t *testing.T) {
 		return
 	}
 
-	fmt.Println(result.GetMainStatement())
+	fmt.Println(result)
 }
diff --git a/packages/stringify/bool.go b/packages/stringify/bool.go
new file mode 100644
index 0000000000000000000000000000000000000000..0cd7045e0ff3745b222328657a63c493db81d742
--- /dev/null
+++ b/packages/stringify/bool.go
@@ -0,0 +1,9 @@
+package stringify
+
+func Bool(value bool) string {
+	if value {
+		return "true"
+	} else {
+		return "false"
+	}
+}
diff --git a/packages/stringify/constants.go b/packages/stringify/constants.go
new file mode 100644
index 0000000000000000000000000000000000000000..798bff37b40b788cc8758b4e6b4cae6c1de6992e
--- /dev/null
+++ b/packages/stringify/constants.go
@@ -0,0 +1,5 @@
+package stringify
+
+const (
+	INDENTATION_SIZE = 4
+)
diff --git a/packages/stringify/int.go b/packages/stringify/int.go
new file mode 100644
index 0000000000000000000000000000000000000000..0a98f3817647ee5649b524e0af33a6fa9c7e5b0e
--- /dev/null
+++ b/packages/stringify/int.go
@@ -0,0 +1,9 @@
+package stringify
+
+import (
+	"strconv"
+)
+
+func Int(value int) string {
+	return strconv.Itoa(value)
+}
diff --git a/packages/stringify/interface.go b/packages/stringify/interface.go
new file mode 100644
index 0000000000000000000000000000000000000000..bbf4f1989fd4c9eb067e547e4c8d87aef82bb0e4
--- /dev/null
+++ b/packages/stringify/interface.go
@@ -0,0 +1,50 @@
+package stringify
+
+import (
+	"fmt"
+	"reflect"
+	"strconv"
+)
+
+func Interface(value interface{}) string {
+	switch value.(type) {
+	case bool:
+		return Bool(value.(bool))
+	case string:
+		return String(value.(string))
+	case []byte:
+		return SliceOfBytes(value.([]byte))
+	case int:
+		return Int(value.(int))
+	case uint64:
+		return strconv.FormatUint(value.(uint64), 10)
+	case reflect.Value:
+		typeCastedValue := value.(reflect.Value)
+		switch typeCastedValue.Kind() {
+		case reflect.Slice:
+			return sliceReflect(typeCastedValue)
+		case reflect.String:
+			return String(typeCastedValue.String())
+		case reflect.Int:
+			return Int(int(typeCastedValue.Int()))
+		case reflect.Uint8:
+			return Int(int(typeCastedValue.Uint()))
+		case reflect.Ptr:
+			return Interface(typeCastedValue.Interface())
+		default:
+			panic("undefined reflect type: " + typeCastedValue.Kind().String())
+		}
+	case fmt.Stringer:
+		return value.(fmt.Stringer).String()
+	default:
+		value := reflect.ValueOf(value)
+		switch value.Kind() {
+		case reflect.Slice:
+			return sliceReflect(value)
+		case reflect.Map:
+			return mapReflect(value)
+		default:
+			panic("undefined type: " + value.Kind().String())
+		}
+	}
+}
diff --git a/packages/stringify/map.go b/packages/stringify/map.go
new file mode 100644
index 0000000000000000000000000000000000000000..b81741e2e9cc26cbc42274f3abab7dca22a83164
--- /dev/null
+++ b/packages/stringify/map.go
@@ -0,0 +1,31 @@
+package stringify
+
+import (
+	"reflect"
+	"strings"
+
+	"github.com/kr/text"
+)
+
+func Map(value interface{}) string {
+	return mapReflect(reflect.ValueOf(value))
+}
+
+func mapReflect(value reflect.Value) (result string) {
+	result = "map{"
+
+	mapKeys := value.MapKeys()
+	if len(mapKeys) >= 1 {
+		result += "\n"
+	}
+
+	for _, mapKey := range mapKeys {
+		item := value.MapIndex(mapKey)
+
+		result += text.Indent("["+Interface(mapKey)+"]: "+Interface(item)+",\n", strings.Repeat(" ", INDENTATION_SIZE))
+	}
+
+	result += "}"
+
+	return
+}
diff --git a/packages/stringify/map_test.go b/packages/stringify/map_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..b1fb2740299844d3c133b75da1263522c95684fc
--- /dev/null
+++ b/packages/stringify/map_test.go
@@ -0,0 +1,15 @@
+package stringify
+
+import (
+	"fmt"
+	"testing"
+)
+
+func TestMap(t *testing.T) {
+	testMap := make(map[string][]byte)
+
+	testMap["huhu"] = []byte{1, 2}
+	testMap["haha"] = []byte{1, 2, 3, 4}
+
+	fmt.Println(Map(testMap))
+}
diff --git a/packages/stringify/slice.go b/packages/stringify/slice.go
new file mode 100644
index 0000000000000000000000000000000000000000..21c5ffb938a0193256c5094ac2ebbf1f1c971e80
--- /dev/null
+++ b/packages/stringify/slice.go
@@ -0,0 +1,41 @@
+package stringify
+
+import (
+	"reflect"
+	"strings"
+
+	"github.com/kr/text"
+)
+
+func Slice(value []interface{}) string {
+	return sliceReflect(reflect.ValueOf(value))
+}
+
+func sliceReflect(value reflect.Value) (result string) {
+	result += "["
+
+	newLineVersion := false
+	for i := 0; i < value.Len(); i++ {
+		item := value.Index(i)
+
+		valueString := Interface(item)
+		if strings.Contains(valueString, "\n") {
+			if !newLineVersion {
+				result += "\n"
+
+				newLineVersion = true
+			}
+			result += text.Indent(Interface(item)+",\n", strings.Repeat(" ", INDENTATION_SIZE))
+		} else {
+			result += Interface(item) + ", "
+		}
+	}
+
+	if !newLineVersion {
+		result = result[:len(result)-2]
+	}
+
+	result += "]"
+
+	return
+}
diff --git a/packages/stringify/slice_of_bytes.go b/packages/stringify/slice_of_bytes.go
new file mode 100644
index 0000000000000000000000000000000000000000..558a25763ed9a31bc627acf70bd7864c93e2d520
--- /dev/null
+++ b/packages/stringify/slice_of_bytes.go
@@ -0,0 +1,13 @@
+package stringify
+
+import "encoding/hex"
+
+func SliceOfBytes(value []byte) string {
+	if value == nil {
+		return "<nil>"
+	} else if len(value) == 0 {
+		return "<empty>"
+	} else {
+		return "0x" + hex.EncodeToString(value) + ""
+	}
+}
diff --git a/packages/stringify/string.go b/packages/stringify/string.go
new file mode 100644
index 0000000000000000000000000000000000000000..e25c63f517f567a5337faa8e4356f0de0044b92f
--- /dev/null
+++ b/packages/stringify/string.go
@@ -0,0 +1,5 @@
+package stringify
+
+func String(value string) string {
+	return "\"" + value + "\""
+}
diff --git a/packages/stringify/struct.go b/packages/stringify/struct.go
new file mode 100644
index 0000000000000000000000000000000000000000..9d1a50a6e7cfccae8cdcba7bf21a7325a56443e6
--- /dev/null
+++ b/packages/stringify/struct.go
@@ -0,0 +1,31 @@
+package stringify
+
+import (
+	"strings"
+
+	"github.com/kr/text"
+)
+
+func Struct(name string, fields ...*structField) string {
+	return structBuilder{
+		name:   name,
+		fields: fields,
+	}.String()
+}
+
+type structBuilder struct {
+	name   string
+	fields []*structField
+}
+
+func (stringifyStruct structBuilder) String() (result string) {
+	result = stringifyStruct.name + " {\n"
+
+	for _, field := range stringifyStruct.fields {
+		result += text.Indent(field.String()+"\n", strings.Repeat(" ", INDENTATION_SIZE))
+	}
+
+	result += "}"
+
+	return result
+}
diff --git a/packages/stringify/struct_field.go b/packages/stringify/struct_field.go
new file mode 100644
index 0000000000000000000000000000000000000000..d4190b22a4c1ea52b67c533b820d1321657e6929
--- /dev/null
+++ b/packages/stringify/struct_field.go
@@ -0,0 +1,17 @@
+package stringify
+
+type structField struct {
+	name  string
+	value interface{}
+}
+
+func StructField(name string, value interface{}) *structField {
+	return &structField{
+		name:  name,
+		value: value,
+	}
+}
+
+func (structField *structField) String() (result string) {
+	return structField.name + ": " + Interface(structField.value)
+}