Skip to content

Commit

Permalink
feat: first commit
Browse files Browse the repository at this point in the history
  • Loading branch information
just1not2 committed Feb 8, 2022
0 parents commit 9034925
Show file tree
Hide file tree
Showing 14 changed files with 1,586 additions and 0 deletions.
13 changes: 13 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Short description

_Please describe shortly this PR here (for a release, just write "Release x.y.z")_


# Changes

_Please list here the precise changes_


# Tests

_Please explain here how you tested your code_
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
bin/
config.json
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# CHANGELOG

## 1.0.0 (2022-02-08)

### Features
- Initial release
28 changes: 28 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Copyright: (c) 2022, Justin Béra (@just1not2) <[email protected]>
# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)

# Build
FROM golang:1.17 AS build

WORKDIR /app

COPY go.mod ./
COPY go.sum ./
RUN go mod download

COPY *.go ./
RUN CGO_ENABLED=0 go build -o /prometheus-exporter-yourls


# Install
FROM scratch
LABEL maintainer="[email protected]"

WORKDIR /

COPY --from=build /prometheus-exporter-yourls /prometheus-exporter-yourls
COPY config.json.template config.json

EXPOSE 9923

ENTRYPOINT ["/prometheus-exporter-yourls"]
24 changes: 24 additions & 0 deletions GNUmakefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Copyright: (c) 2022, Justin Béra (@just1not2) <[email protected]>
# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)

VERSION=1.0.0

default: install

build:
docker build -t just1not2/prometheus-exporter-yourls:test .

install: build
docker run -p 9923:9923 -v ${shell pwd}/config.json:/config.json just1not2/prometheus-exporter-yourls:test config.json

local:
go build -o ./bin/prometheus-exporter-yourls
./bin/prometheus-exporter-yourls config.json

release: build
docker tag just1not2/prometheus-exporter-yourls:test just1not2/prometheus-exporter-yourls:latest
docker tag just1not2/prometheus-exporter-yourls:test just1not2/prometheus-exporter-yourls:${shell echo ${VERSION} | cut -d '.' -f -2}
docker tag just1not2/prometheus-exporter-yourls:test just1not2/prometheus-exporter-yourls:${VERSION}
docker push just1not2/prometheus-exporter-yourls:latest
docker push just1not2/prometheus-exporter-yourls:${shell echo ${VERSION} | cut -d '.' -f -2}
docker push just1not2/prometheus-exporter-yourls:${VERSION}
674 changes: 674 additions & 0 deletions LICENSE

Large diffs are not rendered by default.

104 changes: 104 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# YOURLS Prometheus Exporter

A Prometheus exporter to fetch some metrics from a YOURLS URL-shortener instance.

## Exported metrics

### Gauges
Name | Description
--- | ---
`yourls_clicks_total`|Total number of clicks
`yourls_links_total`|Total number of deployed links

### Go metrics
A variety of Go metrics that describe the exporter are exported as well.


## Installation

### Docker image
The preferred way to use the exporter is by running the provided Docker image. It is currently available on Docker Hub:

```bash
docker pull just1not2/prometheus-exporter-yourls:latest
```

In addition to the `latest` tag which points to the version currently in the `main` branch, tagged versions are also available.

### From source
You can clone the repository and build the exporter locally.

```bash
git clone https://github.com/just1not2/prometheus-exporter-yourls.git
cd prometheus-exporter-yourls
make local
```


## Configuration

### Exporter configuration
There are two ways to configure the exporter:
* Environment variables
* Configuration file

The first method takes precedence over the second one.

To use the first one, you just have to declare environment variables while launching the Docker image, a Docker-compose or directly the executable:
```bash
docker run -p 9923:9923 \
-e YOURLS_URL=http://yourls.example.com \
-e YOURLS_SIGNATURE=SECRET_API_KEY \
just1not2/prometheus-exporter-yourls:latest
```

If you prefer to configure your provider with the second method, you should copy the [config.json.template](./config.json.template) file and replace the sample values. You can then launch the exporter:
```bash
docker run -p 9923:9923 -v $(pwd)/config.json:/config.json just1not2/prometheus-exporter-yourls:latest config.json
```

The configuration parameters that can be declared are summarized in this table:
Parameter | Environment variable | Description | Note
--- | --- | --- | ---
`url`|`YOURLS_URL`|URL of the YOURLS instance|_Required_
`signature`|`YOURLS_SIGNATURE`|Signature of the YOURLS instance|_Required_
`exporter_port`|`YOURLS_EXPORTER_PORT`|Port on which the exporter listens|_Default is 9923_
`exporter_timeout`|`YOURLS_EXPORTER_TIMEOUT`|Timeout of requests to the YOURLS instance|_Default is 10_

The `signature` parameter can be found at `http://<your YOURLS instance URL>/admin/tools.php`.

### Scrape configuration
The exporter will query the YOURLS server every time it is scraped by Prometheus. You can modify the configuration of the exporter to change the scrape interval:

