├── requirements.txt ├── Dockerfile ├── .travis.yml ├── README.md ├── LICENSE ├── .github └── workflows │ └── docker-publish.yml └── tinyproxy_exporter /requirements.txt: -------------------------------------------------------------------------------- 1 | prometheus-client>=0.0.18 2 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM library/python:3.8 2 | 3 | COPY requirements.txt requirements.txt 4 | 5 | RUN pip install -r requirements.txt 6 | 7 | COPY tinyproxy_exporter tinyproxy_exporter 8 | 9 | ENTRYPOINT ["python", "tinyproxy_exporter"] 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | os: linux 2 | dist: trusty 3 | group: stable 4 | language: python 5 | python: 3.6 6 | before_install: 7 | - sudo apt-get install tinyproxy 8 | script: 9 | - | 10 | ./tinyproxy_exporter & 11 | sleep 1 12 | if [ "$(pidof python3)" ] 13 | then 14 | kill "$(pidof python3)" 15 | sudo rm /usr/share/tinyproxy/stats.html 16 | sudo service tinyproxy restart 17 | ./tinyproxy_exporter & 18 | sleep 1 19 | if [ "$(pidof python3)" ] 20 | then 21 | kill "$(pidof python3)" 22 | else 23 | exit 1 24 | fi 25 | else 26 | exit 1 27 | fi 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tinyproxy_exporter 2 | [![Build Status](https://travis-ci.com/igzivkov/tinyproxy_exporter.svg?branch=master)](https://travis-ci.com/igzivkov/tinyproxy_exporter) 3 | 4 | A simple Prometheus exporter for Tinyproxy. 5 | 6 | ## Usage 7 | 8 | ``` 9 | $ pip install -r requirements.txt 10 | $ python3 tinyproxy_exporter --help 11 | usage: tinyproxy_exporter [-h] [-l LISTEN] [-s STATHOST] [-t TINYPROXY] 12 | 13 | Prometheus exporter for Tinyproxy. 14 | 15 | optional arguments: 16 | -h, --help show this help message and exit 17 | -l LISTEN address on which to expose metrics (default ":9240") 18 | -s STATHOST internal statistics page address (default "tinyproxy.stats") 19 | -t TINYPROXY tinyproxy address (default "127.0.0.1:8888") 20 | ``` 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Igor Živković 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.github/workflows/docker-publish.yml: -------------------------------------------------------------------------------- 1 | name: Docker 2 | 3 | # This workflow uses actions that are not certified by GitHub. 4 | # They are provided by a third-party and are governed by 5 | # separate terms of service, privacy policy, and support 6 | # documentation. 7 | 8 | on: 9 | push: 10 | branches: [ master ] 11 | # Publish semver tags as releases. 12 | tags: [ 'v*.*.*' ] 13 | pull_request: 14 | branches: [ master ] 15 | 16 | env: 17 | # Use docker.io for Docker Hub if empty 18 | REGISTRY: ghcr.io 19 | # github.repository as / 20 | IMAGE_NAME: ${{ github.repository }} 21 | 22 | 23 | jobs: 24 | build: 25 | 26 | runs-on: ubuntu-latest 27 | permissions: 28 | contents: read 29 | packages: write 30 | 31 | steps: 32 | - name: Checkout repository 33 | uses: actions/checkout@v2 34 | 35 | # Login against a Docker registry except on PR 36 | # https://github.com/docker/login-action 37 | - name: Log into registry ${{ env.REGISTRY }} 38 | if: github.event_name != 'pull_request' 39 | uses: docker/login-action@28218f9b04b4f3f62068d7b6ce6ca5b26e35336c 40 | with: 41 | registry: ${{ env.REGISTRY }} 42 | username: ${{ github.actor }} 43 | password: ${{ secrets.GITHUB_TOKEN }} 44 | 45 | # Extract metadata (tags, labels) for Docker 46 | # https://github.com/docker/metadata-action 47 | - name: Extract Docker metadata 48 | id: meta 49 | uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38 50 | with: 51 | images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} 52 | 53 | # Build and push Docker image with Buildx (don't push on PR) 54 | # https://github.com/docker/build-push-action 55 | - name: Build and push Docker image 56 | uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc 57 | with: 58 | context: . 59 | push: ${{ github.event_name != 'pull_request' }} 60 | tags: ${{ steps.meta.outputs.tags }} 61 | labels: ${{ steps.meta.outputs.labels }} 62 | -------------------------------------------------------------------------------- /tinyproxy_exporter: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import argparse 4 | import re 5 | import sys 6 | import time 7 | import urllib 8 | 9 | from prometheus_client import start_http_server 10 | from prometheus_client.core import CounterMetricFamily, GaugeMetricFamily, REGISTRY 11 | 12 | 13 | class TinyproxyCollector(object): 14 | def __init__(self, stathost, tinyproxy): 15 | self.stathost = stathost 16 | self.tinyproxy = tinyproxy 17 | 18 | def collect(self): 19 | handler = urllib.request.ProxyHandler({'http': self.tinyproxy}) 20 | opener = urllib.request.build_opener(handler) 21 | urllib.request.install_opener(opener) 22 | response = urllib.request.urlopen('http://{0}'.format(self.stathost)) 23 | values = [ 24 | float(x) for x in re.findall( 25 | b'(?:|: )(\d+)(?:|
|\n

)', response.read()) 26 | ] 27 | yield GaugeMetricFamily('tinyproxy_connections_open', 28 | 'Number of open connections', values[0]) 29 | yield CounterMetricFamily('tinyproxy_requests_total', 30 | 'Number of requests', values[1]) 31 | yield CounterMetricFamily('tinyproxy_connections_bad_total', 32 | 'Number of bad connections', values[2]) 33 | yield CounterMetricFamily('tinyproxy_connections_denied_total', 34 | 'Number of denied connections', values[3]) 35 | yield CounterMetricFamily( 36 | 'tinyproxy_connections_refused_total', 37 | 'Number of refused connections due to high load', values[4]) 38 | 39 | 40 | def parse_args(): 41 | parser = argparse.ArgumentParser( 42 | description='Prometheus exporter for Tinyproxy.') 43 | parser.add_argument( 44 | '-l', 45 | metavar='LISTEN', 46 | default=':9240', 47 | help='address on which to expose metrics (default ":9240")') 48 | parser.add_argument( 49 | '-s', 50 | metavar='STATHOST', 51 | default='tinyproxy.stats', 52 | help='internal statistics page address (default "tinyproxy.stats")') 53 | parser.add_argument('-t', 54 | metavar='TINYPROXY', 55 | default='127.0.0.1:8888', 56 | help='tinyproxy address (default "127.0.0.1:8888")') 57 | return parser.parse_args() 58 | 59 | 60 | def main(): 61 | try: 62 | args = parse_args() 63 | addr, port = args.l.split(':') 64 | REGISTRY.register(TinyproxyCollector(args.s, args.t)) 65 | start_http_server(int(port), addr) 66 | while True: 67 | time.sleep(1) 68 | except KeyboardInterrupt: 69 | print(' Interrupted') 70 | sys.exit(0) 71 | 72 | 73 | if __name__ == '__main__': 74 | main() 75 | --------------------------------------------------------------------------------