diff --git a/src/exporter.go b/src/exporter.go
index 5d568c3..97e8887 100644
--- a/src/exporter.go
+++ b/src/exporter.go
@@ -49,11 +49,37 @@ var (
 		"Number of errors found since startup.",
 		[]string{"type"}, nil,
 	)
+
 	metricServerPing = prometheus.NewDesc(
 		prometheus.BuildFQName(sockNamespace, "", "up"),
 		"Check if the fail2ban server is up",
 		nil, nil,
 	)
+	metricJailCount = prometheus.NewDesc(
+		prometheus.BuildFQName(sockNamespace, "", "jail_count"),
+		"Number of defined jails",
+		nil, nil,
+	)
+	metricJailFailedCurrent = prometheus.NewDesc(
+		prometheus.BuildFQName(sockNamespace, "", "jail_failed_current"),
+		"Number of current failures on this jail's filter",
+		[]string{"jail"}, nil,
+	)
+	metricJailFailedTotal = prometheus.NewDesc(
+		prometheus.BuildFQName(sockNamespace, "", "jail_failed_total"),
+		"Number of total failures on this jail's filter",
+		[]string{"jail"}, nil,
+	)
+	metricJailBannedCurrent = prometheus.NewDesc(
+		prometheus.BuildFQName(sockNamespace, "", "jail_banned_current"),
+		"Number of IPs currently banned in this jail",
+		[]string{"jail"}, nil,
+	)
+	metricJailBannedTotal = prometheus.NewDesc(
+		prometheus.BuildFQName(sockNamespace, "", "jail_banned_total"),
+		"Total number of IPs banned by this jail (includes expired bans)",
+		[]string{"jail"}, nil,
+	)
 )
 
 type Exporter struct {
@@ -73,6 +99,11 @@ func (e *Exporter) Describe(ch chan<- *prometheus.Desc) {
 	}
 	if e.socket != nil {
 		ch <- metricServerPing
+		ch <- metricJailCount
+		ch <- metricJailFailedCurrent
+		ch <- metricJailFailedTotal
+		ch <- metricJailBannedCurrent
+		ch <- metricJailBannedTotal
 	}
 }
 
@@ -86,6 +117,7 @@ func (e *Exporter) Collect(ch chan<- prometheus.Metric) {
 	}
 	if e.socket != nil {
 		e.collectServerPingMetric(ch)
+		e.collectJailMetrics(ch)
 	}
 }
 
@@ -164,6 +196,42 @@ func (e *Exporter) collectServerPingMetric(ch chan<- prometheus.Metric) {
 	)
 }
 