```yml
scrape_configs:
- job_name: yourls
scrape_interval: 1m
static_configs:
- targets: ['<your YOURLS exporter host>:9923']
```
## See also
* [YOURLS official documentation](https://yourls.org)
* [Use Prometheus exporter](https://prometheus.io/docs/instrumenting/exporters/)
## Contributing to this exporter
This exporter started as personal project, but I welcome community contributions to this exporter. If you find problems, please open an issue or create a PR against the [YOURLS exporter repository](https://github.com/just1not2/prometheus-exporter-yourls).
You can also reach me by email at `[email protected]`.


## Licensing

GNU General Public License v3.0 or later.

See [LICENSE](./LICENSE) to see the full text.


## Author information

This exporter was created in 2022 by Justin Béra.
60 changes: 60 additions & 0 deletions collector.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Copyright: (c) 2022, Justin Béra (@just1not2) <[email protected]>
// GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)

package main

import (
"strconv"
"time"

"github.com/prometheus/client_golang/prometheus"
)

type YourlsCollector struct {
client *YourlsClient
YourlsClicksTotalDescription *prometheus.Desc
YourlsLinksTotalDescription *prometheus.Desc
}

func NewYourlsCollector(configuration *YourlsConfiguration) *YourlsCollector {
return &YourlsCollector{
client: NewYourlsClient(configuration.YourlsURL, configuration.Signature, configuration.HTTPTimeout),
YourlsClicksTotalDescription: prometheus.NewDesc("yourls_clicks_total",
"Total number of clicks.",
nil, nil,
),
YourlsLinksTotalDescription: prometheus.NewDesc("yourls_links_total",
"Total number of deployed links.",
nil, nil,
),
}
}

func (collector *YourlsCollector) Describe(ch chan<- *prometheus.Desc) {
ch <- collector.YourlsClicksTotalDescription
ch <- collector.YourlsLinksTotalDescription
}

func (collector *YourlsCollector) Collect(ch chan<- prometheus.Metric) {
// Gathers the specific query parameters
parameters := map[string]string{
"action": "stats",
}

body, err := collector.client.Request(parameters)
if err != nil {
panic(err)
}

// Converts returned statistics into floats
linksTotalValue, _ := strconv.ParseFloat(body.Stats["total_links"], 64)
clicksTotalValue, _ := strconv.ParseFloat(body.Stats["total_clicks"], 64)

// Sends metrics to the channel
m1 := prometheus.MustNewConstMetric(collector.YourlsClicksTotalDescription, prometheus.GaugeValue, clicksTotalValue)
m2 := prometheus.MustNewConstMetric(collector.YourlsLinksTotalDescription, prometheus.GaugeValue, linksTotalValue)
m1 = prometheus.NewMetricWithTimestamp(time.Now(), m1)
m2 = prometheus.NewMetricWithTimestamp(time.Now(), m2)
ch <- m1
ch <- m2
}
6 changes: 6 additions & 0 deletions config.json.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"url": "http://yourls.example.com",
"signature": "SECRET_API_KEY",
"exporter_port": 9923,
"exporter_timeout": 10
}
89 changes: 89 additions & 0 deletions configuration.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// Copyright: (c) 2022, Justin Béra (@just1not2) <[email protected]>
// GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)

package main

import (
"encoding/json"
"fmt"
"log"
"os"
"strconv"
"time"
)

const (
DEFAULT_HTTP_TIMEOUT = 10
DEFAULT_PORT = 9923
)

type ExporterConfiguration struct {
HTTPTimeout float64 `json:"exporter_timeout"`
Port float64 `json:"exporter_port"`
Signature string `json:"signature"`
YourlsURL string `json:"url"`
}

type YourlsConfiguration struct {
HTTPTimeout time.Duration
Port float64
Signature string
YourlsURL string
}

func NewConfiguration() *YourlsConfiguration {
// Initializes exporter configuration
configuration := &ExporterConfiguration{
HTTPTimeout: DEFAULT_HTTP_TIMEOUT,
Port: DEFAULT_PORT,
}

// Uses configuration file if it is passed as a parameter
if len(os.Args) > 1 {
file, err := os.Open(os.Args[1])
if err != nil {
fmt.Printf("warn: %s, cannot read %s (skipping)\n", err, os.Args[1])
} else {
if err := json.NewDecoder(file).Decode(configuration); err != nil {
fmt.Printf("warn: %s, cannot read %s (skipping)\n", err, os.Args[1])
}
}
file.Close()
}

// Overwrites configuration with existing environment variables
if keyString, defined := os.LookupEnv("YOURLS_URL"); defined {
configuration.YourlsURL = keyString
}
if keyString, defined := os.LookupEnv("YOURLS_SIGNATURE"); defined {
configuration.Signature = keyString
}
if keyString, defined := os.LookupEnv("YOURLS_EXPORTER_PORT"); defined {
if keyFloat, err := strconv.ParseFloat(keyString, 64); err != nil {
fmt.Printf("warn: %s, cannot use YOURLS_EXPORTER_PORT environment variable (skipping)\n", err)
} else {
configuration.Port = keyFloat
}
}
if keyString, defined := os.LookupEnv("YOURLS_EXPORTER_TIMEOUT"); defined {
if keyFloat, err := strconv.ParseFloat(keyString, 64); err != nil {
fmt.Printf("warn: %s, cannot use YOURLS_EXPORTER_TIMEOUT environment variable (skipping)\n", err)
} else {
configuration.HTTPTimeout = keyFloat
}
}

// Throws error for missing fields
if configuration.YourlsURL == "" {
log.Fatal("error: YOURLS_URL environment variable and 'url' configuration parameter were both undefined")
} else if configuration.Signature == "" {
log.Fatal("error: YOURLS_SIGNATURE environment variable and 'signature' configuration parameter were both undefined")
}

return &YourlsConfiguration{
HTTPTimeout: time.Duration(configuration.HTTPTimeout * float64(time.Second)),
YourlsURL: fmt.Sprintf("%s/yourls-api.php?format=json&signature=%s", configuration.YourlsURL, configuration.Signature),
Port: configuration.Port,
Signature: configuration.Signature,
}
}
17 changes: 17 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
module github.com/just1not2/prometheus-exporter-yourls

go 1.17

require github.com/prometheus/client_golang v1.12.1

require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/common v0.32.1 // indirect
github.com/prometheus/procfs v0.7.3 // indirect
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 // indirect
google.golang.org/protobuf v1.26.0 // indirect
)
Loading

0 comments on commit 9034925

Please sign in to comment.