Skip to content

Commit 3c8d7ac

Browse files
committed
Updates to support BI6 & syslog to loki documentation and configs
1 parent 681e20b commit 3c8d7ac

File tree

8 files changed

+3026
-521
lines changed

8 files changed

+3026
-521
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
logs/*
1+
logs/*
2+
*.exe

README.md

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
# blueiris_exporter
22
Prometheus exporter for Blue Iris.
3-
Confirmed working on Blue Iris version's 5.5.0.12 to 5.9.2.2
3+
Confirmed working on Blue Iris version's 5.5.0.12 to 6.0.1.22
44

5-
>**Attention: blueiris_exporter is currently not combitible with Blue Iris version >=6. There is currently no ETA on when/if support will be added.**
5+
In newer versions of Blue Iris, there is an option to export the logs to a SysLog server.
6+
As an alternative to the blueiris_exporter (or in addition to), you can follow [this guide](syslog/syslog_to_loki.md) to learn how to set this up.
67

78
If you increment `parse_errors` metrics, please send me the details and I will work on adding support for it. Blue Iris has changed it's log format for AI a few times and I'm working on adding support for all of it. Also, if you have any ideas for different metrics, let me know!
89

@@ -160,6 +161,12 @@ profile | Count of activation of profiles
160161
ai_error | Count of AI error log lines
161162

162163

163-
## Grafana Dashboard
164-
https://grafana.com/grafana/dashboards/17432-blueiris/
165-
![Grafana Dashboard](images/grafana-dashboard.png)
164+
## Grafana Dashboards
165+
166+
### blueiris_exporter
167+
https://grafana.com/grafana/dashboards/17432-blueiris/<br>
168+
grafana_dashboard.json
169+
![Grafana Dashboard](images/grafana-dashboard.png)
170+
171+
### Blue Iris Loki
172+
syslog/grafana_dashboard_loki.json

blueiris/blueirisMetrics.go

Lines changed: 47 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@ func BlueIris(ch chan<- prometheus.Metric, m common.MetricInfo, SecMet []common.
5858
scrapeTime := time.Now()
5959
mutex.Lock()
6060

61-
parseErrors = make(map[string]float64)
6261
startScanning := false
6362

6463
dir := logpath
@@ -336,19 +335,46 @@ func findObject(line string) (match []string, r *regexp.Regexp, matchType string
336335
} else if strings.HasSuffix(line, "AI: not responding") {
337336
notrespondingcount++
338337

339-
} else if strings.Contains(line, "EXTERNAL") || strings.Contains(line, "MOTION") || strings.Contains(line, "DIO") || strings.Contains(line, "Triggered") || strings.Contains(line, "Re-triggered") {
340-
r := regexp.MustCompile(`(?P<camera>[^\s\\]*)(\s*(?P<motion>EXTERNAL|MOTION|DIO|Triggered|Re-triggered))`)
338+
} else if strings.Contains(line, "AI:") || strings.Contains(line, "DeepStack:") || strings.Contains(line, "Trigger: Alert canceled") {
339+
newLine := strings.Join(strings.Fields(line), " ")
340+
r := regexp.MustCompile(`(?P<camera>[^\s\\]*)(\sAI:\s|\sDeepStack:\s|\sTrigger:\s)(\[Objects\]\s|Alert\s|\[.+\]\s|)(?P<object>[aA-zZ]*|cancelled|canceled)(\s|:)(\[|)(?P<detail>[0-9]*|.*)(%|\])(\s)(\[.+\]\s|)(?P<duration>[0-9]*)ms`)
341+
match := r.FindStringSubmatch(newLine)
342+
343+
if strings.Contains(newLine, "CodeProject.AI") {
344+
return nil, nil, ""
345+
}
346+
347+
if len(match) == 0 {
348+
r2 := regexp.MustCompile(`(?P<camera>[^\s\\]*)(\sAI:\s|\sDeepStack:\s)(\[Objects\]\s|Alert\s|\[.+\]\s|)(?P<object>[aA-zZ]*|cancelled|canceled)(\s|:)(\[|)(?P<detail>[0-9]*|.*)`)
349+
match2 := r2.FindStringSubmatch(newLine)
350+
if len(match2) == 0 {
351+
parseErrors = appendCounterMap(parseErrors, line)
352+
parseErrorsTotal++
353+
}
354+
return nil, nil, ""
355+
} else {
356+
if strings.Contains(newLine, "cancelled") || strings.Contains(newLine, "canceled") {
357+
return match, r, "canceled"
358+
} else {
359+
return match, r, "alert"
360+
}
361+
}
362+
363+
} else if strings.Contains(line, "EXTERNAL") || strings.Contains(line, "MOTION") || strings.Contains(line, "DIO") || strings.Contains(line, "Triggered") || strings.Contains(line, "Re-triggered") || strings.Contains(line, "Trigger") {
364+
r := regexp.MustCompile(`(?P<camera>[^\s\\]*)(\s*(?P<motion>EXTERNAL|MOTION|DIO|Triggered|Re-triggered|Trigger))`)
341365
match := r.FindStringSubmatch(line)
342366
if len(match) == 0 {
343367
parseErrors = appendCounterMap(parseErrors, line)
344368
parseErrorsTotal++
345369
} else {
346-
cameraMatch := r.SubexpIndex("camera")
347-
camera := match[cameraMatch]
348-
triggerCount[camera]++
349-
makeMap(camera, camerastatus)
350-
camerastatus[camera]["status"] = 0.0
351-
camerastatus[camera]["detail"] = "trigger"
370+
if !strings.Contains(line, "Alert canceled") || !strings.Contains(line, "Alert confirmed") {
371+
cameraMatch := r.SubexpIndex("camera")
372+
camera := match[cameraMatch]
373+
triggerCount[camera]++
374+
makeMap(camera, camerastatus)
375+
camerastatus[camera]["status"] = 0.0
376+
camerastatus[camera]["detail"] = "trigger"
377+
}
352378
}
353379

354380
} else if strings.Contains(line, "Push:") {
@@ -368,27 +394,6 @@ func findObject(line string) (match []string, r *regexp.Regexp, matchType string
368394
pushCount[camera+"|"+status+"|"+detail]++
369395
}
370396

371-
} else if strings.Contains(line, "AI:") || strings.Contains(line, "DeepStack:") {
372-
newLine := strings.Join(strings.Fields(line), " ")
373-
r := regexp.MustCompile(`(?P<camera>[^\s\\]*)(\sAI:\s|\sDeepStack:\s)(\[Objects\]\s|Alert\s|\[.+\]\s|)(?P<object>[aA-zZ]*|cancelled|canceled)(\s|:)(\[|)(?P<detail>[0-9]*|.*)(%|\])(\s)(\[.+\]\s|)(?P<duration>[0-9]*)ms`)
374-
match := r.FindStringSubmatch(newLine)
375-
376-
if len(match) == 0 {
377-
r2 := regexp.MustCompile(`(?P<camera>[^\s\\]*)(\sAI:\s|\sDeepStack:\s)(\[Objects\]\s|Alert\s|\[.+\]\s|)(?P<object>[aA-zZ]*|cancelled|canceled)(\s|:)(\[|)(?P<detail>[0-9]*|.*)`)
378-
match2 := r2.FindStringSubmatch(newLine)
379-
if len(match2) == 0 {
380-
parseErrors = appendCounterMap(parseErrors, line)
381-
parseErrorsTotal++
382-
}
383-
return nil, nil, ""
384-
} else {
385-
if strings.Contains(newLine, "cancelled") || strings.Contains(newLine, "canceled") {
386-
return match, r, "canceled"
387-
} else {
388-
return match, r, "alert"
389-
}
390-
}
391-
392397
} else if strings.Contains(line, "Signal:") {
393398
r := regexp.MustCompile(`(?P<camera>[^\s\\]*)(\s*Signal:\s)(?P<status>.+)`)
394399
match := r.FindStringSubmatch(line)
@@ -615,11 +620,20 @@ func findObject(line string) (match []string, r *regexp.Regexp, matchType string
615620
}
616621

617622
func appendCounterMap(m map[string]float64, key string) map[string]float64 {
618-
if val, ok := m[key]; ok {
623+
logr := regexp.MustCompile(`^.+(\.\d\d\d|\s[APM]{2})\s(?P<log>.+)`)
624+
logmatch := logr.FindStringSubmatch(key)
625+
626+
logKey := key
627+
if len(logmatch) != 0 {
628+
loglineMatch := logr.SubexpIndex("log")
629+
logKey = logmatch[loglineMatch]
630+
}
631+
632+
if val, ok := m[logKey]; ok {
619633
val++
620-
m[key] = val
634+
m[logKey] = val
621635
} else {
622-
m[key] = 1
636+
m[logKey] = 1
623637
}
624638
return m
625639
}

go.mod

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
11
module github.com/wymangr/blueiris_exporter
22

3-
go 1.18
3+
go 1.25
44

55
require (
6-
github.com/prometheus/client_golang v1.13.1
7-
golang.org/x/sys v0.1.0
6+
github.com/prometheus/client_golang v1.23.2
7+
golang.org/x/sys v0.40.0
88
gopkg.in/alecthomas/kingpin.v2 v2.2.6
99
)
1010

1111
require (
1212
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect
13-
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect
13+
github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b // indirect
1414
github.com/beorn7/perks v1.0.1 // indirect
15-
github.com/cespare/xxhash/v2 v2.1.2 // indirect
16-
github.com/golang/protobuf v1.5.2 // indirect
17-
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
18-
github.com/prometheus/client_model v0.2.0 // indirect
19-
github.com/prometheus/common v0.37.0 // indirect
20-
github.com/prometheus/procfs v0.8.0 // indirect
21-
github.com/stretchr/testify v1.8.0 // indirect
22-
google.golang.org/protobuf v1.28.1 // indirect
15+
github.com/cespare/xxhash/v2 v2.3.0 // indirect
16+
github.com/kr/text v0.2.0 // indirect
17+
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
18+
github.com/prometheus/client_model v0.6.2 // indirect
19+
github.com/prometheus/common v0.67.5 // indirect
20+
github.com/prometheus/procfs v0.19.2 // indirect
21+
go.yaml.in/yaml/v2 v2.4.3 // indirect
22+
google.golang.org/protobuf v1.36.11 // indirect
2323
)

0 commit comments

Comments
 (0)