From b6a86b54012b3966571621714910d206c26a303c Mon Sep 17 00:00:00 2001 From: Stefan Schwarz Date: Mon, 28 Dec 2020 01:26:55 +0100 Subject: [PATCH] init --- Dockerfile | 14 +++++++++++ go.mod | 3 +++ main.go | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 86 insertions(+) create mode 100644 Dockerfile create mode 100644 go.mod create mode 100644 main.go diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..052f0bd --- /dev/null +++ b/Dockerfile @@ -0,0 +1,14 @@ +FROM golang:1.15 as build + +ADD . /app +WORKDIR /app +ENV GOOS=linux +ENV CGO_ENABLED=0 +RUN go build . + +# --- + +FROM busybox + +COPY --from=build /app/viewercount /usr/local/bin/viewercount +CMD /usr/local/bin/viewercount diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..359e1af --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module foosinn/viewercount + +go 1.15 diff --git a/main.go b/main.go new file mode 100644 index 0000000..5c5631e --- /dev/null +++ b/main.go @@ -0,0 +1,69 @@ +package main + +import ( + "bufio" + "fmt" + "log" + "os" + "regexp" + "strconv" + + "net/http" +) + +const COUNTER_SLOTS = 20 + +func main() { + counter := NewCounter() + go counter.ScanStdin() + + http.HandleFunc("/", counter.MetricsHandler) + err := http.ListenAndServe(":8080", nil) + log.Fatalf("unable to listen: %s", err) +} + +type Counter struct { + counters []int + names []string + current int +} + +func NewCounter() *Counter { + return &Counter{ + counters: make([]int, COUNTER_SLOTS), + names: make([]string, COUNTER_SLOTS), + } +} + +func (c *Counter) MetricsHandler(w http.ResponseWriter, r *http.Request) { + w.Header().Add("Content-Type", "text/plain") + for i, name := range c.names { + fmt.Fprintf(w, "rc3_stream_count[name=%q] %d\n", name, c.counters[i]) + } + fmt.Fprintf(w, "rc3_stream_current[name=\"%d\"] 1\n", c.current) +} + +func (c *Counter) ScanStdin() { + matcher := regexp.MustCompile(`/hls/stream-([0-9]*).ts`) + scanner := bufio.NewScanner(os.Stdin) + scanner.Split(bufio.ScanLines) + + for scanner.Scan() { + matches := matcher.FindStringSubmatch(scanner.Text()) + if len(matches) == 2 { + c.countViewers(matches[1]) + } + } +} + +func (c *Counter) countViewers(part string) { + i, _ := strconv.Atoi(part) + mod := i % COUNTER_SLOTS + + if c.names[mod] != part { + c.counters[mod] = 0 + } + c.current = i + c.counters[mod] += 1 + c.names[mod] = part +}