+func (e *Exporter) collectJailMetrics(ch chan<- prometheus.Metric) {
+	jails, err := e.socket.GetJails()
+	var count float64 = 0
+	if err == nil {
+		count = float64(len(jails))
+	}
+	ch <- prometheus.MustNewConstMetric(
+		metricJailCount, prometheus.GaugeValue, count,
+	)
+
+	for i := range jails {
+		e.collectJailStatsMetric(ch, jails[i])
+	}
+}
+
+func (e *Exporter) collectJailStatsMetric(ch chan<- prometheus.Metric, jail string) {
+	stats, err := e.socket.GetJailStats(jail)
+	if err != nil {
+		log.Printf("failed to get stats for jail %s: %v", jail, err)
+		return
+	}
+
+	ch <- prometheus.MustNewConstMetric(
+		metricJailFailedCurrent, prometheus.GaugeValue, float64(stats.FailedCurrent), jail,
+	)
+	ch <- prometheus.MustNewConstMetric(
+		metricJailFailedTotal, prometheus.GaugeValue, float64(stats.FailedTotal), jail,
+	)
+	ch <- prometheus.MustNewConstMetric(
+		metricJailBannedCurrent, prometheus.GaugeValue, float64(stats.BannedCurrent), jail,
+	)
+	ch <- prometheus.MustNewConstMetric(
+		metricJailBannedTotal, prometheus.GaugeValue, float64(stats.BannedTotal), jail,
+	)
+}
+
 func printAppVersion() {
 	fmt.Println(version)
 	fmt.Printf("    build date:  %s\r\n    commit hash: %s\r\n    built by:    %s\r\n", date, commit, builtBy)
diff --git a/src/socket/fail2banSocket.go b/src/socket/fail2banSocket.go
index 35513c8..8cce14f 100644
--- a/src/socket/fail2banSocket.go
+++ b/src/socket/fail2banSocket.go
@@ -1,11 +1,12 @@
 package socket
 
 import (
-	"log"
-	"net"
-
+	"fmt"
 	"github.com/kisielk/og-rek"
 	"github.com/nlpodyssey/gopickle/types"
+	"log"
+	"net"
+	"strings"
 )
 
 type Fail2BanSocket struct {
@@ -13,6 +14,13 @@ type Fail2BanSocket struct {
 	encoder *ogórek.Encoder
 }
 
+type JailStats struct {
+	FailedCurrent int
+	FailedTotal   int
+	BannedCurrent int
+	BannedTotal   int
+}
+
 func MustConnectToSocket(path string) *Fail2BanSocket {
 	c, err := net.Dial("unix", path)
 	if err != nil {
@@ -37,6 +45,85 @@ func (s *Fail2BanSocket) Ping() bool {
 		}
 		log.Printf("unexpected response data: %s", t)
 	}
-	log.Printf("unexpected response format - cannot parse: %v", response)
+	log.Printf("(%s) unexpected response format - cannot parse: %v", pingCommand, response)
 	return false
 }
+
+func (s *Fail2BanSocket) GetJails() ([]string, error) {
+	response, err := s.sendCommand([]string{statusCommand})
+	if err != nil {
+		return nil, err
+	}
+
+	if lvl1, ok := response.(*types.Tuple); ok {
+		if lvl2, ok := lvl1.Get(1).(*types.List); ok {
+			if lvl3, ok := lvl2.Get(1).(*types.Tuple); ok {
+				if lvl4, ok := lvl3.Get(1).(string); ok {
+					splitJails := strings.Split(lvl4, ",")
+					return trimSpaceForAll(splitJails), nil
+				}
+			}
+		}
+	}
+	return nil, newBadFormatError(statusCommand, response)
+}
+
+func (s *Fail2BanSocket) GetJailStats(jail string) (JailStats, error) {
+	response, err := s.sendCommand([]string{statusCommand, jail})
+	if err != nil {
+		return JailStats{}, err
+	}
+
+	stats := JailStats{
+		FailedCurrent: -1,
+		FailedTotal:   -1,
+		BannedCurrent: -1,
+		BannedTotal:   -1,
+	}
+
+	if lvl1, ok := response.(*types.Tuple); ok {
+		if lvl2, ok := lvl1.Get(1).(*types.List); ok {
+			if filter, ok := lvl2.Get(0).(*types.Tuple); ok {
+				if filterLvl1, ok := filter.Get(1).(*types.List); ok {
+					if filterCurrentTuple, ok := filterLvl1.Get(0).(*types.Tuple); ok {
+						if filterCurrent, ok := filterCurrentTuple.Get(1).(int); ok {
+							stats.FailedCurrent = filterCurrent
+						}
+					}
+					if filterTotalTuple, ok := filterLvl1.Get(1).(*types.Tuple); ok {
+						if filterTotal, ok := filterTotalTuple.Get(1).(int); ok {
+							stats.FailedTotal = filterTotal
+						}
+					}
+				}
+			}
+			if actions, ok := lvl2.Get(1).(*types.Tuple); ok {
+				if actionsLvl1, ok := actions.Get(1).(*types.List); ok {
+					if actionsCurrentTuple, ok := actionsLvl1.Get(0).(*types.Tuple); ok {
+						if actionsCurrent, ok := actionsCurrentTuple.Get(1).(int); ok {
+							stats.BannedCurrent = actionsCurrent
+						}
+					}
+					if actionsTotalTuple, ok := actionsLvl1.Get(1).(*types.Tuple); ok {
+						if actionsTotal, ok := actionsTotalTuple.Get(1).(int); ok {
+							stats.BannedTotal = actionsTotal
+						}
+					}
+				}
+			}
+			return stats, nil
+		}
+	}
+	return stats, newBadFormatError(statusCommand, response)
+}
+
+func newBadFormatError(command string, data interface{}) error {
+	return fmt.Errorf("(%s) unexpected response format - cannot parse: %v", command, data)
+}
+
+func trimSpaceForAll(slice []string) []string {
+	for i := range slice {
+		slice[i] = strings.TrimSpace(slice[i])
+	}
+	return slice
+}
diff --git a/src/socket/protocol.go b/src/socket/protocol.go
index 1cf533d..5320f5b 100644
--- a/src/socket/protocol.go
+++ b/src/socket/protocol.go
@@ -10,6 +10,7 @@ import (
 const (
 	commandTerminator    = "<F2B_END_COMMAND>"
 	pingCommand          = "ping"
+	statusCommand        = "status"
 	socketReadBufferSize = 1024
 )